[
  {
    "path": ".clippy.toml",
    "content": "disallowed-names = [\n  \"..\",\n  \"e\", # no single letter error bindings\n]\ndisallowed-methods = [\n  { path = \"std::cell::RefCell::default()\", reason = \"prefer explicit inner type default (remove allow-invalid when rust-lang/rust-clippy/#8581 is fixed)\", allow-invalid = true },\n  { path = \"std::rc::Rc::default()\", reason = \"prefer explicit inner type default (remove allow-invalid when rust-lang/rust-clippy/#8581 is fixed)\", allow-invalid = true },\n]\n"
  },
  {
    "path": ".codecov.yml",
    "content": "comment: false\n\ncoverage:\n  status:\n    project:\n      default:\n        threshold: 100% # make CI green\n    patch:\n      default:\n        threshold: 100% # make CI green\n\nignore: # ignore code coverage on following paths\n  - \"**/tests\"\n  - \"**/benches\"\n  - \"**/examples\"\n"
  },
  {
    "path": ".cspell.yml",
    "content": "version: \"0.2\"\nwords:\n  - actix\n  - addrs\n  - ALPN\n  - bytestring\n  - httparse\n  - MSRV\n  - realip\n  - rustls\n  - rustup\n  - serde\n  - uring\n  - webpki\n  - zstd\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [robjtede, JohnTitor]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a bug report.\n---\n\nYour issue may already be reported! Please search on the [Actix Web issue tracker](https://github.com/actix/actix-web/issues) before creating one.\n\n## Expected Behavior\n\n<!--- If you're describing a bug, tell us what should happen -->\n<!--- If you're suggesting a change/improvement, tell us how it should work -->\n\n## Current Behavior\n\n<!--- If describing a bug, tell us what happens instead of the expected behavior -->\n<!--- If suggesting a change/improvement, explain the difference from current behavior -->\n\n## Possible Solution\n\n<!--- Not obligatory, but suggest a fix/reason for the bug, -->\n<!--- or ideas how to implement the addition or change -->\n\n## Steps to Reproduce (for bugs)\n\n<!--- Provide a link to a live example, or an unambiguous set of steps to -->\n<!--- reproduce this bug. Include code to reproduce, if relevant -->\n\n1.\n2.\n3.\n4.\n\n## Context\n\n<!--- How has this issue affected you? What are you trying to accomplish? -->\n<!--- Providing context helps us come up with a solution that is most useful in the real world -->\n\n## Your Environment\n\n<!--- Include as many relevant details about the environment you experienced the bug in -->\n\n- Rust Version (I.e, output of `rustc -V`):\n- Actix Web Version:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Actix Discord\n    url: https://discord.gg/NWpN5mmg3x\n    about: Actix developer discussion and community chat\n  - name: GitHub Discussions\n    url: https://github.com/actix/actix-web/discussions\n    about: Actix Web Q&A\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Thanks for considering contributing actix! -->\n<!-- Please fill out the following to get your PR reviewed quicker. -->\n\n## PR Type\n\n<!-- What kind of change does this PR make? -->\n<!-- Bug Fix / Feature / Refactor / Code Style / Other -->\n\nPR_TYPE\n\n## PR Checklist\n\n<!-- Check your PR fulfills the following items. -->\n<!-- For draft PRs check the boxes as you complete them. -->\n\n- [ ] Tests for the changes have been added / updated.\n- [ ] Documentation comments have been added / updated.\n- [ ] A changelog entry has been made for the appropriate packages.\n- [ ] Format code with the latest stable rustfmt.\n- [ ] (Team) Label with affected crates and semver status.\n\n## Overview\n\n<!-- Describe the current and new behavior. -->\n<!-- Emphasize any breaking changes. -->\n\n<!-- If this PR fixes or closes an issue, reference it here. -->\n<!-- Closes #000 -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: cargo\n    directory: /\n    schedule:\n      interval: weekly\n    versioning-strategy: lockfile-only\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "A-files:\n  - changed-files:\n      - any-glob-to-any-file: actix-files/**\n\nA-http:\n  - changed-files:\n      - any-glob-to-any-file: actix-http/**\n\nA-http-test:\n  - changed-files:\n      - any-glob-to-any-file: actix-http-test/**\n\nA-multipart:\n  - changed-files:\n      - any-glob-to-any-file: actix-multipart/**\n\nA-multipart-derive:\n  - changed-files:\n      - any-glob-to-any-file: actix-multipart-derive/**\n\nA-router:\n  - changed-files:\n      - any-glob-to-any-file: actix-router/**\n\nA-test:\n  - changed-files:\n      - any-glob-to-any-file: actix-test/**\n\nA-web:\n  - changed-files:\n      - any-glob-to-any-file: actix-web/**\n\nA-web-actors:\n  - changed-files:\n      - any-glob-to-any-file: actix-web-actors/**\n\nA-web-codegen:\n  - changed-files:\n      - any-glob-to-any-file: actix-web-codegen/**\n\nA-awc:\n  - changed-files:\n      - any-glob-to-any-file: awc/**\n"
  },
  {
    "path": ".github/workflows/bench.yml",
    "content": "name: Benchmark\n\non:\n  push:\n    branches: [main]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  check_benchmark:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust\n        run: |\n          rustup set profile minimal\n          rustup install nightly\n          rustup override set nightly\n\n      - name: Check benchmark\n        run: cargo bench --bench=server -- --sample-size=15\n"
  },
  {
    "path": ".github/workflows/ci-post-merge.yml",
    "content": "name: CI (post-merge)\n\non:\n  push:\n    branches: [main]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build_and_test_nightly:\n    strategy:\n      fail-fast: false\n      matrix:\n        # prettier-ignore\n        target:\n          - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }\n          - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }\n          - { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }\n        version:\n          - { name: nightly, version: nightly }\n\n    name: ${{ matrix.target.name }} / ${{ matrix.version.name }}\n    runs-on: ${{ matrix.target.os }}\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install nasm\n        if: matrix.target.os == 'windows-latest'\n        uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2\n\n      - name: Install OpenSSL\n        if: matrix.target.os == 'windows-latest'\n        shell: bash\n        run: |\n          set -e\n          choco install openssl --version=1.1.1.2100 -y --no-progress\n          echo 'OPENSSL_DIR=C:\\Program Files\\OpenSSL' >> $GITHUB_ENV\n          echo \"RUSTFLAGS=-C target-feature=+crt-static\" >> $GITHUB_ENV\n\n      - name: Install Rust (${{ matrix.version.name }})\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: ${{ matrix.version.version }}\n\n      - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean\n\n      - name: check minimal\n        run: just check-min\n\n      - name: check default\n        run: just check-default\n\n      - name: tests\n        timeout-minutes: 60\n        run: just test\n\n      - name: CI cache clean\n        run: cargo-ci-cache-clean\n\n  ci_feature_powerset_check:\n    name: Verify Feature Combinations\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Free Disk Space\n        run: ./scripts/free-disk-space.sh\n\n      - name: Setup mold linker\n        uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1\n\n      - name: Install Rust\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n\n      - name: Install just, cargo-hack\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just,cargo-hack\n\n      - name: Check feature combinations\n        run: just check-feature-combinations\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n  merge_group:\n    types: [checks_requested]\n  push:\n    branches: [main]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  read_msrv:\n    name: Read MSRV\n    uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@8b553824444060021f2843d7b4d803f3624d15e5 # v0.1.0\n\n  build_and_test:\n    needs: read_msrv\n\n    strategy:\n      fail-fast: false\n      matrix:\n        # prettier-ignore\n        target:\n          - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }\n          - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }\n          - { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }\n        version:\n          - { name: msrv, version: \"${{ needs.read_msrv.outputs.msrv }}\" }\n          - { name: stable, version: stable }\n\n    name: ${{ matrix.target.name }} / ${{ matrix.version.name }}\n    runs-on: ${{ matrix.target.os }}\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install nasm\n        if: matrix.target.os == 'windows-latest'\n        uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2\n\n      - name: Install OpenSSL\n        if: matrix.target.os == 'windows-latest'\n        shell: bash\n        run: |\n          set -e\n          choco install openssl --version=1.1.1.2100 -y --no-progress\n          echo 'OPENSSL_DIR=C:\\Program Files\\OpenSSL' >> $GITHUB_ENV\n          echo \"RUSTFLAGS=-C target-feature=+crt-static\" >> $GITHUB_ENV\n\n      - name: Setup mold linker\n        if: matrix.target.os == 'ubuntu-latest'\n        uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1\n\n      - name: Install Rust (${{ matrix.version.name }})\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: ${{ matrix.version.version }}\n\n      - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean\n\n      - name: workaround MSRV issues\n        if: matrix.version.name == 'msrv'\n        run: just downgrade-for-msrv\n\n      - name: check minimal\n        run: just check-min\n\n      - name: check default\n        run: just check-default\n\n      - name: tests\n        timeout-minutes: 30\n        run: just test\n\n      - name: CI cache clean\n        run: cargo-ci-cache-clean\n\n      - name: deny check\n        if: matrix.version.name == 'stable' && matrix.target.os == 'ubuntu-latest'\n        uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2.0.15\n\n  io-uring:\n    name: io-uring tests\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: nightly\n\n      - name: tests (io-uring)\n        timeout-minutes: 30\n        run: >\n          sudo bash -c \"ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=stable cargo test --lib --tests -p=actix-files --all-features\"\n\n  rustdoc:\n    name: doc tests\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust (nightly)\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: nightly\n\n      - name: Install just\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just\n\n      - name: doc tests\n        run: just test-docs\n"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "content": "name: Coverage\n\non:\n  push:\n    branches: [main]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  coverage:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust (nightly)\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: nightly\n          components: llvm-tools\n\n      - name: Install just, cargo-llvm-cov, cargo-nextest\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just,cargo-llvm-cov,cargo-nextest\n\n      - name: Generate code coverage\n        run: just test-coverage-codecov\n\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2\n        with:\n          files: codecov.json\n          fail_ci_if_error: true\n        env:\n          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: Labeler\n\non:\n  pull_request_target:\n    types: [opened, synchronize, reopened]\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  labeler:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  fmt:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust (nightly)\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: nightly\n          components: rustfmt\n\n      - name: Check with Rustfmt\n        run: cargo fmt --all -- --check\n\n  clippy:\n    permissions:\n      contents: read\n      checks: write # to add clippy checks to PR diffs\n\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          components: clippy\n\n      - name: Check with Clippy\n        uses: giraffate/clippy-action@13b9d32482f25d29ead141b79e7e04e7900281e0 # v1.0.1\n        with:\n          reporter: github-pr-check\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          clippy_flags: >-\n            --workspace --all-features --tests --examples --bins --\n            -A unknown_lints -D clippy::todo -D clippy::dbg_macro\n\n  lint-docs:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust (nightly)\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: nightly\n          components: rust-docs\n\n      - name: Check for broken intra-doc links\n        env:\n          RUSTDOCFLAGS: -D warnings\n        run: cargo +nightly doc --no-deps --workspace --all-features\n\n  check-external-types:\n    if: false # rustdoc mismatch currently\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}\n\n      - name: Install just\n        uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32\n        with:\n          tool: just\n\n      - name: Install cargo-check-external-types\n        uses: taiki-e/cache-cargo-install-action@59027ebf20a9617c4e819eb53ccd2673cb162b89 # v3.0.3\n        with:\n          tool: cargo-check-external-types\n\n      - name: check external types\n        run: just check-external-types-all +${{ vars.RUST_VERSION_EXTERNAL_TYPES }}\n"
  },
  {
    "path": ".github/workflows/semver-labeler.yml",
    "content": "name: Semver Labeler\n\non:\n  workflow_run:\n    workflows: [CI]\n    types: [completed]\n\njobs:\n  semver-label:\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n      contents: read\n    env:\n      ACTIONS_STEP_DEBUG: true\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          ref: ${{ github.event.workflow_run.head_sha }}\n\n      - name: Install Rust\n        uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4\n        with:\n          toolchain: stable\n\n      - uses: JohnTitor/cargo-semver-checks@3b76737b550e48ad0bd5912e2757e80eee6294b0 # v0.2.1\n        with:\n          label-prefix: B-semver-\n          label-strategy: skip-if-human\n"
  },
  {
    "path": ".gitignore",
    "content": "target/\nguide/build/\n/gh-pages\n\n*.so\n*.out\n*.pyc\n*.pid\n*.sock\n*~\n.DS_Store\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Configuration directory generated by CLion\n.idea\n\n# Configuration directory generated by VSCode\n.vscode\n\n# code coverage\n/lcov.info\n/codecov.json\n"
  },
  {
    "path": ".prettierrc.yml",
    "content": "overrides:\n  - files: \"*.md\"\n    options:\n      printWidth: 9999\n      proseWrap: never\n"
  },
  {
    "path": ".rustfmt.toml",
    "content": "group_imports = \"StdExternalCrate\"\nimports_granularity = \"Crate\"\nuse_field_init_shorthand = true\n"
  },
  {
    "path": ".taplo.toml",
    "content": "exclude = [\"target/*\"]\ninclude = [\"**/*.toml\"]\n\n[formatting]\ncolumn_width = 100\nalign_comments = false\n\n[[rule]]\ninclude = [\"**/Cargo.toml\"]\nkeys = [\"features\"]\nformatting.column_width = 105\nformatting.reorder_keys = false\n\n[[rule]]\ninclude = [\"**/Cargo.toml\"]\nkeys = [\n  \"dependencies\",\n  \"*-dependencies\",\n  \"workspace.dependencies\",\n  \"workspace.*-dependencies\",\n  \"target.*.dependencies\",\n  \"target.*.*-dependencies\",\n]\nformatting.column_width = 120\nformatting.reorder_keys = true\n\n[[rule]]\ninclude = [\"**/Cargo.toml\"]\nkeys = [\n  \"dependencies.*\",\n  \"*-dependencies.*\",\n  \"workspace.dependencies.*\",\n  \"workspace.*-dependencies.*\",\n  \"target.*.dependencies\",\n  \"target.*.*-dependencies\",\n]\nformatting.column_width = 120\nformatting.reorder_keys = false\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# Changelog\n\nChangelogs are kept separately for each crate in this repo.\n\nActix Web changelog [is now here &rarr;](./actix-web/CHANGES.md).\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at robjtede@icloud.com ([@robjtede]) or huyuumi@neet.club ([@JohnTitor]). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n[@robjtede]: https://github.com/robjtede\n[@JohnTitor]: https://github.com/JohnTitor\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n  \"actix-files\",\n  \"actix-http-test\",\n  \"actix-http\",\n  \"actix-multipart\",\n  \"actix-multipart-derive\",\n  \"actix-router\",\n  \"actix-test\",\n  \"actix-web-codegen\",\n  \"actix-web\",\n  \"awc\",\n]\n\n[workspace.package]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.88\"\n\n[profile.dev]\n# Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.\ndebug = 0\n\n[profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\n\n[patch.crates-io]\nactix-files = { path = \"actix-files\" }\nactix-http = { path = \"actix-http\" }\nactix-http-test = { path = \"actix-http-test\" }\nactix-multipart = { path = \"actix-multipart\" }\nactix-multipart-derive = { path = \"actix-multipart-derive\" }\nactix-router = { path = \"actix-router\" }\nactix-test = { path = \"actix-test\" }\nactix-web = { path = \"actix-web\" }\nactix-web-codegen = { path = \"actix-web-codegen\" }\nawc = { path = \"awc\" }\n\n# uncomment for quick testing against local actix-net repo\n# actix-service = { path = \"../actix-net/actix-service\" }\n# actix-macros = { path = \"../actix-net/actix-macros\" }\n# actix-rt = { path = \"../actix-net/actix-rt\" }\n# actix-codec = { path = \"../actix-net/actix-codec\" }\n# actix-utils = { path = \"../actix-net/actix-utils\" }\n# actix-tls = { path = \"../actix-net/actix-tls\" }\n# actix-server = { path = \"../actix-net/actix-server\" }\n\n[workspace.lints.rust]\nrust_2018_idioms = { level = \"deny\" }\nfuture_incompatible = { level = \"deny\" }\nnonstandard_style = { level = \"deny\" }\n\n[workspace.lints.clippy]\n# clone_on_ref_ptr = { level = \"deny\" }\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017-NOW Actix Team\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2017-NOW Actix Team\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "actix-files/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Add `Files::try_compressed()` to support serving pre-compressed static files [#2615]\n- Fix handling of `bytes=0-`\n- Fix `NamedFile` panic when serving files with pre-UNIX epoch modification times. [#2748]\n- Fix invalid `Content-Encoding: identity` header in `NamedFile` range responses. [#3191]\n\n[#2615]: https://github.com/actix/actix-web/pull/2615\n[#2748]: https://github.com/actix/actix-web/issues/2748\n[#3191]: https://github.com/actix/actix-web/issues/3191\n\n## 0.6.10\n\n### Security Notice\n\nWe addressed 2 vulnerabilities in this release:\n\n- Do not panic with empty Range header.\n- Avoid serving CWD on invalid `Files::new` inputs.\n\nWe encourage updating your `actix-files` version as soon as possible.\n\n### Other changes\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n- `PathBufWrap` & `UriSegmentError` made public. [#3694]\n\n[#3694]: https://github.com/actix/actix-web/pull/3694\n\n## 0.6.9\n\n- Correct `derive_more` dependency feature requirements.\n\n## 0.6.8\n\n- Add `Files::with_permanent_redirect()` method.\n- Change default redirect status code to 307 Temporary Redirect.\n\n## 0.6.7\n\n- Add `{Files, NamedFile}::read_mode_threshold()` methods to allow faster synchronous reads of small files.\n- Minimum supported Rust version (MSRV) is now 1.75.\n\n## 0.6.6\n\n- Update `tokio-uring` dependency to `0.4`.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 0.6.5\n\n- Fix handling of special characters in filenames.\n\n## 0.6.4\n\n- Fix handling of newlines in filenames.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 0.6.3\n\n- XHTML files now use `Content-Disposition: inline` instead of `attachment`. [#2903]\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n- Update `tokio-uring` dependency to `0.4`.\n\n[#2903]: https://github.com/actix/actix-web/pull/2903\n\n## 0.6.2\n\n- Allow partial range responses for video content to start streaming sooner. [#2817]\n- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.\n\n[#2817]: https://github.com/actix/actix-web/pull/2817\n\n## 0.6.1\n\n- Add `NamedFile::{modified, metadata, content_type, content_disposition, encoding}()` getters. [#2021]\n- Update `tokio-uring` dependency to `0.3`.\n- Audio files now use `Content-Disposition: inline` instead of `attachment`. [#2645]\n- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.\n\n[#2021]: https://github.com/actix/actix-web/pull/2021\n[#2645]: https://github.com/actix/actix-web/pull/2645\n\n## 0.6.0\n\n- No significant changes since `0.6.0-beta.16`.\n\n## 0.6.0-beta.16\n\n- No significant changes since `0.6.0-beta.15`.\n\n## 0.6.0-beta.15\n\n- No significant changes since `0.6.0-beta.14`.\n\n## 0.6.0-beta.14\n\n- The `prefer_utf8` option introduced in `0.4.0` is now true by default. [#2583]\n\n[#2583]: https://github.com/actix/actix-web/pull/2583\n\n## 0.6.0-beta.13\n\n- The `Files` service now rejects requests with URL paths that include `%2F` (decoded: `/`). [#2398]\n- The `Files` service now correctly decodes `%25` in the URL path to `%` for the file path. [#2398]\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n[#2398]: https://github.com/actix/actix-web/pull/2398\n\n## 0.6.0-beta.12\n\n- No significant changes since `0.6.0-beta.11`.\n\n## 0.6.0-beta.11\n\n- No significant changes since `0.6.0-beta.10`.\n\n## 0.6.0-beta.10\n\n- No significant changes since `0.6.0-beta.9`.\n\n## 0.6.0-beta.9\n\n- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408]\n- Add `NamedFile::open_async`. [#2408]\n- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453]\n- The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408]\n- The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408]\n- Add `impl Clone` for `FilesService`. [#2408]\n\n[#2408]: https://github.com/actix/actix-web/pull/2408\n[#2453]: https://github.com/actix/actix-web/pull/2453\n\n## 0.6.0-beta.8\n\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n## 0.6.0-beta.7\n\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n## 0.6.0-beta.6\n\n- Added `Files::path_filter()`. [#2274]\n- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228]\n\n[#2274]: https://github.com/actix/actix-web/pull/2274\n[#2228]: https://github.com/actix/actix-web/pull/2228\n\n## 0.6.0-beta.5\n\n- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]\n- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]\n- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]\n- `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257]\n\n[#2135]: https://github.com/actix/actix-web/pull/2135\n[#2156]: https://github.com/actix/actix-web/pull/2156\n[#2225]: https://github.com/actix/actix-web/pull/2225\n[#2257]: https://github.com/actix/actix-web/pull/2257\n\n## 0.6.0-beta.4\n\n- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]\n\n[#2046]: https://github.com/actix/actix-web/pull/2046\n\n## 0.6.0-beta.3\n\n- No notable changes.\n\n## 0.6.0-beta.2\n\n- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]\n- Replace `v_htmlescape` with `askama_escape`. [#1953]\n\n[#1887]: https://github.com/actix/actix-web/pull/1887\n[#1953]: https://github.com/actix/actix-web/pull/1953\n\n## 0.6.0-beta.1\n\n- `HttpRange::parse` now has its own error type.\n- Update `bytes` to `1.0`. [#1813]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n\n## 0.5.0\n\n- Optionally support hidden files/directories. [#1811]\n\n[#1811]: https://github.com/actix/actix-web/pull/1811\n\n## 0.4.1\n\n- Clarify order of parameters in `Files::new` and improve docs.\n\n## 0.4.0\n\n- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714]\n\n[#1714]: https://github.com/actix/actix-web/pull/1714\n\n## 0.3.0\n\n- No significant changes from 0.3.0-beta.1.\n\n## 0.3.0-beta.1\n\n- Update `v_htmlescape` to 0.10\n- Update `actix-web` and `actix-http` dependencies to beta.1\n\n## 0.3.0-alpha.1\n\n- Update `actix-web` and `actix-http` dependencies to alpha\n- Fix some typos in the docs\n- Bump minimum supported Rust version to 1.40\n- Support sending Content-Length when Content-Range is specified [#1384]\n\n[#1384]: https://github.com/actix/actix-web/pull/1384\n\n## 0.2.1\n\n- Use the same format for file URLs regardless of platforms\n\n## 0.2.0\n\n- Fix BodyEncoding trait import #1220\n\n## 0.2.0-alpha.1\n\n- Migrate to `std::future`\n\n## 0.1.7\n\n- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)\n\n## 0.1.6\n\n- Add option to redirect to a slash-ended path `Files` #1132\n\n## 0.1.5\n\n- Bump up `mime_guess` crate version to 2.0.1\n- Bump up `percent-encoding` crate version to 2.1\n- Allow user defined request guards for `Files` #1113\n\n## 0.1.4\n\n- Allow to disable `Content-Disposition` header #686\n\n## 0.1.3\n\n- Do not set `Content-Length` header, let actix-http set it #930\n\n## 0.1.2\n\n- Content-Length is 0 for NamedFile HEAD request #914\n- Fix ring dependency from actix-web default features for #741\n\n## 0.1.1\n\n- Static files are incorrectly served as both chunked and with length #812\n\n## 0.1.0\n\n- NamedFile last-modified check always fails due to nano-seconds in file modified date #820\n\n## 0.1.0-beta.4\n\n- Update actix-web to beta.4\n\n## 0.1.0-beta.1\n\n- Update actix-web to beta.1\n\n## 0.1.0-alpha.6\n\n- Update actix-web to alpha6\n\n## 0.1.0-alpha.4\n\n- Update actix-web to alpha4\n\n## 0.1.0-alpha.2\n\n- Add default handler support\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "actix-files/Cargo.toml",
    "content": "[package]\nname = \"actix-files\"\nversion = \"0.6.10\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\", \"Rob Ede <robjtede@icloud.com>\"]\ndescription = \"Static file serving for Actix Web\"\nkeywords = [\"actix\", \"http\", \"async\", \"futures\"]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\ncategories = [\"asynchronous\", \"web-programming::http-server\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\"actix_http::*\", \"actix_service::*\", \"actix_web::*\", \"http::*\", \"mime::*\"]\n\n[features]\nexperimental-io-uring = [\"actix-web/experimental-io-uring\", \"tokio-uring\"]\n\n[dependencies]\nactix-http = \"3\"\nactix-service = \"2\"\nactix-utils = \"3\"\nactix-web = { version = \"4\", default-features = false }\n\nbitflags = \"2\"\nbytes = \"1\"\nderive_more = { version = \"2\", features = [\"deref\", \"deref_mut\", \"display\", \"error\", \"from\"] }\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nhttp-range = \"0.1.4\"\nlog = \"0.4\"\nmime = \"0.3.9\"\nmime_guess = \"2.0.1\"\npercent-encoding = \"2.1\"\npin-project-lite = \"0.2.7\"\nv_htmlescape = \"0.15.5\"\n\n# experimental-io-uring\n[target.'cfg(target_os = \"linux\")'.dependencies]\ntokio-uring = { version = \"0.5\", optional = true, features = [\"bytes\"] }\nactix-server = { version = \"2.4\", optional = true } # ensure matching tokio-uring versions\n\n[dev-dependencies]\nactix-rt = \"2.7\"\nactix-test = \"0.1\"\nactix-web = \"4\"\nenv_logger = \"0.11\"\nfiletime = \"0.2\"\ntempfile = \"3.2\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-files/README.md",
    "content": "# `actix-files`\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files)\n[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.9)](https://docs.rs/actix-files/0.6.9)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![License](https://img.shields.io/crates/l/actix-files.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-files/0.6.9/status.svg)](https://deps.rs/crate/actix-files/0.6.9)\n[![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n<!-- cargo-rdme start -->\n\nStatic file serving for Actix Web.\n\nProvides a non-blocking service for serving static files from disk.\n\n## Examples\n\n```rust\nuse actix_web::App;\nuse actix_files::Files;\n\nlet app = App::new()\n    .service(Files::new(\"/static\", \".\").prefer_utf8(true));\n```\n\n<!-- cargo-rdme end -->\n"
  },
  {
    "path": "actix-files/examples/guarded-listing.rs",
    "content": "use actix_files::Files;\nuse actix_web::{get, guard, middleware, App, HttpServer, Responder};\n\nconst EXAMPLES_DIR: &str = concat![env!(\"CARGO_MANIFEST_DIR\"), \"/examples\"];\n\n#[get(\"/\")]\nasync fn index() -> impl Responder {\n    \"Hello world!\"\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    log::info!(\"starting HTTP server at http://localhost:8080\");\n\n    HttpServer::new(|| {\n        App::new()\n            .service(index)\n            .service(\n                Files::new(\"/assets\", EXAMPLES_DIR)\n                    .show_files_listing()\n                    .guard(guard::Header(\"show-listing\", \"?1\")),\n            )\n            .service(Files::new(\"/assets\", EXAMPLES_DIR))\n            .wrap(middleware::Compress::default())\n            .wrap(middleware::Logger::default())\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .workers(2)\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-files/src/chunked.rs",
    "content": "use std::{\n    cmp, fmt,\n    future::Future,\n    io,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_web::{error::Error, web::Bytes};\n#[cfg(feature = \"experimental-io-uring\")]\nuse bytes::BytesMut;\nuse futures_core::{ready, Stream};\nuse pin_project_lite::pin_project;\n\nuse super::named::File;\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum ReadMode {\n    Sync,\n    Async,\n}\n\npin_project! {\n    /// Adapter to read a `std::file::File` in chunks.\n    #[doc(hidden)]\n    pub struct ChunkedReadFile<F, Fut> {\n        size: u64,\n        offset: u64,\n        #[pin]\n        state: ChunkedReadFileState<Fut>,\n        counter: u64,\n        callback: F,\n        read_mode: ReadMode,\n    }\n}\n\n#[cfg(not(feature = \"experimental-io-uring\"))]\npin_project! {\n    #[project = ChunkedReadFileStateProj]\n    #[project_replace = ChunkedReadFileStateProjReplace]\n    enum ChunkedReadFileState<Fut> {\n        File { file: Option<File>, },\n        Future { #[pin] fut: Fut },\n    }\n}\n\n#[cfg(feature = \"experimental-io-uring\")]\npin_project! {\n    #[project = ChunkedReadFileStateProj]\n    #[project_replace = ChunkedReadFileStateProjReplace]\n    enum ChunkedReadFileState<Fut> {\n        File { file: Option<(File, BytesMut)> },\n        Future { #[pin] fut: Fut },\n    }\n}\n\nimpl<F, Fut> fmt::Debug for ChunkedReadFile<F, Fut> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"ChunkedReadFile\")\n    }\n}\n\npub(crate) fn new_chunked_read(\n    size: u64,\n    offset: u64,\n    file: File,\n    read_mode_threshold: u64,\n) -> impl Stream<Item = Result<Bytes, Error>> {\n    ChunkedReadFile {\n        size,\n        offset,\n        #[cfg(not(feature = \"experimental-io-uring\"))]\n        state: ChunkedReadFileState::File { file: Some(file) },\n        #[cfg(feature = \"experimental-io-uring\")]\n        state: ChunkedReadFileState::File {\n            file: Some((file, BytesMut::new())),\n        },\n        counter: 0,\n        callback: chunked_read_file_callback,\n        read_mode: if size < read_mode_threshold {\n            ReadMode::Sync\n        } else {\n            ReadMode::Async\n        },\n    }\n}\n\n#[cfg(not(feature = \"experimental-io-uring\"))]\nfn chunked_read_file_callback_sync(\n    mut file: File,\n    offset: u64,\n    max_bytes: usize,\n) -> Result<(File, Bytes), io::Error> {\n    use io::{Read as _, Seek as _};\n\n    let mut buf = Vec::with_capacity(max_bytes);\n\n    file.seek(io::SeekFrom::Start(offset))?;\n\n    let n_bytes = file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;\n\n    if n_bytes == 0 {\n        Err(io::Error::from(io::ErrorKind::UnexpectedEof))\n    } else {\n        Ok((file, Bytes::from(buf)))\n    }\n}\n\n#[cfg(not(feature = \"experimental-io-uring\"))]\n#[inline]\nasync fn chunked_read_file_callback(\n    file: File,\n    offset: u64,\n    max_bytes: usize,\n    read_mode: ReadMode,\n) -> Result<(File, Bytes), Error> {\n    let res = match read_mode {\n        ReadMode::Sync => chunked_read_file_callback_sync(file, offset, max_bytes)?,\n        ReadMode::Async => {\n            actix_web::web::block(move || chunked_read_file_callback_sync(file, offset, max_bytes))\n                .await??\n        }\n    };\n\n    Ok(res)\n}\n\n#[cfg(feature = \"experimental-io-uring\")]\nasync fn chunked_read_file_callback(\n    file: File,\n    offset: u64,\n    max_bytes: usize,\n    mut bytes_mut: BytesMut,\n) -> io::Result<(File, Bytes, BytesMut)> {\n    bytes_mut.reserve(max_bytes);\n\n    let (res, mut bytes_mut) = file.read_at(bytes_mut, offset).await;\n    let n_bytes = res?;\n\n    if n_bytes == 0 {\n        return Err(io::ErrorKind::UnexpectedEof.into());\n    }\n\n    let bytes = bytes_mut.split_to(n_bytes).freeze();\n\n    Ok((file, bytes, bytes_mut))\n}\n\n#[cfg(feature = \"experimental-io-uring\")]\nimpl<F, Fut> Stream for ChunkedReadFile<F, Fut>\nwhere\n    F: Fn(File, u64, usize, BytesMut) -> Fut,\n    Fut: Future<Output = io::Result<(File, Bytes, BytesMut)>>,\n{\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let mut this = self.as_mut().project();\n        match this.state.as_mut().project() {\n            ChunkedReadFileStateProj::File { file } => {\n                let size = *this.size;\n                let offset = *this.offset;\n                let counter = *this.counter;\n\n                if size == counter {\n                    Poll::Ready(None)\n                } else {\n                    let max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;\n\n                    let (file, bytes_mut) = file\n                        .take()\n                        .expect(\"ChunkedReadFile polled after completion\");\n\n                    let fut = (this.callback)(file, offset, max_bytes, bytes_mut);\n\n                    this.state\n                        .project_replace(ChunkedReadFileState::Future { fut });\n\n                    self.poll_next(cx)\n                }\n            }\n            ChunkedReadFileStateProj::Future { fut } => {\n                let (file, bytes, bytes_mut) = ready!(fut.poll(cx))?;\n\n                this.state.project_replace(ChunkedReadFileState::File {\n                    file: Some((file, bytes_mut)),\n                });\n\n                *this.offset += bytes.len() as u64;\n                *this.counter += bytes.len() as u64;\n\n                Poll::Ready(Some(Ok(bytes)))\n            }\n        }\n    }\n}\n\n#[cfg(not(feature = \"experimental-io-uring\"))]\nimpl<F, Fut> Stream for ChunkedReadFile<F, Fut>\nwhere\n    F: Fn(File, u64, usize, ReadMode) -> Fut,\n    Fut: Future<Output = Result<(File, Bytes), Error>>,\n{\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let mut this = self.as_mut().project();\n        match this.state.as_mut().project() {\n            ChunkedReadFileStateProj::File { file } => {\n                let size = *this.size;\n                let offset = *this.offset;\n                let counter = *this.counter;\n\n                if size == counter {\n                    Poll::Ready(None)\n                } else {\n                    let max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;\n\n                    let file = file\n                        .take()\n                        .expect(\"ChunkedReadFile polled after completion\");\n\n                    let fut = (this.callback)(file, offset, max_bytes, *this.read_mode);\n\n                    this.state\n                        .project_replace(ChunkedReadFileState::Future { fut });\n\n                    self.poll_next(cx)\n                }\n            }\n            ChunkedReadFileStateProj::Future { fut } => {\n                let (file, bytes) = ready!(fut.poll(cx))?;\n\n                this.state\n                    .project_replace(ChunkedReadFileState::File { file: Some(file) });\n\n                *this.offset += bytes.len() as u64;\n                *this.counter += bytes.len() as u64;\n\n                Poll::Ready(Some(Ok(bytes)))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-files/src/directory.rs",
    "content": "use std::{\n    fmt::Write,\n    fs::DirEntry,\n    io,\n    path::{Path, PathBuf},\n};\n\nuse actix_web::{dev::ServiceResponse, HttpRequest, HttpResponse};\nuse percent_encoding::{utf8_percent_encode, CONTROLS};\nuse v_htmlescape::escape as escape_html_entity;\n\n/// A directory; responds with the generated directory listing.\n#[derive(Debug)]\npub struct Directory {\n    /// Base directory.\n    pub base: PathBuf,\n\n    /// Path of subdirectory to generate listing for.\n    pub path: PathBuf,\n}\n\nimpl Directory {\n    /// Create a new directory\n    pub fn new(base: PathBuf, path: PathBuf) -> Directory {\n        Directory { base, path }\n    }\n\n    /// Is this entry visible from this directory?\n    pub fn is_visible(&self, entry: &io::Result<DirEntry>) -> bool {\n        if let Ok(ref entry) = *entry {\n            if let Some(name) = entry.file_name().to_str() {\n                if name.starts_with('.') {\n                    return false;\n                }\n            }\n            if let Ok(ref md) = entry.metadata() {\n                let ft = md.file_type();\n                return ft.is_dir() || ft.is_file() || ft.is_symlink();\n            }\n        }\n        false\n    }\n}\n\npub(crate) type DirectoryRenderer =\n    dyn Fn(&Directory, &HttpRequest) -> Result<ServiceResponse, io::Error>;\n\n/// Returns percent encoded file URL path.\nmacro_rules! encode_file_url {\n    ($path:ident) => {\n        utf8_percent_encode(&$path, CONTROLS)\n    };\n}\n\n/// Returns HTML entity encoded formatter.\n///\n/// ```plain\n/// \" => &quot;\n/// & => &amp;\n/// ' => &#x27;\n/// < => &lt;\n/// > => &gt;\n/// / => &#x2f;\n/// ```\nmacro_rules! encode_file_name {\n    ($entry:ident) => {\n        escape_html_entity(&$entry.file_name().to_string_lossy())\n    };\n}\n\npub(crate) fn directory_listing(\n    dir: &Directory,\n    req: &HttpRequest,\n) -> Result<ServiceResponse, io::Error> {\n    let index_of = format!(\"Index of {}\", req.path());\n    let mut body = String::new();\n    let base = Path::new(req.path());\n\n    for entry in dir.path.read_dir()? {\n        if dir.is_visible(&entry) {\n            let entry = entry.unwrap();\n            let p = match entry.path().strip_prefix(&dir.path) {\n                Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\\\', \"/\"),\n                Ok(p) => base.join(p).to_string_lossy().into_owned(),\n                Err(_) => continue,\n            };\n\n            // if file is a directory, add '/' to the end of the name\n            if let Ok(metadata) = entry.metadata() {\n                if metadata.is_dir() {\n                    let _ = write!(\n                        body,\n                        \"<li><a href=\\\"{}\\\">{}/</a></li>\",\n                        encode_file_url!(p),\n                        encode_file_name!(entry),\n                    );\n                } else {\n                    let _ = write!(\n                        body,\n                        \"<li><a href=\\\"{}\\\">{}</a></li>\",\n                        encode_file_url!(p),\n                        encode_file_name!(entry),\n                    );\n                }\n            } else {\n                continue;\n            }\n        }\n    }\n\n    let html = format!(\n        \"<html>\\\n         <head><title>{}</title></head>\\\n         <body><h1>{}</h1>\\\n         <ul>\\\n         {}\\\n         </ul></body>\\n</html>\",\n        index_of, index_of, body\n    );\n    Ok(ServiceResponse::new(\n        req.clone(),\n        HttpResponse::Ok()\n            .content_type(\"text/html; charset=utf-8\")\n            .body(html),\n    ))\n}\n"
  },
  {
    "path": "actix-files/src/encoding.rs",
    "content": "use mime::Mime;\n\n/// Transforms MIME `text/*` types into their UTF-8 equivalent, if supported.\n///\n/// MIME types that are converted\n/// - application/javascript\n/// - text/html\n/// - text/css\n/// - text/plain\n/// - text/csv\n/// - text/tab-separated-values\npub(crate) fn equiv_utf8_text(ct: Mime) -> Mime {\n    // use (roughly) order of file-type popularity for a web server\n\n    if ct == mime::APPLICATION_JAVASCRIPT {\n        return mime::APPLICATION_JAVASCRIPT_UTF_8;\n    }\n\n    if ct == mime::TEXT_HTML {\n        return mime::TEXT_HTML_UTF_8;\n    }\n\n    if ct == mime::TEXT_CSS {\n        return mime::TEXT_CSS_UTF_8;\n    }\n\n    if ct == mime::TEXT_PLAIN {\n        return mime::TEXT_PLAIN_UTF_8;\n    }\n\n    if ct == mime::TEXT_CSV {\n        return mime::TEXT_CSV_UTF_8;\n    }\n\n    if ct == mime::TEXT_TAB_SEPARATED_VALUES {\n        return mime::TEXT_TAB_SEPARATED_VALUES_UTF_8;\n    }\n\n    ct\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_equiv_utf8_text() {\n        assert_eq!(equiv_utf8_text(mime::TEXT_PLAIN), mime::TEXT_PLAIN_UTF_8);\n        assert_eq!(equiv_utf8_text(mime::TEXT_XML), mime::TEXT_XML);\n        assert_eq!(equiv_utf8_text(mime::IMAGE_PNG), mime::IMAGE_PNG);\n    }\n}\n"
  },
  {
    "path": "actix-files/src/error.rs",
    "content": "use actix_web::{http::StatusCode, ResponseError};\nuse derive_more::Display;\n\n/// Errors which can occur when serving static files.\n#[derive(Debug, PartialEq, Eq, Display)]\npub enum FilesError {\n    /// Path is not a directory.\n    #[allow(dead_code)]\n    #[display(\"path is not a directory. Unable to serve static files\")]\n    IsNotDirectory,\n\n    /// Cannot render directory.\n    #[display(\"unable to render directory without index file\")]\n    IsDirectory,\n}\n\nimpl ResponseError for FilesError {\n    /// Returns `404 Not Found`.\n    fn status_code(&self) -> StatusCode {\n        StatusCode::NOT_FOUND\n    }\n}\n\n/// Error which can occur with parsing/validating a request-uri path\n#[derive(Debug, PartialEq, Eq, Display)]\n#[non_exhaustive]\npub enum UriSegmentError {\n    /// Segment started with the wrapped invalid character.\n    #[display(\"segment started with invalid character: ('{_0}')\")]\n    BadStart(char),\n\n    /// Segment contained the wrapped invalid character.\n    #[display(\"segment contained invalid character ('{_0}')\")]\n    BadChar(char),\n\n    /// Segment ended with the wrapped invalid character.\n    #[display(\"segment ended with invalid character: ('{_0}')\")]\n    BadEnd(char),\n\n    /// Path is not a valid UTF-8 string after percent-decoding.\n    #[display(\"path is not a valid UTF-8 string after percent-decoding\")]\n    NotValidUtf8,\n}\n\nimpl ResponseError for UriSegmentError {\n    /// Returns `400 Bad Request`.\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n"
  },
  {
    "path": "actix-files/src/files.rs",
    "content": "use std::{\n    cell::RefCell,\n    fmt, io,\n    path::{Path, PathBuf},\n    rc::Rc,\n};\n\nuse actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt};\nuse actix_web::{\n    dev::{\n        AppService, HttpServiceFactory, RequestHead, ResourceDef, ServiceRequest, ServiceResponse,\n    },\n    error::Error,\n    guard::Guard,\n    http::header::DispositionType,\n    HttpRequest,\n};\nuse futures_core::future::LocalBoxFuture;\n\nuse crate::{\n    directory_listing, named,\n    service::{FilesService, FilesServiceInner},\n    Directory, DirectoryRenderer, HttpNewService, MimeOverride, PathFilter,\n};\n\n/// Static files handling service.\n///\n/// `Files` service must be registered with `App::service()` method.\n///\n/// # Examples\n/// ```\n/// use actix_web::App;\n/// use actix_files::Files;\n///\n/// let app = App::new()\n///     .service(Files::new(\"/static\", \".\"));\n/// ```\npub struct Files {\n    mount_path: String,\n    directory: PathBuf,\n    index: Option<String>,\n    show_index: bool,\n    redirect_to_slash: bool,\n    with_permanent_redirect: bool,\n    default: Rc<RefCell<Option<Rc<HttpNewService>>>>,\n    renderer: Rc<DirectoryRenderer>,\n    mime_override: Option<Rc<MimeOverride>>,\n    path_filter: Option<Rc<PathFilter>>,\n    file_flags: named::Flags,\n    use_guards: Option<Rc<dyn Guard>>,\n    guards: Vec<Rc<dyn Guard>>,\n    hidden_files: bool,\n    try_compressed: bool,\n    read_mode_threshold: u64,\n}\n\nimpl fmt::Debug for Files {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"Files\")\n    }\n}\n\nimpl Clone for Files {\n    fn clone(&self) -> Self {\n        Self {\n            directory: self.directory.clone(),\n            index: self.index.clone(),\n            show_index: self.show_index,\n            redirect_to_slash: self.redirect_to_slash,\n            with_permanent_redirect: self.with_permanent_redirect,\n            default: self.default.clone(),\n            renderer: self.renderer.clone(),\n            file_flags: self.file_flags,\n            mount_path: self.mount_path.clone(),\n            mime_override: self.mime_override.clone(),\n            path_filter: self.path_filter.clone(),\n            use_guards: self.use_guards.clone(),\n            guards: self.guards.clone(),\n            hidden_files: self.hidden_files,\n            try_compressed: self.try_compressed,\n            read_mode_threshold: self.read_mode_threshold,\n        }\n    }\n}\n\nimpl Files {\n    /// Create new `Files` instance for a specified base directory.\n    ///\n    /// # Argument Order\n    /// The first argument (`mount_path`) is the root URL at which the static files are served.\n    /// For example, `/assets` will serve files at `example.com/assets/...`.\n    ///\n    /// The second argument (`serve_from`) is the location on disk at which files are loaded.\n    /// This can be a relative path. For example, `./` would serve files from the current\n    /// working directory.\n    ///\n    /// # Implementation Notes\n    /// If the mount path is set as the root path `/`, services registered after this one will\n    /// be inaccessible. Register more specific handlers and services first.\n    ///\n    /// If `serve_from` cannot be canonicalized at startup, an error is logged and the original\n    /// path is preserved. Requests will return `404 Not Found` until the path exists.\n    ///\n    /// `Files` utilizes the existing Tokio thread-pool for blocking filesystem operations.\n    /// The number of running threads is adjusted over time as needed, up to a maximum of 512 times\n    /// the number of server [workers](actix_web::HttpServer::workers), by default.\n    pub fn new<T: Into<PathBuf>>(mount_path: &str, serve_from: T) -> Files {\n        let orig_dir = serve_from.into();\n        let dir = match orig_dir.canonicalize() {\n            Ok(canon_dir) => canon_dir,\n            Err(_) => {\n                log::error!(\"Specified path is not a directory: {:?}\", orig_dir);\n                // Preserve original path so requests don't fall back to CWD.\n                orig_dir\n            }\n        };\n\n        Files {\n            mount_path: mount_path.trim_end_matches('/').to_owned(),\n            directory: dir,\n            index: None,\n            show_index: false,\n            redirect_to_slash: false,\n            with_permanent_redirect: false,\n            default: Rc::new(RefCell::new(None)),\n            renderer: Rc::new(directory_listing),\n            mime_override: None,\n            path_filter: None,\n            file_flags: named::Flags::default(),\n            use_guards: None,\n            guards: Vec::new(),\n            hidden_files: false,\n            try_compressed: false,\n            read_mode_threshold: 0,\n        }\n    }\n\n    /// Show files listing for directories.\n    ///\n    /// By default show files listing is disabled.\n    ///\n    /// When used with [`Files::index_file()`], files listing is shown as a fallback\n    /// when the index file is not found.\n    pub fn show_files_listing(mut self) -> Self {\n        self.show_index = true;\n        self\n    }\n\n    /// Redirects to a slash-ended path when browsing a directory.\n    ///\n    /// By default never redirect.\n    pub fn redirect_to_slash_directory(mut self) -> Self {\n        self.redirect_to_slash = true;\n        self\n    }\n\n    /// Redirect with permanent redirect status code (308).\n    ///\n    /// By default redirect with temporary redirect status code (307).\n    pub fn with_permanent_redirect(mut self) -> Self {\n        self.with_permanent_redirect = true;\n        self\n    }\n\n    /// Set custom directory renderer.\n    pub fn files_listing_renderer<F>(mut self, f: F) -> Self\n    where\n        for<'r, 's> F:\n            Fn(&'r Directory, &'s HttpRequest) -> Result<ServiceResponse, io::Error> + 'static,\n    {\n        self.renderer = Rc::new(f);\n        self\n    }\n\n    /// Specifies MIME override callback.\n    pub fn mime_override<F>(mut self, f: F) -> Self\n    where\n        F: Fn(&mime::Name<'_>) -> DispositionType + 'static,\n    {\n        self.mime_override = Some(Rc::new(f));\n        self\n    }\n\n    /// Sets path filtering closure.\n    ///\n    /// The path provided to the closure is relative to `serve_from` path.\n    /// You can safely join this path with the `serve_from` path to get the real path.\n    /// However, the real path may not exist since the filter is called before checking path existence.\n    ///\n    /// When a path doesn't pass the filter, [`Files::default_handler`] is called if set, otherwise,\n    /// `404 Not Found` is returned.\n    ///\n    /// # Examples\n    /// ```\n    /// use std::path::Path;\n    /// use actix_files::Files;\n    ///\n    /// // prevent searching subdirectories and following symlinks\n    /// let files_service = Files::new(\"/\", \"./static\").path_filter(|path, _| {\n    ///     path.components().count() == 1\n    ///         && Path::new(\"./static\")\n    ///             .join(path)\n    ///             .symlink_metadata()\n    ///             .map(|m| !m.file_type().is_symlink())\n    ///             .unwrap_or(false)\n    /// });\n    /// ```\n    pub fn path_filter<F>(mut self, f: F) -> Self\n    where\n        F: Fn(&Path, &RequestHead) -> bool + 'static,\n    {\n        self.path_filter = Some(Rc::new(f));\n        self\n    }\n\n    /// Sets index file for directory requests.\n    ///\n    /// When a directory is requested, this value is appended to the directory's path on disk.\n    /// Therefore, the index file path is relative to the served directory (the `serve_from` path\n    /// passed to [`Files::new`]) and should not include the `serve_from` prefix.\n    ///\n    /// For example, to serve `./static/index.html` when mounting `Files::new(\"/\", \"./static\")`,\n    /// configure it as `.index_file(\"index.html\")` (not `.index_file(\"./static/index.html\")`).\n    ///\n    /// If the index file is not found, files listing is shown as a fallback if\n    /// [`Files::show_files_listing()`] is set.\n    pub fn index_file<T: Into<String>>(mut self, index: T) -> Self {\n        self.index = Some(index.into());\n        self\n    }\n\n    /// Sets the size threshold that determines file read mode (sync/async).\n    ///\n    /// When a file is smaller than the threshold (bytes), the reader will use synchronous\n    /// (blocking) file reads. For larger files, it switches to async reads to avoid blocking the\n    /// main thread.\n    ///\n    /// Tweaking this value according to your expected usage may lead to significant performance\n    /// gains (or losses in other handlers, if `size` is too high).\n    ///\n    /// When the `experimental-io-uring` crate feature is enabled, file reads are always async.\n    ///\n    /// Default is 0, meaning all files are read asynchronously.\n    pub fn read_mode_threshold(mut self, size: u64) -> Self {\n        self.read_mode_threshold = size;\n        self\n    }\n\n    /// Specifies whether to use ETag or not.\n    ///\n    /// Default is true.\n    pub fn use_etag(mut self, value: bool) -> Self {\n        self.file_flags.set(named::Flags::ETAG, value);\n        self\n    }\n\n    /// Specifies whether to use Last-Modified or not.\n    ///\n    /// Default is true.\n    pub fn use_last_modified(mut self, value: bool) -> Self {\n        self.file_flags.set(named::Flags::LAST_MD, value);\n        self\n    }\n\n    /// Specifies whether text responses should signal a UTF-8 encoding.\n    ///\n    /// Default is false (but will default to true in a future version).\n    pub fn prefer_utf8(mut self, value: bool) -> Self {\n        self.file_flags.set(named::Flags::PREFER_UTF8, value);\n        self\n    }\n\n    /// Adds a routing guard.\n    ///\n    /// Use this to allow multiple chained file services that respond to strictly different\n    /// properties of a request. Due to the way routing works, if a guard check returns true and the\n    /// request starts being handled by the file service, it will not be able to back-out and try\n    /// the next service, you will simply get a 404 (or 405) error response.\n    ///\n    /// To allow `POST` requests to retrieve files, see [`Files::method_guard()`].\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{guard::Header, App};\n    /// use actix_files::Files;\n    ///\n    /// App::new().service(\n    ///     Files::new(\"/\",\"/my/site/files\")\n    ///         .guard(Header(\"Host\", \"example.com\"))\n    /// );\n    /// ```\n    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {\n        self.guards.push(Rc::new(guard));\n        self\n    }\n\n    /// Specifies guard to check before fetching directory listings or files.\n    ///\n    /// Note that this guard has no effect on routing; it's main use is to guard on the request's\n    /// method just before serving the file, only allowing `GET` and `HEAD` requests by default.\n    /// See [`Files::guard`] for routing guards.\n    pub fn method_guard<G: Guard + 'static>(mut self, guard: G) -> Self {\n        self.use_guards = Some(Rc::new(guard));\n        self\n    }\n\n    /// See [`Files::method_guard`].\n    #[doc(hidden)]\n    #[deprecated(since = \"0.6.0\", note = \"Renamed to `method_guard`.\")]\n    pub fn use_guards<G: Guard + 'static>(self, guard: G) -> Self {\n        self.method_guard(guard)\n    }\n\n    /// Disable `Content-Disposition` header.\n    ///\n    /// By default Content-Disposition` header is enabled.\n    pub fn disable_content_disposition(mut self) -> Self {\n        self.file_flags.remove(named::Flags::CONTENT_DISPOSITION);\n        self\n    }\n\n    /// Sets default handler which is used when no matched file could be found.\n    ///\n    /// # Examples\n    /// Setting a fallback static file handler:\n    /// ```\n    /// use actix_files::{Files, NamedFile};\n    /// use actix_web::dev::{ServiceRequest, ServiceResponse, fn_service};\n    ///\n    /// # fn run() -> Result<(), actix_web::Error> {\n    /// let files = Files::new(\"/\", \"./static\")\n    ///     .index_file(\"index.html\")\n    ///     .default_handler(fn_service(|req: ServiceRequest| async {\n    ///         let (req, _) = req.into_parts();\n    ///         let file = NamedFile::open_async(\"./static/404.html\").await?;\n    ///         let res = file.into_response(&req);\n    ///         Ok(ServiceResponse::new(req, res))\n    ///     }));\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn default_handler<F, U>(mut self, f: F) -> Self\n    where\n        F: IntoServiceFactory<U, ServiceRequest>,\n        U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>\n            + 'static,\n    {\n        // create and configure default resource\n        self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(\n            f.into_factory().map_init_err(|_| ()),\n        )))));\n\n        self\n    }\n\n    /// Enables serving hidden files and directories, allowing a leading dots in url fragments.\n    pub fn use_hidden_files(mut self) -> Self {\n        self.hidden_files = true;\n        self\n    }\n\n    /// Attempts to search for a suitable pre-compressed version of a file on disk before falling\n    /// back to the uncompressed version.\n    ///\n    /// Currently, `.gz`, `.br`, and `.zst` files are supported.\n    pub fn try_compressed(mut self) -> Self {\n        self.try_compressed = true;\n        self\n    }\n}\n\nimpl HttpServiceFactory for Files {\n    fn register(mut self, config: &mut AppService) {\n        let guards = if self.guards.is_empty() {\n            None\n        } else {\n            let guards = std::mem::take(&mut self.guards);\n            Some(\n                guards\n                    .into_iter()\n                    .map(|guard| -> Box<dyn Guard> { Box::new(guard) })\n                    .collect::<Vec<_>>(),\n            )\n        };\n\n        if self.default.borrow().is_none() {\n            *self.default.borrow_mut() = Some(config.default_service());\n        }\n\n        let rdef = if config.is_root() {\n            ResourceDef::root_prefix(&self.mount_path)\n        } else {\n            ResourceDef::prefix(&self.mount_path)\n        };\n\n        config.register_service(rdef, guards, self, None)\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for Files {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = FilesService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let mut inner = FilesServiceInner {\n            directory: self.directory.clone(),\n            index: self.index.clone(),\n            show_index: self.show_index,\n            redirect_to_slash: self.redirect_to_slash,\n            default: None,\n            renderer: self.renderer.clone(),\n            mime_override: self.mime_override.clone(),\n            path_filter: self.path_filter.clone(),\n            file_flags: self.file_flags,\n            guards: self.use_guards.clone(),\n            hidden_files: self.hidden_files,\n            try_compressed: self.try_compressed,\n            size_threshold: self.read_mode_threshold,\n            with_permanent_redirect: self.with_permanent_redirect,\n        };\n\n        if let Some(ref default) = *self.default.borrow() {\n            let fut = default.new_service(());\n            Box::pin(async {\n                match fut.await {\n                    Ok(default) => {\n                        inner.default = Some(default);\n                        Ok(FilesService(Rc::new(inner)))\n                    }\n                    Err(_) => Err(()),\n                }\n            })\n        } else {\n            Box::pin(async move { Ok(FilesService(Rc::new(inner))) })\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_web::{\n        http::StatusCode,\n        test::{self, TestRequest},\n        App, HttpResponse,\n    };\n\n    use super::*;\n\n    #[actix_web::test]\n    async fn custom_files_listing_renderer() {\n        let srv = test::init_service(\n            App::new().service(\n                Files::new(\"/\", \"./tests\")\n                    .show_files_listing()\n                    .files_listing_renderer(|dir, req| {\n                        Ok(ServiceResponse::new(\n                            req.clone(),\n                            HttpResponse::Ok().body(dir.path.to_str().unwrap().to_owned()),\n                        ))\n                    }),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/\").to_request();\n        let res = test::call_service(&srv, req).await;\n\n        assert_eq!(res.status(), StatusCode::OK);\n        let body = test::read_body(res).await;\n        let body_str = std::str::from_utf8(&body).unwrap();\n        let actual_path = Path::new(&body_str);\n        let expected_path = Path::new(\"actix-files/tests\");\n        assert!(\n            actual_path.ends_with(expected_path),\n            \"body {:?} does not end with {:?}\",\n            actual_path,\n            expected_path\n        );\n    }\n}\n"
  },
  {
    "path": "actix-files/src/lib.rs",
    "content": "//! Static file serving for Actix Web.\n//!\n//! Provides a non-blocking service for serving static files from disk.\n//!\n//! # Examples\n//! ```\n//! use actix_web::App;\n//! use actix_files::Files;\n//!\n//! let app = App::new()\n//!     .service(Files::new(\"/static\", \".\").prefer_utf8(true));\n//! ```\n\n#![warn(missing_docs, missing_debug_implementations)]\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\nuse std::path::Path;\n\nuse actix_service::boxed::{BoxService, BoxServiceFactory};\nuse actix_web::{\n    dev::{RequestHead, ServiceRequest, ServiceResponse},\n    error::Error,\n    http::header::DispositionType,\n};\nuse mime_guess::from_ext;\n\nmod chunked;\nmod directory;\nmod encoding;\nmod error;\nmod files;\nmod named;\nmod path_buf;\nmod range;\nmod service;\n\npub use self::{\n    chunked::ChunkedReadFile, directory::Directory, error::UriSegmentError, files::Files,\n    named::NamedFile, path_buf::PathBufWrap, range::HttpRange, service::FilesService,\n};\nuse self::{\n    directory::{directory_listing, DirectoryRenderer},\n    error::FilesError,\n};\n\ntype HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;\ntype HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;\n\n/// Return the MIME type associated with a filename extension (case-insensitive).\n/// If `ext` is empty or no associated type for the extension was found, returns\n/// the type `application/octet-stream`.\n#[inline]\npub fn file_extension_to_mime(ext: &str) -> mime::Mime {\n    from_ext(ext).first_or_octet_stream()\n}\n\ntype MimeOverride = dyn Fn(&mime::Name<'_>) -> DispositionType;\n\ntype PathFilter = dyn Fn(&Path, &RequestHead) -> bool;\n\n#[cfg(test)]\nmod tests {\n    use std::{\n        fmt::Write as _,\n        fs::{self},\n        ops::Add,\n        time::{Duration, SystemTime},\n    };\n\n    use actix_web::{\n        dev::ServiceFactory,\n        guard,\n        http::{\n            header::{self, ContentDisposition, DispositionParam},\n            Method, StatusCode,\n        },\n        middleware::Compress,\n        test::{self, TestRequest},\n        web::{self, Bytes},\n        App, HttpResponse, Responder,\n    };\n\n    use super::*;\n    use crate::named::File;\n\n    #[actix_web::test]\n    async fn test_file_extension_to_mime() {\n        let m = file_extension_to_mime(\"\");\n        assert_eq!(m, mime::APPLICATION_OCTET_STREAM);\n\n        let m = file_extension_to_mime(\"jpg\");\n        assert_eq!(m, mime::IMAGE_JPEG);\n\n        let m = file_extension_to_mime(\"invalid extension!!\");\n        assert_eq!(m, mime::APPLICATION_OCTET_STREAM);\n\n        let m = file_extension_to_mime(\"\");\n        assert_eq!(m, mime::APPLICATION_OCTET_STREAM);\n    }\n\n    #[actix_rt::test]\n    async fn test_if_modified_since_without_if_none_match() {\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));\n\n        let req = TestRequest::default()\n            .insert_header((header::IF_MODIFIED_SINCE, since))\n            .to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);\n    }\n\n    #[actix_rt::test]\n    async fn test_if_modified_since_without_if_none_match_same() {\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let since = file.last_modified().unwrap();\n\n        let req = TestRequest::default()\n            .insert_header((header::IF_MODIFIED_SINCE, since))\n            .to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);\n    }\n\n    #[actix_rt::test]\n    async fn test_if_modified_since_with_if_none_match() {\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));\n\n        let req = TestRequest::default()\n            .insert_header((header::IF_NONE_MATCH, \"miss_etag\"))\n            .insert_header((header::IF_MODIFIED_SINCE, since))\n            .to_http_request();\n        let resp = file.respond_to(&req);\n        assert_ne!(resp.status(), StatusCode::NOT_MODIFIED);\n    }\n\n    #[actix_rt::test]\n    async fn test_if_unmodified_since() {\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let since = file.last_modified().unwrap();\n\n        let req = TestRequest::default()\n            .insert_header((header::IF_UNMODIFIED_SINCE, since))\n            .to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_if_unmodified_since_failed() {\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let since = header::HttpDate::from(SystemTime::UNIX_EPOCH);\n\n        let req = TestRequest::default()\n            .insert_header((header::IF_UNMODIFIED_SINCE, since))\n            .to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::PRECONDITION_FAILED);\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_text() {\n        assert!(NamedFile::open_async(\"test--\").await.is_err());\n        let mut file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/x-toml\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"Cargo.toml\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_content_disposition() {\n        assert!(NamedFile::open_async(\"test--\").await.is_err());\n        let mut file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"Cargo.toml\\\"\"\n        );\n\n        let file = NamedFile::open_async(\"Cargo.toml\")\n            .await\n            .unwrap()\n            .disable_content_disposition();\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none());\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_non_ascii_file_name() {\n        let file = {\n            #[cfg(feature = \"experimental-io-uring\")]\n            {\n                crate::named::File::open(\"Cargo.toml\").await.unwrap()\n            }\n\n            #[cfg(not(feature = \"experimental-io-uring\"))]\n            {\n                crate::named::File::open(\"Cargo.toml\").unwrap()\n            }\n        };\n\n        let mut file = NamedFile::from_file(file, \"貨物.toml\").unwrap();\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/x-toml\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"貨物.toml\\\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_set_content_type() {\n        let mut file = NamedFile::open_async(\"Cargo.toml\")\n            .await\n            .unwrap()\n            .set_content_type(mime::TEXT_XML);\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/xml\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"Cargo.toml\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_image() {\n        let mut file = NamedFile::open_async(\"tests/test.png\").await.unwrap();\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"image/png\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"test.png\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_javascript() {\n        let file = NamedFile::open_async(\"tests/test.js\").await.unwrap();\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/javascript\",\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"test.js\\\"\",\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_image_attachment() {\n        let cd = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::Filename(String::from(\"test.png\"))],\n        };\n        let mut file = NamedFile::open_async(\"tests/test.png\")\n            .await\n            .unwrap()\n            .set_content_disposition(cd);\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"image/png\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"attachment; filename=\\\"test.png\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_binary() {\n        let mut file = NamedFile::open_async(\"tests/test.binary\").await.unwrap();\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"application/octet-stream\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"attachment; filename=\\\"test.binary\\\"\"\n        );\n    }\n\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn status_code_customize_same_output() {\n        let file1 = NamedFile::open_async(\"Cargo.toml\")\n            .await\n            .unwrap()\n            .set_status_code(StatusCode::NOT_FOUND);\n\n        let file2 = NamedFile::open_async(\"Cargo.toml\")\n            .await\n            .unwrap()\n            .customize()\n            .with_status(StatusCode::NOT_FOUND);\n\n        let req = TestRequest::default().to_http_request();\n        let res1 = file1.respond_to(&req);\n        let res2 = file2.respond_to(&req);\n\n        assert_eq!(res1.status(), StatusCode::NOT_FOUND);\n        assert_eq!(res2.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_status_code_text() {\n        let mut file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n\n        {\n            file.file();\n            let _f: &File = &file;\n        }\n\n        {\n            let _f: &mut File = &mut file;\n        }\n\n        let file = file.customize().with_status(StatusCode::NOT_FOUND);\n\n        let req = TestRequest::default().to_http_request();\n        let resp = file.respond_to(&req);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/x-toml\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"Cargo.toml\\\"\"\n        );\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_mime_override() {\n        fn all_attachment(_: &mime::Name<'_>) -> DispositionType {\n            DispositionType::Attachment\n        }\n\n        let srv = test::init_service(\n            App::new().service(\n                Files::new(\"/\", \".\")\n                    .mime_override(all_attachment)\n                    .index_file(\"Cargo.toml\"),\n            ),\n        )\n        .await;\n\n        let request = TestRequest::get().uri(\"/\").to_request();\n        let response = test::call_service(&srv, request).await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        let content_disposition = response\n            .headers()\n            .get(header::CONTENT_DISPOSITION)\n            .expect(\"To have CONTENT_DISPOSITION\");\n        let content_disposition = content_disposition\n            .to_str()\n            .expect(\"Convert CONTENT_DISPOSITION to str\");\n        assert_eq!(content_disposition, \"attachment; filename=\\\"Cargo.toml\\\"\");\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_ranges_status_code() {\n        let srv = test::init_service(\n            App::new().service(Files::new(\"/test\", \".\").index_file(\"Cargo.toml\")),\n        )\n        .await;\n\n        // Valid range header\n        let request = TestRequest::get()\n            .uri(\"/t%65st/Cargo.toml\")\n            .insert_header((header::RANGE, \"bytes=10-20\"))\n            .to_request();\n        let response = test::call_service(&srv, request).await;\n        assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);\n\n        // Invalid range header\n        let request = TestRequest::get()\n            .uri(\"/t%65st/Cargo.toml\")\n            .insert_header((header::RANGE, \"bytes=1-0\"))\n            .to_request();\n        let response = test::call_service(&srv, request).await;\n\n        assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_empty_range_headers() {\n        let srv = actix_test::start(|| App::new().service(Files::new(\"/\", \".\")));\n\n        for range in [\"\", \"bytes=\"] {\n            let response = srv\n                .get(\"/tests/test.binary\")\n                .insert_header((header::RANGE, range))\n                .send()\n                .await\n                .unwrap();\n\n            assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);\n            let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();\n            assert_eq!(content_range.to_str().unwrap(), \"bytes */100\");\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_content_range_headers() {\n        let srv = actix_test::start(|| App::new().service(Files::new(\"/\", \".\")));\n\n        // Valid range header\n        let response = srv\n            .get(\"/tests/test.binary\")\n            .insert_header((header::RANGE, \"bytes=10-20\"))\n            .send()\n            .await\n            .unwrap();\n        let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();\n        assert_eq!(content_range.to_str().unwrap(), \"bytes 10-20/100\");\n\n        // Invalid range header\n        let response = srv\n            .get(\"/tests/test.binary\")\n            .insert_header((header::RANGE, \"bytes=10-5\"))\n            .send()\n            .await\n            .unwrap();\n        let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();\n        assert_eq!(content_range.to_str().unwrap(), \"bytes */100\");\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_range_header_from_zero_to_end_returns_partial_content() {\n        let srv = actix_test::start(|| App::new().service(Files::new(\"/\", \".\")));\n\n        let response = srv\n            .get(\"/tests/test.binary\")\n            .insert_header((header::RANGE, \"bytes=0-\"))\n            .send()\n            .await\n            .unwrap();\n\n        assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);\n\n        let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();\n        assert_eq!(content_range.to_str().unwrap(), \"bytes 0-99/100\");\n\n        let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(content_length.to_str().unwrap(), \"100\");\n\n        // Should be no transfer-encoding\n        let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING);\n        assert!(transfer_encoding.is_none());\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_content_length_headers() {\n        let srv = actix_test::start(|| App::new().service(Files::new(\"/\", \".\")));\n\n        // Valid range header\n        let response = srv\n            .get(\"/tests/test.binary\")\n            .insert_header((header::RANGE, \"bytes=10-20\"))\n            .send()\n            .await\n            .unwrap();\n        let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(content_length.to_str().unwrap(), \"11\");\n\n        // Valid range header, starting from 0\n        let response = srv\n            .get(\"/tests/test.binary\")\n            .insert_header((header::RANGE, \"bytes=0-20\"))\n            .send()\n            .await\n            .unwrap();\n        let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(content_length.to_str().unwrap(), \"21\");\n\n        // Without range header\n        let mut response = srv.get(\"/tests/test.binary\").send().await.unwrap();\n        let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(content_length.to_str().unwrap(), \"100\");\n\n        // Should be no transfer-encoding\n        let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING);\n        assert!(transfer_encoding.is_none());\n\n        // Check file contents\n        let bytes = response.body().await.unwrap();\n        let data = web::Bytes::from(fs::read(\"tests/test.binary\").unwrap());\n        assert_eq!(bytes, data);\n    }\n\n    #[actix_rt::test]\n    async fn test_head_content_length_headers() {\n        let srv = actix_test::start(|| App::new().service(Files::new(\"/\", \".\")));\n\n        let response = srv.head(\"/tests/test.binary\").send().await.unwrap();\n\n        let content_length = response\n            .headers()\n            .get(header::CONTENT_LENGTH)\n            .unwrap()\n            .to_str()\n            .unwrap();\n\n        assert_eq!(content_length, \"100\");\n    }\n\n    #[actix_rt::test]\n    async fn test_static_files_with_spaces() {\n        let srv =\n            test::init_service(App::new().service(Files::new(\"/\", \".\").index_file(\"Cargo.toml\")))\n                .await;\n        let request = TestRequest::get()\n            .uri(\"/tests/test%20space.binary\")\n            .to_request();\n        let response = test::call_service(&srv, request).await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        let bytes = test::read_body(response).await;\n        let data = web::Bytes::from(fs::read(\"tests/test space.binary\").unwrap());\n        assert_eq!(bytes, data);\n    }\n\n    #[cfg(not(target_os = \"windows\"))]\n    #[actix_rt::test]\n    async fn test_static_files_with_special_characters() {\n        // Create the file we want to test against ad-hoc. We can't check it in as otherwise\n        // Windows can't even checkout this repository.\n        let temp_dir = tempfile::tempdir().unwrap();\n        let file_with_newlines = temp_dir.path().join(\"test\\n\\x0B\\x0C\\rnewline.text\");\n        fs::write(&file_with_newlines, \"Look at my newlines\").unwrap();\n\n        let srv = test::init_service(\n            App::new().service(Files::new(\"/\", temp_dir.path()).index_file(\"Cargo.toml\")),\n        )\n        .await;\n        let request = TestRequest::get()\n            .uri(\"/test%0A%0B%0C%0Dnewline.text\")\n            .to_request();\n        let response = test::call_service(&srv, request).await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        let bytes = test::read_body(response).await;\n        let data = web::Bytes::from(fs::read(file_with_newlines).unwrap());\n        assert_eq!(bytes, data);\n    }\n\n    #[actix_rt::test]\n    async fn test_files_not_allowed() {\n        let srv = test::init_service(App::new().service(Files::new(\"/\", \".\"))).await;\n\n        let req = TestRequest::default()\n            .uri(\"/Cargo.toml\")\n            .method(Method::POST)\n            .to_request();\n\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n\n        let srv = test::init_service(App::new().service(Files::new(\"/\", \".\"))).await;\n        let req = TestRequest::default()\n            .method(Method::PUT)\n            .uri(\"/Cargo.toml\")\n            .to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n    }\n\n    #[actix_rt::test]\n    async fn test_files_guards() {\n        let srv = test::init_service(\n            App::new().service(Files::new(\"/\", \".\").method_guard(guard::Post())),\n        )\n        .await;\n\n        let req = TestRequest::default()\n            .uri(\"/Cargo.toml\")\n            .method(Method::POST)\n            .to_request();\n\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_content_encoding() {\n        let srv = test::init_service(App::new().wrap(Compress::default()).service(\n            web::resource(\"/\").to(|| async {\n                NamedFile::open_async(\"Cargo.toml\")\n                    .await\n                    .unwrap()\n                    .set_content_encoding(header::ContentEncoding::Identity)\n            }),\n        ))\n        .await;\n\n        let request = TestRequest::get()\n            .uri(\"/\")\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&srv, request).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert!(res.headers().contains_key(header::CONTENT_ENCODING));\n        assert!(!test::read_body(res).await.is_empty());\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_content_encoding_gzip() {\n        let srv = test::init_service(App::new().wrap(Compress::default()).service(\n            web::resource(\"/\").to(|| async {\n                NamedFile::open_async(\"Cargo.toml\")\n                    .await\n                    .unwrap()\n                    .set_content_encoding(header::ContentEncoding::Gzip)\n            }),\n        ))\n        .await;\n\n        let request = TestRequest::get()\n            .uri(\"/\")\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&srv, request).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers()\n                .get(header::CONTENT_ENCODING)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"gzip\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_allowed_method() {\n        let req = TestRequest::default().method(Method::GET).to_http_request();\n        let file = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let resp = file.respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_static_files() {\n        let srv =\n            test::init_service(App::new().service(Files::new(\"/\", \".\").show_files_listing())).await;\n        let req = TestRequest::with_uri(\"/missing\").to_request();\n\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let srv = test::init_service(App::new().service(Files::new(\"/\", \".\"))).await;\n\n        let req = TestRequest::default().to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let srv =\n            test::init_service(App::new().service(Files::new(\"/\", \".\").show_files_listing())).await;\n        let req = TestRequest::with_uri(\"/tests\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/html; charset=utf-8\"\n        );\n\n        let bytes = test::read_body(resp).await;\n        assert!(format!(\"{:?}\", bytes).contains(\"/tests/test.png\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_to_slash_directory() {\n        // should not redirect if no index and files listing is disabled\n        let srv = test::init_service(\n            App::new().service(Files::new(\"/\", \".\").redirect_to_slash_directory()),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/tests\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        // should redirect if index present\n        let srv = test::init_service(\n            App::new().service(\n                Files::new(\"/\", \".\")\n                    .index_file(\"test.png\")\n                    .redirect_to_slash_directory(),\n            ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/tests\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::TEMPORARY_REDIRECT);\n\n        // should redirect if index present with permanent redirect\n        let srv = test::init_service(\n            App::new().service(\n                Files::new(\"/\", \".\")\n                    .index_file(\"test.png\")\n                    .redirect_to_slash_directory()\n                    .with_permanent_redirect(),\n            ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/tests\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::PERMANENT_REDIRECT);\n\n        // should redirect if files listing is enabled\n        let srv = test::init_service(\n            App::new().service(\n                Files::new(\"/\", \".\")\n                    .show_files_listing()\n                    .redirect_to_slash_directory(),\n            ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/tests\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::TEMPORARY_REDIRECT);\n\n        // should not redirect if the path is wrong\n        let req = TestRequest::with_uri(\"/not_existing\").to_request();\n        let resp = test::call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_static_files_bad_directory() {\n        let service = Files::new(\"/\", \"./missing\").new_service(()).await.unwrap();\n\n        let req = TestRequest::with_uri(\"/\").to_srv_request();\n        let resp = test::call_service(&service, req).await;\n\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_static_files_bad_directory_does_not_serve_cwd_files() {\n        let service = Files::new(\"/\", \"./missing\").new_service(()).await.unwrap();\n\n        let req = TestRequest::with_uri(\"/Cargo.toml\").to_srv_request();\n        let resp = test::call_service(&service, req).await;\n\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_default_handler_file_missing() {\n        let st = Files::new(\"/\", \".\")\n            .default_handler(|req: ServiceRequest| async {\n                Ok(req.into_response(HttpResponse::Ok().body(\"default content\")))\n            })\n            .new_service(())\n            .await\n            .unwrap();\n        let req = TestRequest::with_uri(\"/missing\").to_srv_request();\n        let resp = test::call_service(&st, req).await;\n\n        assert_eq!(resp.status(), StatusCode::OK);\n        let bytes = test::read_body(resp).await;\n        assert_eq!(bytes, web::Bytes::from_static(b\"default content\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_serve_index_nested() {\n        let service = Files::new(\".\", \".\")\n            .index_file(\"lib.rs\")\n            .new_service(())\n            .await\n            .unwrap();\n\n        let req = TestRequest::default().uri(\"/src\").to_srv_request();\n        let resp = test::call_service(&service, req).await;\n\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/x-rust\"\n        );\n        assert_eq!(\n            resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"lib.rs\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn integration_serve_index() {\n        let srv = test::init_service(\n            App::new().service(Files::new(\"test\", \".\").index_file(\"Cargo.toml\")),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let bytes = test::read_body(res).await;\n\n        let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n        assert_eq!(bytes, data);\n\n        let req = TestRequest::get().uri(\"/test/\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let bytes = test::read_body(res).await;\n        let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n        assert_eq!(bytes, data);\n\n        // nonexistent index file\n        let req = TestRequest::get().uri(\"/test/unknown\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::get().uri(\"/test/unknown/\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn integration_percent_encoded() {\n        let srv = test::init_service(\n            App::new().service(Files::new(\"test\", \".\").index_file(\"Cargo.toml\")),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test/%43argo.toml\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        // `%2F` == `/`\n        let req = TestRequest::get().uri(\"/test%2Ftest.binary\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::get().uri(\"/test/Cargo.toml%00\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_percent_encoding_2() {\n        let temp_dir = tempfile::tempdir().unwrap();\n        let filename = match cfg!(unix) {\n            true => \"ض:?#[]{}<>()@!$&'`|*+,;= %20\\n.test\",\n            false => \"ض#[]{}()@!$&'`+,;= %20.test\",\n        };\n        let filename_encoded = filename\n            .as_bytes()\n            .iter()\n            .fold(String::new(), |mut buf, c| {\n                write!(&mut buf, \"%{:02X}\", c).unwrap();\n                buf\n            });\n        std::fs::File::create(temp_dir.path().join(filename)).unwrap();\n\n        let srv = test::init_service(App::new().service(Files::new(\"/\", temp_dir.path()))).await;\n\n        let req = TestRequest::get()\n            .uri(&format!(\"/{}\", filename_encoded))\n            .to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_serve_named_file() {\n        let factory = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let srv = test::init_service(App::new().service(factory)).await;\n\n        let req = TestRequest::get().uri(\"/Cargo.toml\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let bytes = test::read_body(res).await;\n        let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n        assert_eq!(bytes, data);\n\n        let req = TestRequest::get().uri(\"/test/unknown\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_serve_named_file_prefix() {\n        let factory = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let srv =\n            test::init_service(App::new().service(web::scope(\"/test\").service(factory))).await;\n\n        let req = TestRequest::get().uri(\"/test/Cargo.toml\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let bytes = test::read_body(res).await;\n        let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n        assert_eq!(bytes, data);\n\n        let req = TestRequest::get().uri(\"/Cargo.toml\").to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_named_file_default_service() {\n        let factory = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let srv = test::init_service(App::new().default_service(factory)).await;\n\n        for route in [\"/foobar\", \"/baz\", \"/\"].iter() {\n            let req = TestRequest::get().uri(route).to_request();\n            let res = test::call_service(&srv, req).await;\n            assert_eq!(res.status(), StatusCode::OK);\n\n            let bytes = test::read_body(res).await;\n            let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n            assert_eq!(bytes, data);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_default_handler_named_file() {\n        let factory = NamedFile::open_async(\"Cargo.toml\").await.unwrap();\n        let st = Files::new(\"/\", \".\")\n            .default_handler(factory)\n            .new_service(())\n            .await\n            .unwrap();\n        let req = TestRequest::with_uri(\"/missing\").to_srv_request();\n        let resp = test::call_service(&st, req).await;\n\n        assert_eq!(resp.status(), StatusCode::OK);\n        let bytes = test::read_body(resp).await;\n        let data = Bytes::from(fs::read(\"Cargo.toml\").unwrap());\n        assert_eq!(bytes, data);\n    }\n\n    #[actix_rt::test]\n    async fn test_symlinks() {\n        let srv = test::init_service(App::new().service(Files::new(\"test\", \".\"))).await;\n\n        let req = TestRequest::get()\n            .uri(\"/test/tests/symlink-test.png\")\n            .to_request();\n        let res = test::call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(header::CONTENT_DISPOSITION).unwrap(),\n            \"inline; filename=\\\"symlink-test.png\\\"\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_index_with_show_files_listing() {\n        let service = Files::new(\".\", \".\")\n            .index_file(\"lib.rs\")\n            .show_files_listing()\n            .new_service(())\n            .await\n            .unwrap();\n\n        // Serve the index if exists\n        let req = TestRequest::default().uri(\"/src\").to_srv_request();\n        let resp = test::call_service(&service, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/x-rust\"\n        );\n\n        // Show files listing, otherwise.\n        let req = TestRequest::default().uri(\"/tests\").to_srv_request();\n        let resp = test::call_service(&service, req).await;\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            \"text/html; charset=utf-8\"\n        );\n        let bytes = test::read_body(resp).await;\n        assert!(format!(\"{:?}\", bytes).contains(\"/tests/test.png\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_path_filter() {\n        // prevent searching subdirectories\n        let st = Files::new(\"/\", \".\")\n            .path_filter(|path, _| path.components().count() == 1)\n            .new_service(())\n            .await\n            .unwrap();\n\n        let req = TestRequest::with_uri(\"/Cargo.toml\").to_srv_request();\n        let resp = test::call_service(&st, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/src/lib.rs\").to_srv_request();\n        let resp = test::call_service(&st, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_default_handler_filter() {\n        let st = Files::new(\"/\", \".\")\n            .default_handler(|req: ServiceRequest| async {\n                Ok(req.into_response(HttpResponse::Ok().body(\"default content\")))\n            })\n            .path_filter(|path, _| path.extension() == Some(\"png\".as_ref()))\n            .new_service(())\n            .await\n            .unwrap();\n        let req = TestRequest::with_uri(\"/Cargo.toml\").to_srv_request();\n        let resp = test::call_service(&st, req).await;\n\n        assert_eq!(resp.status(), StatusCode::OK);\n        let bytes = test::read_body(resp).await;\n        assert_eq!(bytes, web::Bytes::from_static(b\"default content\"));\n    }\n}\n"
  },
  {
    "path": "actix-files/src/named.rs",
    "content": "use std::{\n    fs::Metadata,\n    io,\n    path::{Path, PathBuf},\n    time::{SystemTime, UNIX_EPOCH},\n};\n\nuse actix_web::{\n    body::{self, BoxBody, SizedStream},\n    dev::{\n        self, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory, ServiceRequest,\n        ServiceResponse,\n    },\n    http::{\n        header::{\n            self, Charset, ContentDisposition, ContentEncoding, DispositionParam, DispositionType,\n            ExtendedValue,\n        },\n        StatusCode,\n    },\n    Error, HttpMessage, HttpRequest, HttpResponse, Responder,\n};\nuse bitflags::bitflags;\nuse derive_more::{Deref, DerefMut};\nuse futures_core::future::LocalBoxFuture;\nuse mime::Mime;\n\nuse crate::{encoding::equiv_utf8_text, range::HttpRange};\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    pub(crate) struct Flags: u8 {\n        const ETAG =                0b0000_0001;\n        const LAST_MD =             0b0000_0010;\n        const CONTENT_DISPOSITION = 0b0000_0100;\n        const PREFER_UTF8 =         0b0000_1000;\n    }\n}\n\nimpl Default for Flags {\n    fn default() -> Self {\n        Flags::from_bits_truncate(0b0000_1111)\n    }\n}\n\n/// A file with an associated name.\n///\n/// `NamedFile` can be registered as services:\n/// ```\n/// use actix_web::App;\n/// use actix_files::NamedFile;\n///\n/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {\n/// let file = NamedFile::open_async(\"./static/index.html\").await?;\n/// let app = App::new().service(file);\n/// # Ok(())\n/// # }\n/// ```\n///\n/// They can also be returned from handlers:\n/// ```\n/// use actix_web::{Responder, get};\n/// use actix_files::NamedFile;\n///\n/// #[get(\"/\")]\n/// async fn index() -> impl Responder {\n///     NamedFile::open_async(\"./static/index.html\").await\n/// }\n/// ```\n#[derive(Debug, Deref, DerefMut)]\npub struct NamedFile {\n    #[deref]\n    #[deref_mut]\n    file: File,\n    path: PathBuf,\n    modified: Option<SystemTime>,\n    pub(crate) md: Metadata,\n    pub(crate) flags: Flags,\n    pub(crate) status_code: StatusCode,\n    pub(crate) content_type: Mime,\n    pub(crate) content_disposition: ContentDisposition,\n    pub(crate) encoding: Option<ContentEncoding>,\n    pub(crate) read_mode_threshold: u64,\n}\n\n#[cfg(not(feature = \"experimental-io-uring\"))]\npub(crate) use std::fs::File;\n\n#[cfg(feature = \"experimental-io-uring\")]\npub(crate) use tokio_uring::fs::File;\n\nuse super::chunked;\n\npub(crate) fn get_content_type_and_disposition(\n    path: &Path,\n) -> Result<(mime::Mime, ContentDisposition), io::Error> {\n    let filename = match path.file_name() {\n        Some(name) => name.to_string_lossy(),\n        None => {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Provided path has no filename\",\n            ));\n        }\n    };\n\n    let ct = mime_guess::from_path(path).first_or_octet_stream();\n\n    let disposition = match ct.type_() {\n        mime::IMAGE | mime::TEXT | mime::AUDIO | mime::VIDEO => DispositionType::Inline,\n        mime::APPLICATION => match ct.subtype() {\n            mime::JAVASCRIPT | mime::JSON => DispositionType::Inline,\n            name if name == \"wasm\" || name == \"xhtml\" => DispositionType::Inline,\n            _ => DispositionType::Attachment,\n        },\n        _ => DispositionType::Attachment,\n    };\n\n    // replace special characters in filenames which could occur on some filesystems\n    let filename_s = filename\n        .replace('\\n', \"%0A\") // \\n line break\n        .replace('\\x0B', \"%0B\") // \\v vertical tab\n        .replace('\\x0C', \"%0C\") // \\f form feed\n        .replace('\\r', \"%0D\"); // \\r carriage return\n    let mut parameters = vec![DispositionParam::Filename(filename_s)];\n\n    if !filename.is_ascii() {\n        parameters.push(DispositionParam::FilenameExt(ExtendedValue {\n            charset: Charset::Ext(String::from(\"UTF-8\")),\n            language_tag: None,\n            value: filename.into_owned().into_bytes(),\n        }))\n    }\n\n    let cd = ContentDisposition {\n        disposition,\n        parameters,\n    };\n\n    Ok((ct, cd))\n}\n\nimpl NamedFile {\n    /// Creates an instance from a previously opened file.\n    ///\n    /// The given `path` need not exist and is only used to determine the `ContentType` and\n    /// `ContentDisposition` headers.\n    ///\n    /// # Examples\n    /// ```ignore\n    /// use std::{\n    ///     io::{self, Write as _},\n    ///     env,\n    ///     fs::File\n    /// };\n    /// use actix_files::NamedFile;\n    ///\n    /// let mut file = File::create(\"foo.txt\")?;\n    /// file.write_all(b\"Hello, world!\")?;\n    /// let named_file = NamedFile::from_file(file, \"bar.txt\")?;\n    /// # std::fs::remove_file(\"foo.txt\");\n    /// Ok(())\n    /// ```\n    pub fn from_file<P: AsRef<Path>>(file: File, path: P) -> io::Result<NamedFile> {\n        let path = path.as_ref().to_path_buf();\n\n        // Get the name of the file and use it to construct default Content-Type\n        // and Content-Disposition values\n        let (content_type, content_disposition) = get_content_type_and_disposition(&path)?;\n\n        let md = {\n            #[cfg(not(feature = \"experimental-io-uring\"))]\n            {\n                file.metadata()?\n            }\n\n            #[cfg(feature = \"experimental-io-uring\")]\n            {\n                use std::os::unix::prelude::{AsRawFd, FromRawFd};\n\n                let fd = file.as_raw_fd();\n\n                // SAFETY: fd is borrowed and lives longer than the unsafe block\n                unsafe {\n                    let file = std::fs::File::from_raw_fd(fd);\n                    let md = file.metadata();\n                    // SAFETY: forget the fd before exiting block in success or error case but don't\n                    // run destructor (that would close file handle)\n                    std::mem::forget(file);\n                    md?\n                }\n            }\n        };\n\n        let modified = md.modified().ok();\n        let encoding = None;\n\n        Ok(NamedFile {\n            path,\n            file,\n            content_type,\n            content_disposition,\n            md,\n            modified,\n            encoding,\n            status_code: StatusCode::OK,\n            flags: Flags::default(),\n            read_mode_threshold: 0,\n        })\n    }\n\n    /// Attempts to open a file in read-only mode.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_files::NamedFile;\n    /// let file = NamedFile::open(\"foo.txt\");\n    /// ```\n    #[cfg(not(feature = \"experimental-io-uring\"))]\n    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {\n        let file = File::open(&path)?;\n        Self::from_file(file, path)\n    }\n\n    /// Attempts to open a file asynchronously in read-only mode.\n    ///\n    /// When the `experimental-io-uring` crate feature is enabled, this will be async. Otherwise, it\n    /// will behave just like `open`.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_files::NamedFile;\n    /// # async fn open() {\n    /// let file = NamedFile::open_async(\"foo.txt\").await.unwrap();\n    /// # }\n    /// ```\n    pub async fn open_async<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {\n        let file = {\n            #[cfg(not(feature = \"experimental-io-uring\"))]\n            {\n                File::open(&path)?\n            }\n\n            #[cfg(feature = \"experimental-io-uring\")]\n            {\n                File::open(&path).await?\n            }\n        };\n\n        Self::from_file(file, path)\n    }\n\n    /// Returns reference to the underlying file object.\n    #[inline]\n    pub fn file(&self) -> &File {\n        &self.file\n    }\n\n    /// Returns the filesystem path to this file.\n    ///\n    /// # Examples\n    /// ```\n    /// # use std::io;\n    /// use actix_files::NamedFile;\n    ///\n    /// # async fn path() -> io::Result<()> {\n    /// let file = NamedFile::open_async(\"test.txt\").await?;\n    /// assert_eq!(file.path().as_os_str(), \"foo.txt\");\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn path(&self) -> &Path {\n        self.path.as_path()\n    }\n\n    /// Returns the time the file was last modified.\n    ///\n    /// Returns `None` only on unsupported platforms; see [`std::fs::Metadata::modified()`].\n    /// Therefore, it is usually safe to unwrap this.\n    #[inline]\n    pub fn modified(&self) -> Option<SystemTime> {\n        self.modified\n    }\n\n    /// Returns the filesystem metadata associated with this file.\n    #[inline]\n    pub fn metadata(&self) -> &Metadata {\n        &self.md\n    }\n\n    /// Returns the `Content-Type` header that will be used when serving this file.\n    #[inline]\n    pub fn content_type(&self) -> &Mime {\n        &self.content_type\n    }\n\n    /// Returns the `Content-Disposition` that will be used when serving this file.\n    #[inline]\n    pub fn content_disposition(&self) -> &ContentDisposition {\n        &self.content_disposition\n    }\n\n    /// Returns the `Content-Encoding` that will be used when serving this file.\n    ///\n    /// A return value of `None` indicates that the content is not already using a compressed\n    /// representation and may be subject to compression downstream.\n    #[inline]\n    pub fn content_encoding(&self) -> Option<ContentEncoding> {\n        self.encoding\n    }\n\n    /// Set response status code.\n    #[deprecated(since = \"0.7.0\", note = \"Prefer `Responder::customize()`.\")]\n    pub fn set_status_code(mut self, status: StatusCode) -> Self {\n        self.status_code = status;\n        self\n    }\n\n    /// Sets the `Content-Type` header that will be used when serving this file. By default the\n    /// `Content-Type` is inferred from the filename extension.\n    #[inline]\n    pub fn set_content_type(mut self, mime_type: Mime) -> Self {\n        self.content_type = mime_type;\n        self\n    }\n\n    /// Set the Content-Disposition for serving this file. This allows changing the\n    /// `inline/attachment` disposition as well as the filename sent to the peer.\n    ///\n    /// By default the disposition is `inline` for `text/*`, `image/*`, `video/*` and\n    /// `application/{javascript, json, wasm}` mime types, and `attachment` otherwise, and the\n    /// filename is taken from the path provided in the `open` method after converting it to UTF-8\n    /// (using `to_string_lossy`).\n    #[inline]\n    pub fn set_content_disposition(mut self, cd: ContentDisposition) -> Self {\n        self.content_disposition = cd;\n        self.flags.insert(Flags::CONTENT_DISPOSITION);\n        self\n    }\n\n    /// Disables `Content-Disposition` header.\n    ///\n    /// By default, the `Content-Disposition` header is sent.\n    #[inline]\n    pub fn disable_content_disposition(mut self) -> Self {\n        self.flags.remove(Flags::CONTENT_DISPOSITION);\n        self\n    }\n\n    /// Sets content encoding for this file.\n    ///\n    /// This prevents the `Compress` middleware from modifying the file contents and signals to\n    /// browsers/clients how to decode it. For example, if serving a compressed HTML file (e.g.,\n    /// `index.html.gz`) then use `.set_content_encoding(ContentEncoding::Gzip)`.\n    #[inline]\n    pub fn set_content_encoding(mut self, enc: ContentEncoding) -> Self {\n        self.encoding = Some(enc);\n        self\n    }\n\n    /// Sets the size threshold that determines file read mode (sync/async).\n    ///\n    /// When a file is smaller than the threshold (bytes), the reader will use synchronous\n    /// (blocking) file reads. For larger files, it switches to async reads to avoid blocking the\n    /// main thread.\n    ///\n    /// Tweaking this value according to your expected usage may lead to significant performance\n    /// gains (or losses in other handlers, if `size` is too high).\n    ///\n    /// When the `experimental-io-uring` crate feature is enabled, file reads are always async.\n    ///\n    /// Default is 0, meaning all files are read asynchronously.\n    pub fn read_mode_threshold(mut self, size: u64) -> Self {\n        self.read_mode_threshold = size;\n        self\n    }\n\n    /// Specifies whether to return `ETag` header in response.\n    ///\n    /// Default is true.\n    #[inline]\n    pub fn use_etag(mut self, value: bool) -> Self {\n        self.flags.set(Flags::ETAG, value);\n        self\n    }\n\n    /// Specifies whether to return `Last-Modified` header in response.\n    ///\n    /// Default is true.\n    #[inline]\n    pub fn use_last_modified(mut self, value: bool) -> Self {\n        self.flags.set(Flags::LAST_MD, value);\n        self\n    }\n\n    /// Specifies whether text responses should signal a UTF-8 encoding.\n    ///\n    /// Default is false (but will default to true in a future version).\n    #[inline]\n    pub fn prefer_utf8(mut self, value: bool) -> Self {\n        self.flags.set(Flags::PREFER_UTF8, value);\n        self\n    }\n\n    /// Creates an `ETag` in a format is similar to Apache's.\n    pub(crate) fn etag(&self) -> Option<header::EntityTag> {\n        let mtime = self.modified?;\n\n        Some({\n            let ino = {\n                #[cfg(unix)]\n                {\n                    #[cfg(unix)]\n                    use std::os::unix::fs::MetadataExt as _;\n\n                    self.md.ino()\n                }\n\n                #[cfg(not(unix))]\n                {\n                    0\n                }\n            };\n\n            // Don't panic for pre-epoch modification times. Encode the timestamp as seconds and\n            // sub-second nanoseconds relative to the UNIX epoch, allowing negative values.\n            let (secs, nanos) = match mtime.duration_since(UNIX_EPOCH) {\n                Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),\n                Err(err) => {\n                    let dur = err.duration();\n\n                    // For timestamps before the epoch, represent the time as a negative seconds\n                    // offset with positive nanoseconds (like POSIX timespec).\n                    if dur.subsec_nanos() == 0 {\n                        (-(dur.as_secs() as i64), 0)\n                    } else {\n                        (\n                            -(dur.as_secs() as i64) - 1,\n                            1_000_000_000 - dur.subsec_nanos(),\n                        )\n                    }\n                }\n            };\n\n            header::EntityTag::new_strong(format!(\n                \"{:x}:{:x}:{:x}:{:x}\",\n                ino,\n                self.md.len(),\n                secs as u64,\n                nanos\n            ))\n        })\n    }\n\n    pub(crate) fn last_modified(&self) -> Option<header::HttpDate> {\n        let mtime = self.modified?;\n\n        // avoid panic in `httpdate` crate when formatting as an HTTP date\n        // see: https://github.com/actix/actix-web/issues/2748\n        //\n        // httpdate supports dates in range [1970, 9999); see:\n        // https://github.com/seanmonstar/httpdate/blob/v1.0.3/src/date.rs\n        let dur = mtime.duration_since(UNIX_EPOCH).ok()?;\n        if dur.as_secs() >= 253_402_300_800 {\n            return None;\n        }\n\n        Some(mtime.into())\n    }\n\n    /// Creates an `HttpResponse` with file as a streaming body.\n    pub fn into_response(self, req: &HttpRequest) -> HttpResponse<BoxBody> {\n        if self.status_code != StatusCode::OK {\n            let mut res = HttpResponse::build(self.status_code);\n\n            let ct = if self.flags.contains(Flags::PREFER_UTF8) {\n                equiv_utf8_text(self.content_type.clone())\n            } else {\n                self.content_type\n            };\n\n            res.insert_header((header::CONTENT_TYPE, ct.to_string()));\n\n            if self.flags.contains(Flags::CONTENT_DISPOSITION) {\n                res.insert_header((\n                    header::CONTENT_DISPOSITION,\n                    self.content_disposition.to_string(),\n                ));\n            }\n\n            if let Some(current_encoding) = self.encoding {\n                res.insert_header((header::CONTENT_ENCODING, current_encoding.as_str()));\n            }\n\n            let reader =\n                chunked::new_chunked_read(self.md.len(), 0, self.file, self.read_mode_threshold);\n\n            return res.streaming(reader);\n        }\n\n        let etag = if self.flags.contains(Flags::ETAG) {\n            self.etag()\n        } else {\n            None\n        };\n\n        let last_modified = if self.flags.contains(Flags::LAST_MD) {\n            self.last_modified()\n        } else {\n            None\n        };\n\n        // check preconditions\n        let precondition_failed = if !any_match(etag.as_ref(), req) {\n            true\n        } else if let (Some(ref m), Some(header::IfUnmodifiedSince(ref since))) =\n            (last_modified, req.get_header())\n        {\n            let t1: SystemTime = (*m).into();\n            let t2: SystemTime = (*since).into();\n\n            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {\n                (Ok(t1), Ok(t2)) => t1.as_secs() > t2.as_secs(),\n                _ => false,\n            }\n        } else {\n            false\n        };\n\n        // check last modified\n        let not_modified = if !none_match(etag.as_ref(), req) {\n            true\n        } else if req.headers().contains_key(header::IF_NONE_MATCH) {\n            false\n        } else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =\n            (last_modified, req.get_header())\n        {\n            let t1: SystemTime = (*m).into();\n            let t2: SystemTime = (*since).into();\n\n            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {\n                (Ok(t1), Ok(t2)) => t1.as_secs() <= t2.as_secs(),\n                _ => false,\n            }\n        } else {\n            false\n        };\n\n        let mut res = HttpResponse::build(self.status_code);\n\n        let ct = if self.flags.contains(Flags::PREFER_UTF8) {\n            equiv_utf8_text(self.content_type.clone())\n        } else {\n            self.content_type\n        };\n\n        res.insert_header((header::CONTENT_TYPE, ct.to_string()));\n\n        if self.flags.contains(Flags::CONTENT_DISPOSITION) {\n            res.insert_header((\n                header::CONTENT_DISPOSITION,\n                self.content_disposition.to_string(),\n            ));\n        }\n\n        if let Some(current_encoding) = self.encoding {\n            res.insert_header((header::CONTENT_ENCODING, current_encoding.as_str()));\n        }\n\n        if let Some(lm) = last_modified {\n            res.insert_header((header::LAST_MODIFIED, lm.to_string()));\n        }\n\n        if let Some(etag) = etag {\n            res.insert_header((header::ETAG, etag.to_string()));\n        }\n\n        res.insert_header((header::ACCEPT_RANGES, \"bytes\"));\n\n        let mut length = self.md.len();\n        let mut offset = 0;\n        let mut ranged_req = false;\n\n        // check for range header\n        if let Some(ranges) = req.headers().get(header::RANGE) {\n            if let Ok(ranges_header) = ranges.to_str() {\n                if let Some(range) = HttpRange::parse(ranges_header, length)\n                    .ok()\n                    .and_then(|ranges| ranges.first().copied())\n                {\n                    ranged_req = true;\n                    length = range.length;\n                    offset = range.start;\n\n                    res.insert_header((\n                        header::CONTENT_RANGE,\n                        format!(\"bytes {}-{}/{}\", offset, offset + length - 1, self.md.len()),\n                    ));\n                } else {\n                    res.insert_header((header::CONTENT_RANGE, format!(\"bytes */{}\", length)));\n                    return res.status(StatusCode::RANGE_NOT_SATISFIABLE).finish();\n                };\n            } else {\n                return res.status(StatusCode::BAD_REQUEST).finish();\n            };\n        };\n\n        if precondition_failed {\n            return res.status(StatusCode::PRECONDITION_FAILED).finish();\n        } else if not_modified {\n            return res\n                .status(StatusCode::NOT_MODIFIED)\n                .body(body::None::new())\n                .map_into_boxed_body();\n        }\n\n        let reader = chunked::new_chunked_read(length, offset, self.file, self.read_mode_threshold);\n\n        if ranged_req {\n            res.status(StatusCode::PARTIAL_CONTENT);\n        }\n\n        res.body(SizedStream::new(length, reader))\n    }\n}\n\n/// Returns true if `req` has no `If-Match` header or one which matches `etag`.\nfn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {\n    match req.get_header::<header::IfMatch>() {\n        None | Some(header::IfMatch::Any) => true,\n\n        Some(header::IfMatch::Items(ref items)) => {\n            if let Some(some_etag) = etag {\n                for item in items {\n                    if item.strong_eq(some_etag) {\n                        return true;\n                    }\n                }\n            }\n\n            false\n        }\n    }\n}\n\n/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`.\nfn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {\n    match req.get_header::<header::IfNoneMatch>() {\n        Some(header::IfNoneMatch::Any) => false,\n\n        Some(header::IfNoneMatch::Items(ref items)) => {\n            if let Some(some_etag) = etag {\n                for item in items {\n                    if item.weak_eq(some_etag) {\n                        return false;\n                    }\n                }\n            }\n\n            true\n        }\n\n        None => true,\n    }\n}\n\nimpl Responder for NamedFile {\n    type Body = BoxBody;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        self.into_response(req)\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for NamedFile {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = NamedFileService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let service = NamedFileService {\n            path: self.path.clone(),\n        };\n\n        Box::pin(async move { Ok(service) })\n    }\n}\n\n#[doc(hidden)]\n#[derive(Debug)]\npub struct NamedFileService {\n    path: PathBuf,\n}\n\nimpl Service<ServiceRequest> for NamedFileService {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    dev::always_ready!();\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let (req, _) = req.into_parts();\n\n        let path = self.path.clone();\n        Box::pin(async move {\n            let file = NamedFile::open_async(path).await?;\n            let res = file.into_response(&req);\n            Ok(ServiceResponse::new(req, res))\n        })\n    }\n}\n\nimpl HttpServiceFactory for NamedFile {\n    fn register(self, config: &mut AppService) {\n        config.register_service(\n            ResourceDef::root_prefix(self.path.to_string_lossy().as_ref()),\n            None,\n            self,\n            None,\n        )\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn audio_files_use_inline_content_disposition() {\n        let (_ct, cd) = get_content_type_and_disposition(Path::new(\"sound.mp3\")).unwrap();\n        assert_eq!(cd.disposition, DispositionType::Inline);\n    }\n}\n"
  },
  {
    "path": "actix-files/src/path_buf.rs",
    "content": "use std::{\n    path::{Component, Path, PathBuf},\n    str::FromStr,\n};\n\nuse actix_utils::future::{ready, Ready};\nuse actix_web::{dev::Payload, FromRequest, HttpRequest};\n\nuse crate::error::UriSegmentError;\n\n/// Secure Path Traversal Guard\n///\n/// This struct parses a request-uri [`PathBuf`](std::path::PathBuf)\n#[derive(Debug, PartialEq, Eq)]\npub struct PathBufWrap(PathBuf);\n\nimpl FromStr for PathBufWrap {\n    type Err = UriSegmentError;\n\n    fn from_str(path: &str) -> Result<Self, Self::Err> {\n        Self::parse_path(path, false)\n    }\n}\n\nimpl PathBufWrap {\n    /// Parse a safe path from the unprocessed tail of a supplied\n    /// [`HttpRequest`](actix_web::HttpRequest), given the choice of allowing hidden files to be\n    /// considered valid segments.\n    ///\n    /// This uses [`HttpRequest::match_info`](actix_web::HttpRequest::match_info) and\n    /// [`Path::unprocessed`](actix_web::dev::Path::unprocessed), which returns the part of the\n    /// path not matched by route patterns. This is useful for mounted services (eg. `Files`),\n    /// where only the tail should be parsed.\n    ///\n    /// Path traversal is guarded by this method.\n    #[inline]\n    pub fn parse_unprocessed_req(\n        req: &HttpRequest,\n        hidden_files: bool,\n    ) -> Result<Self, UriSegmentError> {\n        Self::parse_path(req.match_info().unprocessed(), hidden_files)\n    }\n\n    /// Parse a safe path from the full request path of a supplied\n    /// [`HttpRequest`](actix_web::HttpRequest), given the choice of allowing hidden files to be\n    /// considered valid segments.\n    ///\n    /// This uses [`HttpRequest::path`](actix_web::HttpRequest::path), and is more appropriate\n    /// for non-mounted handlers that want the entire request path.\n    ///\n    /// Path traversal is guarded by this method.\n    #[inline]\n    pub fn parse_req_path(req: &HttpRequest, hidden_files: bool) -> Result<Self, UriSegmentError> {\n        Self::parse_path(req.path(), hidden_files)\n    }\n\n    /// Parse a path, giving the choice of allowing hidden files to be considered valid segments.\n    ///\n    /// Path traversal is guarded by this method.\n    pub fn parse_path(path: &str, hidden_files: bool) -> Result<Self, UriSegmentError> {\n        let mut buf = PathBuf::new();\n\n        // equivalent to `path.split('/').count()`\n        let mut segment_count = path.matches('/').count() + 1;\n\n        // we can decode the whole path here (instead of per-segment decoding)\n        // because we will reject `%2F` in paths using `segment_count`.\n        let path = percent_encoding::percent_decode_str(path)\n            .decode_utf8()\n            .map_err(|_| UriSegmentError::NotValidUtf8)?;\n\n        // disallow decoding `%2F` into `/`\n        if segment_count != path.matches('/').count() + 1 {\n            return Err(UriSegmentError::BadChar('/'));\n        }\n\n        for segment in path.split('/') {\n            if segment == \"..\" {\n                segment_count -= 1;\n                buf.pop();\n            } else if !hidden_files && segment.starts_with('.') {\n                return Err(UriSegmentError::BadStart('.'));\n            } else if segment.starts_with('*') {\n                return Err(UriSegmentError::BadStart('*'));\n            } else if segment.ends_with(':') {\n                return Err(UriSegmentError::BadEnd(':'));\n            } else if segment.ends_with('>') {\n                return Err(UriSegmentError::BadEnd('>'));\n            } else if segment.ends_with('<') {\n                return Err(UriSegmentError::BadEnd('<'));\n            } else if segment.is_empty() {\n                segment_count -= 1;\n                continue;\n            } else if cfg!(windows) && segment.contains('\\\\') {\n                return Err(UriSegmentError::BadChar('\\\\'));\n            } else if cfg!(windows) && segment.contains(':') {\n                return Err(UriSegmentError::BadChar(':'));\n            } else {\n                buf.push(segment)\n            }\n        }\n\n        // make sure we agree with stdlib parser\n        for (i, component) in buf.components().enumerate() {\n            assert!(\n                matches!(component, Component::Normal(_)),\n                \"component `{:?}` is not normal\",\n                component\n            );\n            assert!(i < segment_count);\n        }\n\n        Ok(PathBufWrap(buf))\n    }\n}\n\nimpl AsRef<Path> for PathBufWrap {\n    fn as_ref(&self) -> &Path {\n        self.0.as_ref()\n    }\n}\n\nimpl FromRequest for PathBufWrap {\n    type Error = UriSegmentError;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        // Uses the unprocessed tail of the request path and disallows hidden files.\n        ready(req.match_info().unprocessed().parse())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_path_buf() {\n        assert_eq!(\n            PathBufWrap::from_str(\"/test/.tt\").map(|t| t.0),\n            Err(UriSegmentError::BadStart('.'))\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/test/*tt\").map(|t| t.0),\n            Err(UriSegmentError::BadStart('*'))\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/test/tt:\").map(|t| t.0),\n            Err(UriSegmentError::BadEnd(':'))\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/test/tt<\").map(|t| t.0),\n            Err(UriSegmentError::BadEnd('<'))\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/test/tt>\").map(|t| t.0),\n            Err(UriSegmentError::BadEnd('>'))\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/seg1/seg2/\").unwrap().0,\n            PathBuf::from_iter(vec![\"seg1\", \"seg2\"])\n        );\n        assert_eq!(\n            PathBufWrap::from_str(\"/seg1/../seg2/\").unwrap().0,\n            PathBuf::from_iter(vec![\"seg2\"])\n        );\n    }\n\n    #[test]\n    fn test_parse_path() {\n        assert_eq!(\n            PathBufWrap::parse_path(\"/test/.tt\", false).map(|t| t.0),\n            Err(UriSegmentError::BadStart('.'))\n        );\n\n        assert_eq!(\n            PathBufWrap::parse_path(\"/test/.tt\", true).unwrap().0,\n            PathBuf::from_iter(vec![\"test\", \".tt\"])\n        );\n    }\n\n    #[test]\n    fn path_traversal() {\n        assert_eq!(\n            PathBufWrap::parse_path(\"/../README.md\", false).unwrap().0,\n            PathBuf::from_iter(vec![\"README.md\"])\n        );\n\n        assert_eq!(\n            PathBufWrap::parse_path(\"/../README.md\", true).unwrap().0,\n            PathBuf::from_iter(vec![\"README.md\"])\n        );\n\n        assert_eq!(\n            PathBufWrap::parse_path(\"/../../../../../../../../../../etc/passwd\", false)\n                .unwrap()\n                .0,\n            PathBuf::from_iter(vec![\"etc/passwd\"])\n        );\n    }\n\n    #[test]\n    #[cfg_attr(windows, should_panic)]\n    fn windows_drive_traversal() {\n        // detect issues in windows that could lead to path traversal\n        // see <https://github.com/SergioBenitez/Rocket/issues/1949\n\n        assert_eq!(\n            PathBufWrap::parse_path(\"C:test.txt\", false).unwrap().0,\n            PathBuf::from_iter(vec![\"C:test.txt\"])\n        );\n\n        assert_eq!(\n            PathBufWrap::parse_path(\"C:../whatever\", false).unwrap().0,\n            PathBuf::from_iter(vec![\"C:../whatever\"])\n        );\n\n        assert_eq!(\n            PathBufWrap::parse_path(\":test.txt\", false).unwrap().0,\n            PathBuf::from_iter(vec![\":test.txt\"])\n        );\n    }\n}\n"
  },
  {
    "path": "actix-files/src/range.rs",
    "content": "use std::fmt;\n\nuse derive_more::Error;\n\n/// Copy of `http_range::HttpRangeParseError`.\n#[derive(Debug, Clone)]\nenum HttpRangeParseError {\n    InvalidRange,\n    NoOverlap,\n}\n\nimpl From<http_range::HttpRangeParseError> for HttpRangeParseError {\n    fn from(err: http_range::HttpRangeParseError) -> Self {\n        match err {\n            http_range::HttpRangeParseError::InvalidRange => Self::InvalidRange,\n            http_range::HttpRangeParseError::NoOverlap => Self::NoOverlap,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Error)]\n#[non_exhaustive]\npub struct ParseRangeErr(#[error(not(source))] HttpRangeParseError);\n\nimpl fmt::Display for ParseRangeErr {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"invalid Range header: \")?;\n        f.write_str(match self.0 {\n            HttpRangeParseError::InvalidRange => \"invalid syntax\",\n            HttpRangeParseError::NoOverlap => \"range starts after end of content\",\n        })\n    }\n}\n\n/// HTTP Range header representation.\n#[derive(Debug, Clone, Copy)]\npub struct HttpRange {\n    /// Start of range.\n    pub start: u64,\n\n    /// Length of range.\n    pub length: u64,\n}\n\nimpl HttpRange {\n    /// Parses Range HTTP header string as per RFC 2616.\n    ///\n    /// `header` is HTTP Range header (e.g. `bytes=0-9`).\n    /// `size` is full size of response (file).\n    pub fn parse(header: &str, size: u64) -> Result<Vec<HttpRange>, ParseRangeErr> {\n        let ranges =\n            http_range::HttpRange::parse(header, size).map_err(|err| ParseRangeErr(err.into()))?;\n\n        Ok(ranges\n            .iter()\n            .map(|range| HttpRange {\n                start: range.start,\n                length: range.length,\n            })\n            .collect())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    struct T(&'static str, u64, Vec<HttpRange>);\n\n    #[test]\n    fn test_parse() {\n        let tests = vec![\n            T(\"\", 0, vec![]),\n            T(\"\", 1000, vec![]),\n            T(\"foo\", 0, vec![]),\n            T(\"bytes=\", 0, vec![]),\n            T(\"bytes=7\", 10, vec![]),\n            T(\"bytes= 7 \", 10, vec![]),\n            T(\"bytes=1-\", 0, vec![]),\n            T(\"bytes=5-4\", 10, vec![]),\n            T(\"bytes=0-2,5-4\", 10, vec![]),\n            T(\"bytes=2-5,4-3\", 10, vec![]),\n            T(\"bytes=--5,4--3\", 10, vec![]),\n            T(\"bytes=A-\", 10, vec![]),\n            T(\"bytes=A- \", 10, vec![]),\n            T(\"bytes=A-Z\", 10, vec![]),\n            T(\"bytes= -Z\", 10, vec![]),\n            T(\"bytes=5-Z\", 10, vec![]),\n            T(\"bytes=Ran-dom, garbage\", 10, vec![]),\n            T(\"bytes=0x01-0x02\", 10, vec![]),\n            T(\"bytes=         \", 10, vec![]),\n            T(\"bytes= , , ,   \", 10, vec![]),\n            T(\n                \"bytes=0-9\",\n                10,\n                vec![HttpRange {\n                    start: 0,\n                    length: 10,\n                }],\n            ),\n            T(\n                \"bytes=0-\",\n                10,\n                vec![HttpRange {\n                    start: 0,\n                    length: 10,\n                }],\n            ),\n            T(\n                \"bytes=5-\",\n                10,\n                vec![HttpRange {\n                    start: 5,\n                    length: 5,\n                }],\n            ),\n            T(\n                \"bytes=0-20\",\n                10,\n                vec![HttpRange {\n                    start: 0,\n                    length: 10,\n                }],\n            ),\n            T(\n                \"bytes=15-,0-5\",\n                10,\n                vec![HttpRange {\n                    start: 0,\n                    length: 6,\n                }],\n            ),\n            T(\n                \"bytes=1-2,5-\",\n                10,\n                vec![\n                    HttpRange {\n                        start: 1,\n                        length: 2,\n                    },\n                    HttpRange {\n                        start: 5,\n                        length: 5,\n                    },\n                ],\n            ),\n            T(\n                \"bytes=-2 , 7-\",\n                11,\n                vec![\n                    HttpRange {\n                        start: 9,\n                        length: 2,\n                    },\n                    HttpRange {\n                        start: 7,\n                        length: 4,\n                    },\n                ],\n            ),\n            T(\n                \"bytes=0-0 ,2-2, 7-\",\n                11,\n                vec![\n                    HttpRange {\n                        start: 0,\n                        length: 1,\n                    },\n                    HttpRange {\n                        start: 2,\n                        length: 1,\n                    },\n                    HttpRange {\n                        start: 7,\n                        length: 4,\n                    },\n                ],\n            ),\n            T(\n                \"bytes=-5\",\n                10,\n                vec![HttpRange {\n                    start: 5,\n                    length: 5,\n                }],\n            ),\n            T(\n                \"bytes=-15\",\n                10,\n                vec![HttpRange {\n                    start: 0,\n                    length: 10,\n                }],\n            ),\n            T(\n                \"bytes=0-499\",\n                10000,\n                vec![HttpRange {\n                    start: 0,\n                    length: 500,\n                }],\n            ),\n            T(\n                \"bytes=500-999\",\n                10000,\n                vec![HttpRange {\n                    start: 500,\n                    length: 500,\n                }],\n            ),\n            T(\n                \"bytes=-500\",\n                10000,\n                vec![HttpRange {\n                    start: 9500,\n                    length: 500,\n                }],\n            ),\n            T(\n                \"bytes=9500-\",\n                10000,\n                vec![HttpRange {\n                    start: 9500,\n                    length: 500,\n                }],\n            ),\n            T(\n                \"bytes=0-0,-1\",\n                10000,\n                vec![\n                    HttpRange {\n                        start: 0,\n                        length: 1,\n                    },\n                    HttpRange {\n                        start: 9999,\n                        length: 1,\n                    },\n                ],\n            ),\n            T(\n                \"bytes=500-600,601-999\",\n                10000,\n                vec![\n                    HttpRange {\n                        start: 500,\n                        length: 101,\n                    },\n                    HttpRange {\n                        start: 601,\n                        length: 399,\n                    },\n                ],\n            ),\n            T(\n                \"bytes=500-700,601-999\",\n                10000,\n                vec![\n                    HttpRange {\n                        start: 500,\n                        length: 201,\n                    },\n                    HttpRange {\n                        start: 601,\n                        length: 399,\n                    },\n                ],\n            ),\n            // Match Apache laxity:\n            T(\n                \"bytes=   1 -2   ,  4- 5, 7 - 8 , ,,\",\n                11,\n                vec![\n                    HttpRange {\n                        start: 1,\n                        length: 2,\n                    },\n                    HttpRange {\n                        start: 4,\n                        length: 2,\n                    },\n                    HttpRange {\n                        start: 7,\n                        length: 2,\n                    },\n                ],\n            ),\n        ];\n\n        for t in tests {\n            let header = t.0;\n            let size = t.1;\n            let expected = t.2;\n\n            let res = HttpRange::parse(header, size);\n\n            if let Err(err) = res {\n                if expected.is_empty() {\n                    continue;\n                } else {\n                    panic!(\"parse({header}, {size}) returned error {err:?}\");\n                }\n            }\n\n            let got = res.unwrap();\n\n            if got.len() != expected.len() {\n                panic!(\n                    \"len(parseRange({}, {})) = {}, want {}\",\n                    header,\n                    size,\n                    got.len(),\n                    expected.len()\n                );\n            }\n\n            for i in 0..expected.len() {\n                if got[i].start != expected[i].start {\n                    panic!(\n                        \"parseRange({}, {})[{}].start = {}, want {}\",\n                        header, size, i, got[i].start, expected[i].start\n                    )\n                }\n                if got[i].length != expected[i].length {\n                    panic!(\n                        \"parseRange({}, {})[{}].length = {}, want {}\",\n                        header, size, i, got[i].length, expected[i].length\n                    )\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-files/src/service.rs",
    "content": "use std::{\n    fmt, io,\n    ops::Deref,\n    path::{Path, PathBuf},\n    rc::Rc,\n};\n\nuse actix_web::{\n    body::BoxBody,\n    dev::{self, Service, ServiceRequest, ServiceResponse},\n    error::Error,\n    guard::Guard,\n    http::{header, Method},\n    HttpResponse,\n};\nuse futures_core::future::LocalBoxFuture;\n\nuse crate::{\n    named, Directory, DirectoryRenderer, FilesError, HttpService, MimeOverride, NamedFile,\n    PathBufWrap, PathFilter,\n};\n\n/// Assembled file serving service.\n#[derive(Clone)]\npub struct FilesService(pub(crate) Rc<FilesServiceInner>);\n\nimpl Deref for FilesService {\n    type Target = FilesServiceInner;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\npub struct FilesServiceInner {\n    pub(crate) directory: PathBuf,\n    pub(crate) index: Option<String>,\n    pub(crate) show_index: bool,\n    pub(crate) redirect_to_slash: bool,\n    pub(crate) default: Option<HttpService>,\n    pub(crate) renderer: Rc<DirectoryRenderer>,\n    pub(crate) mime_override: Option<Rc<MimeOverride>>,\n    pub(crate) path_filter: Option<Rc<PathFilter>>,\n    pub(crate) file_flags: named::Flags,\n    pub(crate) guards: Option<Rc<dyn Guard>>,\n    pub(crate) hidden_files: bool,\n    pub(crate) try_compressed: bool,\n    pub(crate) size_threshold: u64,\n    pub(crate) with_permanent_redirect: bool,\n}\n\nimpl fmt::Debug for FilesServiceInner {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"FilesServiceInner\")\n    }\n}\n\nimpl FilesService {\n    async fn handle_err(\n        &self,\n        err: io::Error,\n        req: ServiceRequest,\n    ) -> Result<ServiceResponse, Error> {\n        log::debug!(\"error handling {}: {}\", req.path(), err);\n\n        if let Some(ref default) = self.default {\n            default.call(req).await\n        } else {\n            Ok(req.error_response(err))\n        }\n    }\n\n    fn serve_named_file_with_encoding(\n        &self,\n        req: ServiceRequest,\n        mut named_file: NamedFile,\n        encoding: header::ContentEncoding,\n    ) -> ServiceResponse {\n        if let Some(ref mime_override) = self.mime_override {\n            let new_disposition = mime_override(&named_file.content_type.type_());\n            named_file.content_disposition.disposition = new_disposition;\n        }\n        named_file.flags = self.file_flags;\n\n        let (req, _) = req.into_parts();\n        let mut res = named_file\n            .read_mode_threshold(self.size_threshold)\n            .into_response(&req);\n\n        let header_value = match encoding {\n            header::ContentEncoding::Brotli => Some(\"br\"),\n            header::ContentEncoding::Gzip => Some(\"gzip\"),\n            header::ContentEncoding::Zstd => Some(\"zstd\"),\n            header::ContentEncoding::Identity => None,\n            // Only variants in SUPPORTED_PRECOMPRESSION_ENCODINGS can occur here\n            _ => unreachable!(),\n        };\n        if let Some(header_value) = header_value {\n            res.headers_mut().insert(\n                header::CONTENT_ENCODING,\n                header::HeaderValue::from_static(header_value),\n            );\n            // Response representation varies by Accept-Encoding when serving pre-compressed assets.\n            res.headers_mut().append(\n                header::VARY,\n                header::HeaderValue::from_static(\"accept-encoding\"),\n            );\n        }\n        ServiceResponse::new(req, res)\n    }\n\n    fn serve_named_file(&self, req: ServiceRequest, named_file: NamedFile) -> ServiceResponse {\n        self.serve_named_file_with_encoding(req, named_file, header::ContentEncoding::Identity)\n    }\n\n    fn show_index(&self, req: ServiceRequest, path: PathBuf) -> ServiceResponse {\n        let dir = Directory::new(self.directory.clone(), path);\n\n        let (req, _) = req.into_parts();\n\n        (self.renderer)(&dir, &req).unwrap_or_else(|err| ServiceResponse::from_err(err, req))\n    }\n}\n\nimpl fmt::Debug for FilesService {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"FilesService\")\n    }\n}\n\nimpl Service<ServiceRequest> for FilesService {\n    type Response = ServiceResponse<BoxBody>;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    dev::always_ready!();\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let is_method_valid = if let Some(guard) = &self.guards {\n            // execute user defined guards\n            (**guard).check(&req.guard_ctx())\n        } else {\n            // default behavior\n            matches!(*req.method(), Method::HEAD | Method::GET)\n        };\n\n        let this = self.clone();\n\n        Box::pin(async move {\n            if !is_method_valid {\n                return Ok(req.into_response(\n                    HttpResponse::MethodNotAllowed()\n                        .insert_header(header::ContentType(mime::TEXT_PLAIN_UTF_8))\n                        .body(\"Request did not meet this resource's requirements.\"),\n                ));\n            }\n\n            let path_on_disk =\n                match PathBufWrap::parse_path(req.match_info().unprocessed(), this.hidden_files) {\n                    Ok(item) => item,\n                    Err(err) => return Ok(req.error_response(err)),\n                };\n\n            if let Some(filter) = &this.path_filter {\n                if !filter(path_on_disk.as_ref(), req.head()) {\n                    if let Some(ref default) = this.default {\n                        return default.call(req).await;\n                    } else {\n                        return Ok(req.into_response(HttpResponse::NotFound().finish()));\n                    }\n                }\n            }\n\n            // full file path\n            let path = this.directory.join(&path_on_disk);\n\n            // Try serving pre-compressed file even if the uncompressed file doesn't exist yet.\n            // Still handle directories (index/listing) through the normal branch below.\n            if this.try_compressed && !path.is_dir() {\n                if let Some((named_file, encoding)) = find_compressed(&req, &path).await {\n                    return Ok(this.serve_named_file_with_encoding(req, named_file, encoding));\n                }\n            }\n\n            if let Err(err) = path.canonicalize() {\n                return this.handle_err(err, req).await;\n            }\n\n            if path.is_dir() {\n                if this.redirect_to_slash\n                    && !req.path().ends_with('/')\n                    && (this.index.is_some() || this.show_index)\n                {\n                    let redirect_to = format!(\"{}/\", req.path());\n\n                    let response = if this.with_permanent_redirect {\n                        HttpResponse::PermanentRedirect()\n                    } else {\n                        HttpResponse::TemporaryRedirect()\n                    }\n                    .insert_header((header::LOCATION, redirect_to))\n                    .finish();\n\n                    return Ok(req.into_response(response));\n                }\n\n                match this.index {\n                    Some(ref index) => {\n                        let named_path = path.join(index);\n                        if this.try_compressed {\n                            if let Some((named_file, encoding)) =\n                                find_compressed(&req, &named_path).await\n                            {\n                                return Ok(\n                                    this.serve_named_file_with_encoding(req, named_file, encoding)\n                                );\n                            }\n                        }\n                        // fallback to the uncompressed version\n                        match NamedFile::open_async(named_path).await {\n                            Ok(named_file) => Ok(this.serve_named_file(req, named_file)),\n                            Err(_) if this.show_index => Ok(this.show_index(req, path)),\n                            Err(err) => this.handle_err(err, req).await,\n                        }\n                    }\n                    None if this.show_index => Ok(this.show_index(req, path)),\n                    None => Ok(ServiceResponse::from_err(\n                        FilesError::IsDirectory,\n                        req.into_parts().0,\n                    )),\n                }\n            } else {\n                match NamedFile::open_async(&path).await {\n                    Ok(named_file) => Ok(this.serve_named_file(req, named_file)),\n                    Err(err) => this.handle_err(err, req).await,\n                }\n            }\n        })\n    }\n}\n\n/// Flate doesn't have an accepted file extension, so it is not included here.\nconst SUPPORTED_PRECOMPRESSION_ENCODINGS: &[header::ContentEncoding] = &[\n    header::ContentEncoding::Brotli,\n    header::ContentEncoding::Gzip,\n    header::ContentEncoding::Zstd,\n    header::ContentEncoding::Identity,\n];\n\n/// Searches disk for an acceptable alternate encoding of the content at the given path, as\n/// preferred by the request's `Accept-Encoding` header. Returns the corresponding `NamedFile` with\n/// the most appropriate supported encoding, if any exist.\nasync fn find_compressed(\n    req: &ServiceRequest,\n    original_path: &Path,\n) -> Option<(NamedFile, header::ContentEncoding)> {\n    use actix_web::HttpMessage;\n    use header::{AcceptEncoding, ContentEncoding, Encoding};\n\n    // Retrieve the content type and content disposition based on the original filename. If we\n    // can't get these successfully, don't even try to find a compressed file.\n    let (content_type, content_disposition) =\n        match crate::named::get_content_type_and_disposition(original_path) {\n            Ok(values) => values,\n            Err(_) => return None,\n        };\n\n    let accept_encoding = req.get_header::<AcceptEncoding>()?;\n\n    let mut supported = SUPPORTED_PRECOMPRESSION_ENCODINGS\n        .iter()\n        .copied()\n        .map(Encoding::Known)\n        .collect::<Vec<_>>();\n\n    // Only move the original content-type/disposition into the chosen compressed file once.\n    let mut content_type = Some(content_type);\n    let mut content_disposition = Some(content_disposition);\n\n    loop {\n        // Select next acceptable encoding (honouring q=0 rejections) from remaining supported set.\n        let chosen = accept_encoding.negotiate(supported.iter())?;\n\n        let encoding = match chosen {\n            Encoding::Known(enc) => enc,\n            // No supported encoding should ever be unknown here.\n            Encoding::Unknown(_) => return None,\n        };\n\n        // Identity indicates there is no acceptable pre-compressed representation.\n        if encoding == ContentEncoding::Identity {\n            return None;\n        }\n\n        let extension = match encoding {\n            ContentEncoding::Brotli => \".br\",\n            ContentEncoding::Gzip => \".gz\",\n            ContentEncoding::Zstd => \".zst\",\n            ContentEncoding::Identity => unreachable!(),\n            // Only variants in SUPPORTED_PRECOMPRESSION_ENCODINGS can occur here.\n            _ => unreachable!(),\n        };\n\n        let mut compressed_path = original_path.to_owned();\n        let mut filename = compressed_path.file_name()?.to_owned();\n        filename.push(extension);\n        compressed_path.set_file_name(filename);\n\n        match NamedFile::open_async(&compressed_path).await {\n            Ok(mut named_file) => {\n                named_file.content_type = content_type.take().unwrap();\n                named_file.content_disposition = content_disposition.take().unwrap();\n                return Some((named_file, encoding));\n            }\n            // Ignore errors while searching disk for a suitable encoding.\n            Err(_) => {\n                supported.retain(|enc| enc != &chosen);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-files/tests/encoding.rs",
    "content": "use actix_files::{Files, NamedFile};\nuse actix_web::{\n    http::{\n        header::{self, HeaderValue},\n        StatusCode,\n    },\n    test::{self, TestRequest},\n    web, App,\n};\n\n#[actix_web::test]\nasync fn test_utf8_file_contents() {\n    // use default ISO-8859-1 encoding\n    let srv = test::init_service(App::new().service(Files::new(\"/\", \"./tests\"))).await;\n\n    let req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n\n    // disable UTF-8 attribute\n    let srv =\n        test::init_service(App::new().service(Files::new(\"/\", \"./tests\").prefer_utf8(false))).await;\n\n    let req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain\")),\n    );\n}\n\n#[actix_web::test]\nasync fn test_compression_encodings() {\n    use actix_web::body::MessageBody;\n\n    let utf8_txt_len = std::fs::metadata(\"./tests/utf8.txt\").unwrap().len();\n    let utf8_txt_br_len = std::fs::metadata(\"./tests/utf8.txt.br\").unwrap().len();\n    let utf8_txt_gz_len = std::fs::metadata(\"./tests/utf8.txt.gz\").unwrap().len();\n\n    let srv =\n        test::init_service(App::new().service(Files::new(\"/\", \"./tests\").try_compressed())).await;\n\n    // Select the requested encoding when present\n    let mut req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    req.headers_mut().insert(\n        header::ACCEPT_ENCODING,\n        header::HeaderValue::from_static(\"gzip\"),\n    );\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n    assert_eq!(\n        res.headers().get(header::CONTENT_ENCODING),\n        Some(&HeaderValue::from_static(\"gzip\")),\n    );\n    assert_eq!(\n        res.headers().get(header::VARY),\n        Some(&HeaderValue::from_static(\"accept-encoding\")),\n    );\n    assert_eq!(\n        res.into_body().size(),\n        actix_web::body::BodySize::Sized(utf8_txt_gz_len),\n    );\n\n    // Select the highest priority encoding\n    let mut req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    req.headers_mut().insert(\n        header::ACCEPT_ENCODING,\n        header::HeaderValue::from_static(\"gzip;q=0.6,br;q=0.8,*\"),\n    );\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n    assert_eq!(\n        res.headers().get(header::CONTENT_ENCODING),\n        Some(&HeaderValue::from_static(\"br\")),\n    );\n    assert_eq!(\n        res.headers().get(header::VARY),\n        Some(&HeaderValue::from_static(\"accept-encoding\")),\n    );\n    assert_eq!(\n        res.into_body().size(),\n        actix_web::body::BodySize::Sized(utf8_txt_br_len),\n    );\n\n    // Request encoding that doesn't exist on disk and fallback to no encoding\n    let mut req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    req.headers_mut().insert(\n        header::ACCEPT_ENCODING,\n        header::HeaderValue::from_static(\"zstd\"),\n    );\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None,);\n    assert_eq!(\n        res.into_body().size(),\n        actix_web::body::BodySize::Sized(utf8_txt_len),\n    );\n\n    // Do not select an encoding explicitly refused via q=0\n    let mut req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    req.headers_mut().insert(\n        header::ACCEPT_ENCODING,\n        header::HeaderValue::from_static(\"zstd;q=1, gzip;q=0\"),\n    );\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None,);\n    assert_eq!(\n        res.into_body().size(),\n        actix_web::body::BodySize::Sized(utf8_txt_len),\n    );\n\n    // Can still request a compressed file directly\n    let req = TestRequest::with_uri(\"/utf8.txt.gz\").to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"application/gzip\")),\n    );\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None,);\n\n    // Don't try compressed files\n    let srv = test::init_service(App::new().service(Files::new(\"/\", \"./tests\"))).await;\n\n    let mut req = TestRequest::with_uri(\"/utf8.txt\").to_request();\n    req.headers_mut().insert(\n        header::ACCEPT_ENCODING,\n        header::HeaderValue::from_static(\"gzip\"),\n    );\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain; charset=utf-8\")),\n    );\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None);\n}\n\n#[actix_web::test]\nasync fn partial_range_response_encoding() {\n    let srv = test::init_service(App::new().default_service(web::to(|| async {\n        NamedFile::open_async(\"./tests/test.binary\").await.unwrap()\n    })))\n    .await;\n\n    // range request without accept-encoding returns no content-encoding header\n    let req = TestRequest::with_uri(\"/\")\n        .append_header((header::RANGE, \"bytes=10-20\"))\n        .to_request();\n    let res = test::call_service(&srv, req).await;\n    assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT);\n    assert!(!res.headers().contains_key(header::CONTENT_ENCODING));\n\n    // range request with accept-encoding still returns no content-encoding header\n    let req = TestRequest::with_uri(\"/\")\n        .append_header((header::RANGE, \"bytes=10-20\"))\n        .append_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .to_request();\n    let res = test::call_service(&srv, req).await;\n    assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT);\n    assert!(!res.headers().contains_key(header::CONTENT_ENCODING));\n}\n"
  },
  {
    "path": "actix-files/tests/fixtures/guards/first/index.txt",
    "content": "first"
  },
  {
    "path": "actix-files/tests/fixtures/guards/second/index.txt",
    "content": "second"
  },
  {
    "path": "actix-files/tests/guard.rs",
    "content": "use actix_files::Files;\nuse actix_web::{\n    guard::Host,\n    http::StatusCode,\n    test::{self, TestRequest},\n    App,\n};\nuse bytes::Bytes;\n\n#[actix_web::test]\nasync fn test_guard_filter() {\n    let srv = test::init_service(\n        App::new()\n            .service(Files::new(\"/\", \"./tests/fixtures/guards/first\").guard(Host(\"first.com\")))\n            .service(Files::new(\"/\", \"./tests/fixtures/guards/second\").guard(Host(\"second.com\"))),\n    )\n    .await;\n\n    let req = TestRequest::with_uri(\"/index.txt\")\n        .append_header((\"Host\", \"first.com\"))\n        .to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(test::read_body(res).await, Bytes::from(\"first\"));\n\n    let req = TestRequest::with_uri(\"/index.txt\")\n        .append_header((\"Host\", \"second.com\"))\n        .to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(test::read_body(res).await, Bytes::from(\"second\"));\n}\n"
  },
  {
    "path": "actix-files/tests/pre_epoch_mtime.rs",
    "content": "use std::time::UNIX_EPOCH;\n\nuse actix_files::NamedFile;\nuse actix_web::{\n    http::{header, StatusCode},\n    test, web, App,\n};\nuse filetime::{set_file_mtime, FileTime};\nuse tempfile::tempdir;\n\n#[actix_web::test]\nasync fn serves_file_with_pre_epoch_mtime() {\n    let dir = tempdir().unwrap();\n    let path = dir.path().join(\"pre_epoch.txt\");\n\n    std::fs::write(&path, b\"hello\").unwrap();\n\n    // set mtime to before UNIX epoch; this used to panic during ETag/Last-Modified generation\n    set_file_mtime(&path, FileTime::from_unix_time(-60, 0)).unwrap();\n\n    let mtime = std::fs::metadata(&path).unwrap().modified().unwrap();\n    assert!(\n        mtime < UNIX_EPOCH,\n        \"fixture mtime should be before UNIX_EPOCH\"\n    );\n\n    let srv = {\n        let path = path.clone();\n        test::init_service(App::new().default_service(web::to(move || {\n            let path = path.clone();\n            async move { NamedFile::open_async(path).await.unwrap() }\n        })))\n        .await\n    };\n\n    let req = test::TestRequest::with_uri(\"/\").to_request();\n    let res = test::call_service(&srv, req).await;\n\n    assert_eq!(res.status(), StatusCode::OK);\n\n    // ETag is still generated even for pre-epoch times.\n    assert!(res.headers().contains_key(header::ETAG));\n\n    // HTTP-date formatting in the httpdate crate does not support pre-epoch times.\n    assert!(!res.headers().contains_key(header::LAST_MODIFIED));\n\n    let body = test::read_body(res).await;\n    assert_eq!(&body[..], b\"hello\");\n}\n"
  },
  {
    "path": "actix-files/tests/test space.binary",
    "content": "TǑɂV2vI\\R˙e\u0004vD:藽R\u0010V\u0003Yp\u001f;\u0007Gp!2C.\fpA\u000b\u0010!ߦx\tj+UcX\u0013c%\u0017;\"y\u0010AI"
  },
  {
    "path": "actix-files/tests/test.binary",
    "content": "TǑɂV2vI\\R˙e\u0004vD:藽R\u0010V\u0003Yp\u001f;\u0007Gp!2C.\fpA\u000b\u0010!ߦx\tj+UcX\u0013c%\u0017;\"y\u0010AI"
  },
  {
    "path": "actix-files/tests/test.js",
    "content": "// this file is empty.\n"
  },
  {
    "path": "actix-files/tests/traversal.rs",
    "content": "use actix_files::Files;\nuse actix_web::{\n    http::StatusCode,\n    test::{self, TestRequest},\n    App,\n};\n\n#[actix_rt::test]\nasync fn test_directory_traversal_prevention() {\n    let srv = test::init_service(App::new().service(Files::new(\"/\", \"./tests\"))).await;\n\n    let req = TestRequest::with_uri(\"/../../../../../../../../../../../etc/passwd\").to_request();\n    let res = test::call_service(&srv, req).await;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n\n    let req = TestRequest::with_uri(\n        \"/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd\",\n    )\n    .to_request();\n    let res = test::call_service(&srv, req).await;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n\n    let req = TestRequest::with_uri(\"/%00/etc/passwd%00\").to_request();\n    let res = test::call_service(&srv, req).await;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n}\n"
  },
  {
    "path": "actix-files/tests/utf8.txt",
    "content": "中文内容显示正确。\n\nEnglish is OK.\n"
  },
  {
    "path": "actix-http/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Encode the HTTP/1 `Connection: Upgrade` header in Camel-Case when camel-case header formatting is enabled.[#3953]\n- Fix `HeaderMap` iterators' `len()` and `size_hint()` implementations for multi-value headers.\n\n[#3953]: https://github.com/actix/actix-web/pull/3953\n\n## 3.12.0\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n- Increase default HTTP/2 flow control window sizes. [#3638]\n- Expose configuration methods to improve upload throughput. [#3638]\n- Fix truncated body ending without error when connection closed abnormally. [#3067]\n- Add config/method for `TCP_NODELAY`. [#3918]\n- Do not compress 206 Partial Content responses. [#3191]\n- Fix lingering sockets and client stalls when responding early to dropped chunked request payloads. [#2972]\n\n[#3638]: https://github.com/actix/actix-web/issues/3638\n[#3067]: https://github.com/actix/actix-web/pull/3067\n[#3918]: https://github.com/actix/actix-web/pull/3918\n[#3191]: https://github.com/actix/actix-web/issues/3191\n[#2972]: https://github.com/actix/actix-web/issues/2972\n\n## 3.11.2\n\n- Properly wake Payload receivers when feeding errors or EOF.\n- Add `ServiceConfigBuilder` type to facilitate future configuration extensions.\n- Add a configuration option to allow/disallow half closed connections in HTTP/1. This defaults to allow, reverting the change made in 3.11.1.\n- Shutdown connections when HTTP Responses are written without reading full Requests.\n\n## 3.11.1\n\n- Prevent more hangs after client disconnects.\n- More malformed WebSocket frames are now gracefully rejected.\n- Using `TestRequest::set_payload()` now sets a Content-Length header.\n\n## 3.11.0\n\n- Update `brotli` dependency to `8`.\n\n## 3.10.0\n\n### Added\n\n- Add `header::CLEAR_SITE_DATA` constant.\n- Add `Extensions::get_or_insert[_with]()` methods.\n- Implement `From<Bytes>` for `Payload`.\n- Implement `From<Vec<u8>>` for `Payload`.\n\n### Changed\n\n- Update `brotli` dependency to `7`.\n- Minimum supported Rust version (MSRV) is now 1.75.\n\n## 3.9.0\n\n### Added\n\n- Implement `FromIterator<(HeaderName, HeaderValue)>` for `HeaderMap`.\n\n## 3.8.0\n\n### Added\n\n- Add `error::InvalidStatusCode` re-export.\n\n## 3.7.0\n\n### Added\n\n- Add `rustls-0_23` crate feature\n- Add `{h1::H1Service, h2::H2Service, HttpService}::rustls_0_23()` and `HttpService::rustls_0_23_with_config()` service constructors.\n\n### Changed\n\n- Update `brotli` dependency to `6`.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 3.6.0\n\n### Added\n\n- Add `rustls-0_22` crate feature.\n- Add `{h1::H1Service, h2::H2Service, HttpService}::rustls_0_22()` and `HttpService::rustls_0_22_with_config()` service constructors.\n- Implement `From<&HeaderMap>` for `http::HeaderMap`.\n\n## 3.5.1\n\n### Fixed\n\n- Prevent hang when returning zero-sized response bodies through compression layer.\n\n## 3.5.0\n\n### Added\n\n- Implement `From<HeaderMap>` for `http::HeaderMap`.\n\n### Changed\n\n- Updated `zstd` dependency to `0.13`.\n\n### Fixed\n\n- Prevent compression of zero-sized response bodies.\n\n## 3.4.0\n\n### Added\n\n- Add `rustls-0_20` crate feature.\n- Add `{h1::H1Service, h2::H2Service, HttpService}::rustls_021()` and `HttpService::rustls_021_with_config()` service constructors.\n- Add `body::to_bytes_limited()` function.\n- Add `body::BodyLimitExceeded` error type.\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 3.3.1\n\n### Fixed\n\n- Use correct `http` version requirement to ensure support for const `HeaderName` definitions.\n\n## 3.3.0\n\n### Added\n\n- Implement `MessageBody` for `Cow<'static, str>` and `Cow<'static, [u8]>`. [#2959]\n- Implement `MessageBody` for `&mut B` where `B: MessageBody + Unpin`. [#2868]\n- Implement `MessageBody` for `Pin<B>` where `B::Target: MessageBody`. [#2868]\n- Automatic h2c detection via new service finalizer `HttpService::tcp_auto_h2c()`. [#2957]\n- `HeaderMap::retain()`. [#2955]\n- Header name constants in `header` module. [#2956] [#2968]\n  - `CACHE_STATUS`\n  - `CDN_CACHE_CONTROL`\n  - `CROSS_ORIGIN_EMBEDDER_POLICY`\n  - `CROSS_ORIGIN_OPENER_POLICY`\n  - `PERMISSIONS_POLICY`\n  - `X_FORWARDED_FOR`\n  - `X_FORWARDED_HOST`\n  - `X_FORWARDED_PROTO`\n\n### Fixed\n\n- Fix non-empty body of HTTP/2 HEAD responses. [#2920]\n\n### Performance\n\n- Improve overall performance of operations on `Extensions`. [#2890]\n\n[#2959]: https://github.com/actix/actix-web/pull/2959\n[#2868]: https://github.com/actix/actix-web/pull/2868\n[#2890]: https://github.com/actix/actix-web/pull/2890\n[#2920]: https://github.com/actix/actix-web/pull/2920\n[#2957]: https://github.com/actix/actix-web/pull/2957\n[#2955]: https://github.com/actix/actix-web/pull/2955\n[#2956]: https://github.com/actix/actix-web/pull/2956\n[#2968]: https://github.com/actix/actix-web/pull/2968\n\n## 3.2.2\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n### Fixed\n\n- Avoid possibility of dispatcher getting stuck while back-pressuring I/O. [#2369]\n\n[#2369]: https://github.com/actix/actix-web/pull/2369\n\n## 3.2.1\n\n### Fixed\n\n- Fix parsing ambiguity in Transfer-Encoding and Content-Length headers for HTTP/1.0 requests. [#2794]\n\n[#2794]: https://github.com/actix/actix-web/pull/2794\n\n## 3.2.0\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.\n\n### Fixed\n\n- Websocket parser no longer throws endless overflow errors after receiving an oversized frame. [#2790]\n- Retain previously set Vary headers when using compression encoder. [#2798]\n\n[#2790]: https://github.com/actix/actix-web/pull/2790\n[#2798]: https://github.com/actix/actix-web/pull/2798\n\n## 3.1.0\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.\n\n### Fixed\n\n- Revert broken fix in [#2624] that caused erroneous 500 error responses. Temporarily re-introduces [#2357] bug. [#2779]\n\n[#2624]: https://github.com/actix/actix-web/pull/2624\n[#2357]: https://github.com/actix/actix-web/issues/2357\n[#2779]: https://github.com/actix/actix-web/pull/2779\n\n## 3.0.4\n\n### Fixed\n\n- Document on docs.rs with `ws` feature enabled.\n\n## 3.0.3\n\n### Fixed\n\n- Allow spaces between header name and colon when parsing responses. [#2684]\n\n[#2684]: https://github.com/actix/actix-web/pull/2684\n\n## 3.0.2\n\n### Fixed\n\n- Fix encoding camel-case header names with more than one hyphen. [#2683]\n\n[#2683]: https://github.com/actix/actix-web/pull/2683\n\n## 3.0.1\n\n- Fix panic in H1 dispatcher when pipelining is used with keep-alive. [#2678]\n\n[#2678]: https://github.com/actix/actix-web/issues/2678\n\n## 3.0.0\n\n### Dependencies\n\n- Updated `actix-*` to Tokio v1-based versions. [#1813]\n- Updated `bytes` to `1.0`. [#1813]\n- Updated `h2` to `0.3`. [#1813]\n- Updated `rustls` to `0.20.0`. [#2414]\n- Updated `language-tags` to `0.3`.\n- Updated `tokio` to `1`.\n\n### Added\n\n- Crate Features:\n  - `ws`; disabled by default. [#2618]\n  - `http2`; disabled by default. [#2618]\n  - `compress-brotli`; disabled by default. [#2618]\n  - `compress-gzip`; disabled by default. [#2618]\n  - `compress-zstd`; disabled by default. [#2618]\n- Functions:\n  - `body::to_bytes` for async collecting message body into Bytes. [#2158]\n- Traits:\n  - `TryIntoHeaderPair`; allows using typed and untyped headers in the same methods. [#1869]\n- Types:\n  - `body::BoxBody`; a boxed message body with boxed errors. [#2183]\n  - `body::EitherBody` enum. [#2468]\n  - `body::None` struct. [#2468]\n  - Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]\n- Variants:\n  - `ContentEncoding::Zstd` along with . [#2244]\n  - `Protocol::Http3` for future compatibility and also mark `#[non_exhaustive]`. [00ba8d55]\n- Methods:\n  - `ContentEncoding::to_header_value()`. [#2501]\n  - `header::QualityItem::{max, min}()`. [#2486]\n  - `header::QualityItem::zero()` that uses `Quality::ZERO`. [#2501]\n  - `HeaderMap::drain()` as an efficient draining iterator. [#1964]\n  - `HeaderMap::len_keys()` has the behavior of the old `len` method. [#1964]\n  - `MessageBody::boxed` trait method for wrapping boxing types efficiently. [#2520]\n  - `MessageBody::try_into_bytes` trait method, with default implementation, for optimizations on body types that complete in exactly one poll. [#2522]\n  - `Request::conn_data()`. [#2491]\n  - `Request::take_conn_data()`. [#2491]\n  - `Request::take_req_data()`. [#2487]\n  - `Response::{ok, bad_request, not_found, internal_server_error}()`. [#2159]\n  - `Response::into_body()` that consumes response and returns body type. [#2201]\n  - `Response::map_into_boxed_body()`. [#2468]\n  - `ResponseBuilder::append_header()` method which allows using typed and untyped headers. [#1869]\n  - `ResponseBuilder::insert_header()` method which allows using typed and untyped headers. [#1869]\n  - `ResponseHead::set_camel_case_headers()`. [#2587]\n  - `TestRequest::insert_header()` method which allows using typed and untyped headers. [#1869]\n- Implementations:\n  - Implement `Clone for ws::HandshakeError`. [#2468]\n  - Implement `Clone` for `body::AnyBody<S> where S: Clone`. [#2448]\n  - Implement `Clone` for `RequestHead`. [#2487]\n  - Implement `Clone` for `ResponseHead`. [#2585]\n  - Implement `Copy` for `QualityItem<T> where T: Copy`. [#2501]\n  - Implement `Default` for `ContentEncoding`. [#1912]\n  - Implement `Default` for `HttpServiceBuilder`. [#2611]\n  - Implement `Default` for `KeepAlive`. [#2611]\n  - Implement `Default` for `Response`. [#2201]\n  - Implement `Default` for `ws::Codec`. [#1920]\n  - Implement `Display` for `header::Quality`. [#2486]\n  - Implement `Eq` for `header::ContentEncoding`. [#2501]\n  - Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]\n  - Implement `From<Duration>` for `KeepAlive`. [#2611]\n  - Implement `From<Option<Duration>>` for `KeepAlive`. [#2611]\n  - Implement `From<Vec<u8>>` for `Response<Vec<u8>>`. [#2625]\n  - Implement `FromStr` for `ContentEncoding`. [#1912]\n  - Implement `Header` for `ContentEncoding`. [#1912]\n  - Implement `IntoHeaderValue` for `ContentEncoding`. [#1912]\n  - Implement `IntoIterator` for `HeaderMap`. [#1964]\n  - Implement `MessageBody` for `bytestring::ByteString`. [#2468]\n  - Implement `MessageBody` for `Pin<Box<T>> where T: MessageBody`. [#2152]\n- Misc:\n  - Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]\n  - Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]\n  - `Quality::ZERO` associated constant equivalent to `q=0`. [#2501]\n  - `header::Quality::{MAX, MIN}` associated constants equivalent to `q=1` and `q=0.001`, respectively. [#2486]\n  - Timeout for canceling HTTP/2 server side connection handshake. Configurable with `ServiceConfig::client_timeout`; defaults to 5 seconds. [#2483]\n  - `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]\n\n### Changed\n\n- Traits:\n  - Rename `IntoHeaderValue => TryIntoHeaderValue`. [#2510]\n  - `MessageBody` now has an associated `Error` type. [#2183]\n- Types:\n  - `Protocol` enum is now marked `#[non_exhaustive]`.\n  - `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]\n  - `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]\n  - Error enums are marked `#[non_exhaustive]`. [#2161]\n  - Rename `PayloadStream` to `BoxedPayloadStream`. [#2545]\n  - The body type parameter of `Response` no longer has a default. [#2152]\n- Enum Variants:\n  - Rename `ContentEncoding::{Br => Brotli}`. [#2501]\n  - `Payload` inner fields are now named. [#2545]\n  - `ws::Message::Text` now contains a `bytestring::ByteString`. [#1864]\n- Methods:\n  - Rename `ServiceConfig::{client_timer_expire => client_request_deadline}`. [#2611]\n  - Rename `ServiceConfig::{client_disconnect_timer => client_disconnect_deadline}`. [#2611]\n  - Rename `h1::Codec::{keepalive => keep_alive}`. [#2611]\n  - Rename `h1::Codec::{keepalive_enabled => keep_alive_enabled}`. [#2611]\n  - Rename `h1::ClientCodec::{keepalive => keep_alive}`. [#2611]\n  - Rename `h1::ClientPayloadCodec::{keepalive => keep_alive}`. [#2611]\n  - Rename `header::EntityTag::{weak => new_weak, strong => new_strong}`. [#2565]\n  - Rename `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894]\n  - Deadline methods in `ServiceConfig` now return `std::time::Instant`s instead of Tokio's wrapper type. [#2611]\n  - Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]\n  - `encoding::Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]\n  - `Extensions::insert` returns replaced item. [#1904]\n  - `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]\n  - `HeaderMap::insert` now returns iterator of removed values. [#1964]\n  - `HeaderMap::len` now returns number of values instead of number of keys. [#1964]\n  - `HeaderMap::remove` now returns iterator of removed values. [#1964]\n  - `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]\n  - `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894]\n  - `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]\n  - `ResponseBuilder::json` now takes `impl Serialize`. [#2052]\n  - `ResponseBuilder::message_body` now returns a `Result`. [#2201]∑\n  - `ServiceConfig::keep_alive` now returns a `KeepAlive`. [#2611]\n  - `ws::hash_key` now returns array. [#2035]\n- Trait Implementations:\n  - Implementation of `Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]\n  - Implementation of `Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545]\n  - Implementation of `Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545]\n  - Implementation of `From` for error types now return a `Response<BoxBody>`. [#2468]\n- Misc:\n  - `header` module is now public. [#2171]\n  - `uri` module is now public. [#2171]\n  - Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487]\n  - All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<BoxBody>>`. [#2253]\n  - All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]\n  - Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]\n  - Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]\n  - Brotli (de)compression support is now provided by the `brotli` crate. [#2538]\n  - Minimum supported Rust version (MSRV) is now 1.54.\n\n### Fixed\n\n- A `Vary` header is now correctly sent along with compressed content. [#2501]\n- HTTP/1.1 dispatcher correctly uses client request timeout. [#2611]\n- Fixed issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]\n- `ContentEncoding`'s `Identity` variant can now be parsed from a string. [#2501]\n- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226]\n- Remove unnecessary `Into<Error>` bound on `Encoder` body types. [#2375]\n- Remove unnecessary `Unpin` bound on `ResponseBuilder::streaming`. [#2253]\n- `BodyStream` and `SizedStream` are no longer restricted to `Unpin` types. [#2152]\n- Fixed slice creation pointing to potential uninitialized data on h1 encoder. [#2364]\n- Fixed quality parse error in Accept-Encoding header. [#2344]\n\n### Removed\n\n- Crate Features:\n  - `compress` feature. [#2065]\n  - `cookies` feature. [#2065]\n  - `trust-dns` feature. [#2425]\n  - `actors` optional feature and trait implementation for `actix` types. [#1969]\n- Functions:\n  - `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]\n- Types:\n  - `body::Body`; replaced with `EitherBody` and `BoxBody`. [#2468]\n  - `body::ResponseBody`. [#2446]\n  - `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. Due to the removal of this type from `tokio-openssl` crate. OpenSSL handshake error now returns `ConnectError::SslError`. [#1813]\n  - `error::Canceled` re-export. [#1994]\n  - `error::Result` type alias. [#2201]\n  - `error::BlockingError` [#2660]\n  - `InternalError` and all the error types it constructed were moved up to `actix-web`. [#2215]\n  - Typed HTTP headers; they have moved up to `actix-web`. [2094]\n  - Re-export of `http` crate's `HeaderMap` types in addition to ours. [#2171]\n- Enum Variants:\n  - `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]\n  - `ContentEncoding::Auto`. [#2501]\n  - `EncoderError::Boxed`. [#2446]\n- Methods:\n  - `ContentEncoding::is_compression()`. [#2501]\n  - `h1::Payload::readany()`. [#2545]\n  - `HttpMessage::cookie[s]()` trait methods. [#2065]\n  - `HttpServiceBuilder::new()`; use `default` instead. [#2611]\n  - `on_connect` (previously deprecated) methods have been removed; use `on_connect_ext`. [#1857]\n  - `Response::build_from()`. [#2159]\n  - `Response::error()` [#2205]\n  - `Response::take_body()` and old `Response::into_body()` method that casted body type. [#2201]\n  - `Response`'s status code builders. [#2159]\n  - `ResponseBuilder::{if_true, if_some}()` (previously deprecated). [#2148]\n  - `ResponseBuilder::{set, set_header}()`; use `ResponseBuilder::insert_header()`. [#1869]\n  - `ResponseBuilder::extensions[_mut]()`. [#2585]\n  - `ResponseBuilder::header()`; use `ResponseBuilder::append_header()`. [#1869]\n  - `ResponseBuilder::json()`. [#2148]\n  - `ResponseBuilder::json2()`. [#1903]\n  - `ResponseBuilder::streaming()`. [#2468]\n  - `ResponseHead::extensions[_mut]()`. [#2585]\n  - `ServiceConfig::{client_timer, keep_alive_timer}()`. [#2611]\n  - `TestRequest::with_hdr()`; use `TestRequest::default().insert_header()`. [#1869]\n  - `TestRequest::with_header()`; use `TestRequest::default().insert_header()`. [#1869]\n- Trait implementations:\n  - Implementation of `Copy` for `ws::Codec`. [#1920]\n  - Implementation of `From<Option<usize>> for KeepAlive`; use `Duration`s instead. [#2611]\n  - Implementation of `From<serde_json::Value>` for `Body`. [#2148]\n  - Implementation of `From<usize> for KeepAlive`; use `Duration`s instead. [#2611]\n  - Implementation of `Future` for `Response`. [#2201]\n  - Implementation of `Future` for `ResponseBuilder`. [#2468]\n  - Implementation of `Into<Error>` for `Response<Body>`. [#2215]\n  - Implementation of `Into<Error>` for `ResponseBuilder`. [#2215]\n  - Implementation of `ResponseError` for `actix_utils::timeout::TimeoutError`. [#2127]\n  - Implementation of `ResponseError` for `CookieParseError`. [#2065]\n  - Implementation of `TryFrom<u16>` for `header::Quality`. [#2486]\n- Misc:\n  - `http` module; most everything it contained is exported at the crate root. [#2488]\n  - `cookies` module (re-export). [#2065]\n  - `client` module. Connector types now live in `awc`. [#2425]\n  - `error` field from `Response`. [#2205]\n  - `downcast` and `downcast_get_type_id` macros. [#2291]\n  - Down-casting for `MessageBody` types; use standard `Any` trait. [#2183]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1845]: https://github.com/actix/actix-web/pull/1845\n[#1857]: https://github.com/actix/actix-web/pull/1857\n[#1864]: https://github.com/actix/actix-web/pull/1864\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1878]: https://github.com/actix/actix-web/pull/1878\n[#1894]: https://github.com/actix/actix-web/pull/1894\n[#1903]: https://github.com/actix/actix-web/pull/1903\n[#1904]: https://github.com/actix/actix-web/pull/1904\n[#1912]: https://github.com/actix/actix-web/pull/1912\n[#1920]: https://github.com/actix/actix-web/pull/1920\n[#1964]: https://github.com/actix/actix-web/pull/1964\n[#1969]: https://github.com/actix/actix-web/pull/1969\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#1994]: https://github.com/actix/actix-web/pull/1994\n[#2035]: https://github.com/actix/actix-web/pull/2035\n[#2052]: https://github.com/actix/actix-web/pull/2052\n[#2065]: https://github.com/actix/actix-web/pull/2065\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2127]: https://github.com/actix/actix-web/pull/2127\n[#2148]: https://github.com/actix/actix-web/pull/2148\n[#2152]: https://github.com/actix/actix-web/pull/2152\n[#2158]: https://github.com/actix/actix-web/pull/2158\n[#2159]: https://github.com/actix/actix-web/pull/2159\n[#2161]: https://github.com/actix/actix-web/pull/2161\n[#2171]: https://github.com/actix/actix-web/pull/2171\n[#2183]: https://github.com/actix/actix-web/pull/2183\n[#2196]: https://github.com/actix/actix-web/pull/2196\n[#2201]: https://github.com/actix/actix-web/pull/2201\n[#2205]: https://github.com/actix/actix-web/pull/2205\n[#2215]: https://github.com/actix/actix-web/pull/2215\n[#2244]: https://github.com/actix/actix-web/pull/2244\n[#2250]: https://github.com/actix/actix-web/pull/2250\n[#2253]: https://github.com/actix/actix-web/pull/2253\n[#2291]: https://github.com/actix/actix-web/pull/2291\n[#2344]: https://github.com/actix/actix-web/pull/2344\n[#2364]: https://github.com/actix/actix-web/pull/2364\n[#2375]: https://github.com/actix/actix-web/pull/2375\n[#2377]: https://github.com/actix/actix-web/pull/2377\n[#2414]: https://github.com/actix/actix-web/pull/2414\n[#2425]: https://github.com/actix/actix-web/pull/2425\n[#2442]: https://github.com/actix/actix-web/pull/2442\n[#2446]: https://github.com/actix/actix-web/pull/2446\n[#2448]: https://github.com/actix/actix-web/pull/2448\n[#2456]: https://github.com/actix/actix-web/pull/2456\n[#2467]: https://github.com/actix/actix-web/pull/2467\n[#2468]: https://github.com/actix/actix-web/pull/2468\n[#2470]: https://github.com/actix/actix-web/pull/2470\n[#2474]: https://github.com/actix/actix-web/pull/2474\n[#2483]: https://github.com/actix/actix-web/pull/2483\n[#2486]: https://github.com/actix/actix-web/pull/2486\n[#2487]: https://github.com/actix/actix-web/pull/2487\n[#2488]: https://github.com/actix/actix-web/pull/2488\n[#2491]: https://github.com/actix/actix-web/pull/2491\n[#2497]: https://github.com/actix/actix-web/pull/2497\n[#2501]: https://github.com/actix/actix-web/pull/2501\n[#2510]: https://github.com/actix/actix-web/pull/2510\n[#2520]: https://github.com/actix/actix-web/pull/2520\n[#2522]: https://github.com/actix/actix-web/pull/2522\n[#2527]: https://github.com/actix/actix-web/pull/2527\n[#2538]: https://github.com/actix/actix-web/pull/2538\n[#2545]: https://github.com/actix/actix-web/pull/2545\n[#2565]: https://github.com/actix/actix-web/pull/2565\n[#2585]: https://github.com/actix/actix-web/pull/2585\n[#2587]: https://github.com/actix/actix-web/pull/2587\n[#2611]: https://github.com/actix/actix-web/pull/2611\n[#2618]: https://github.com/actix/actix-web/pull/2618\n[#2624]: https://github.com/actix/actix-web/pull/2624\n[#2625]: https://github.com/actix/actix-web/pull/2625\n[#2660]: https://github.com/actix/actix-web/pull/2660\n[00ba8d55]: https://github.com/actix/actix-web/commit/00ba8d55492284581695d824648590715a8bd386\n\n<details>\n<summary>3.0.0 Pre-Releases</summary>\n\n## 3.0.0-rc.4\n\n### Fixed\n\n- Fix h1 dispatcher panic. [1ce58ecb]\n\n[1ce58ecb]: https://github.com/actix/actix-web/commit/1ce58ecb305c60e51db06e6c913b7a1344e229ca\n\n## 3.0.0-rc.3\n\n- No significant changes since `3.0.0-rc.2`.\n\n## 3.0.0-rc.2\n\n### Added\n\n- Implement `From<Vec<u8>>` for `Response<Vec<u8>>`. [#2625]\n\n### Changed\n\n- `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]\n\n### Fixed\n\n- Issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]\n\n[#2624]: https://github.com/actix/actix-web/pull/2624\n[#2625]: https://github.com/actix/actix-web/pull/2625\n\n## 3.0.0-rc.1\n\n### Added\n\n- Implement `Default` for `KeepAlive`. [#2611]\n- Implement `From<Duration>` for `KeepAlive`. [#2611]\n- Implement `From<Option<Duration>>` for `KeepAlive`. [#2611]\n- Implement `Default` for `HttpServiceBuilder`. [#2611]\n- Crate `ws` feature flag, disabled by default. [#2618]\n- Crate `http2` feature flag, disabled by default. [#2618]\n\n### Changed\n\n- Rename `ServiceConfig::{client_timer_expire => client_request_deadline}`. [#2611]\n- Rename `ServiceConfig::{client_disconnect_timer => client_disconnect_deadline}`. [#2611]\n- Deadline methods in `ServiceConfig` now return `std::time::Instant`s instead of Tokio's wrapper type. [#2611]\n- Rename `h1::Codec::{keepalive => keep_alive}`. [#2611]\n- Rename `h1::Codec::{keepalive_enabled => keep_alive_enabled}`. [#2611]\n- Rename `h1::ClientCodec::{keepalive => keep_alive}`. [#2611]\n- Rename `h1::ClientPayloadCodec::{keepalive => keep_alive}`. [#2611]\n- `ServiceConfig::keep_alive` now returns a `KeepAlive`. [#2611]\n\n### Fixed\n\n- HTTP/1.1 dispatcher correctly uses client request timeout. [#2611]\n\n### Removed\n\n- `ServiceConfig::{client_timer, keep_alive_timer}`. [#2611]\n- `impl From<usize> for KeepAlive`; use `Duration`s instead. [#2611]\n- `impl From<Option<usize>> for KeepAlive`; use `Duration`s instead. [#2611]\n- `HttpServiceBuilder::new`; use `default` instead. [#2611]\n\n[#2611]: https://github.com/actix/actix-web/pull/2611\n[#2618]: https://github.com/actix/actix-web/pull/2618\n\n## 3.0.0-beta.19\n\n### Added\n\n- Response headers can be sent as camel case using `res.head_mut().set_camel_case_headers(true)`. [#2587]\n- `ResponseHead` now implements `Clone`. [#2585]\n\n### Changed\n\n- Brotli (de)compression support is now provided by the `brotli` crate. [#2538]\n\n### Removed\n\n- `ResponseHead::extensions[_mut]()`. [#2585]\n- `ResponseBuilder::extensions[_mut]()`. [#2585]\n\n[#2538]: https://github.com/actix/actix-web/pull/2538\n[#2585]: https://github.com/actix/actix-web/pull/2585\n[#2587]: https://github.com/actix/actix-web/pull/2587\n\n## 3.0.0-beta.18\n\n### Added\n\n- `impl Eq` for `header::ContentEncoding`. [#2501]\n- `impl Copy` for `QualityItem` where `T: Copy`. [#2501]\n- `Quality::ZERO` equivalent to `q=0`. [#2501]\n- `QualityItem::zero` that uses `Quality::ZERO`. [#2501]\n- `ContentEncoding::to_header_value()`. [#2501]\n\n### Changed\n\n- `Quality::MIN` is now the smallest non-zero value. [#2501]\n- `QualityItem::min` semantics changed with `QualityItem::MIN`. [#2501]\n- Rename `ContentEncoding::{Br => Brotli}`. [#2501]\n- Rename `header::EntityTag::{weak => new_weak, strong => new_strong}`. [#2565]\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n### Fixed\n\n- `ContentEncoding::Identity` can now be parsed from a string. [#2501]\n- A `Vary` header is now correctly sent along with compressed content. [#2501]\n\n### Removed\n\n- `ContentEncoding::Auto` variant. [#2501]\n- `ContentEncoding::is_compression()`. [#2501]\n\n[#2501]: https://github.com/actix/actix-web/pull/2501\n[#2565]: https://github.com/actix/actix-web/pull/2565\n\n## 3.0.0-beta.17\n\n### Changed\n\n- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]\n- `Payload` inner fields are now named. [#2545]\n- `impl Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]\n- `impl Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545]\n- `impl Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545]\n- Rename `PayloadStream` to `BoxedPayloadStream`. [#2545]\n\n### Removed\n\n- `h1::Payload::readany`. [#2545]\n\n[#2527]: https://github.com/actix/actix-web/pull/2527\n[#2545]: https://github.com/actix/actix-web/pull/2545\n\n## 3.0.0-beta.16\n\n### Added\n\n- New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522]\n\n### Changed\n\n- Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510]\n- Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510]\n- Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510]\n\n### Removed\n\n- `MessageBody::{is_complete_body,take_complete_body}`. [#2522]\n\n[#2510]: https://github.com/actix/actix-web/pull/2510\n[#2522]: https://github.com/actix/actix-web/pull/2522\n\n## 3.0.0-beta.15\n\n### Added\n\n- Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483]\n- HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483]\n- `Response::map_into_boxed_body`. [#2468]\n- `body::EitherBody` enum. [#2468]\n- `body::None` struct. [#2468]\n- Impl `MessageBody` for `bytestring::ByteString`. [#2468]\n- `impl Clone for ws::HandshakeError`. [#2468]\n- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]\n- `impl Default ` for `ws::Codec`. [#1920]\n- `header::QualityItem::{max, min}`. [#2486]\n- `header::Quality::{MAX, MIN}`. [#2486]\n- `impl Display` for `header::Quality`. [#2486]\n- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]\n- `Request::take_conn_data()`. [#2491]\n- `Request::take_req_data()`. [#2487]\n- `impl Clone` for `RequestHead`. [#2487]\n- New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497]\n- New `boxed` method on `MessageBody` trait for wrapping body type. [#2520]\n\n### Changed\n\n- Rename `body::BoxBody::{from_body => new}`. [#2468]\n- Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468]\n- The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468]\n- Error types using in service builders now require `Into<Response<BoxBody>>`. [#2468]\n- `From` implementations on error types now return a `Response<BoxBody>`. [#2468]\n- `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]\n- `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]\n\n### Removed\n\n- `ResponseBuilder::streaming`. [#2468]\n- `impl Future` for `ResponseBuilder`. [#2468]\n- Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468]\n- Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468]\n- `impl Copy` for `ws::Codec`. [#1920]\n- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]\n- `impl TryFrom<u16>` for `header::Quality`. [#2486]\n- `http` module. Most everything it contained is exported at the crate root. [#2488]\n\n[#2483]: https://github.com/actix/actix-web/pull/2483\n[#2468]: https://github.com/actix/actix-web/pull/2468\n[#1920]: https://github.com/actix/actix-web/pull/1920\n[#2486]: https://github.com/actix/actix-web/pull/2486\n[#2487]: https://github.com/actix/actix-web/pull/2487\n[#2488]: https://github.com/actix/actix-web/pull/2488\n[#2491]: https://github.com/actix/actix-web/pull/2491\n[#2497]: https://github.com/actix/actix-web/pull/2497\n[#2520]: https://github.com/actix/actix-web/pull/2520\n\n## 3.0.0-beta.14\n\n### Changed\n\n- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]\n- Expose `header::map` module. [#2467]\n- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]\n- Update `actix-tls` to `3.0.0-rc.1`. [#2474]\n\n[#2467]: https://github.com/actix/actix-web/pull/2467\n[#2470]: https://github.com/actix/actix-web/pull/2470\n[#2474]: https://github.com/actix/actix-web/pull/2474\n\n## 3.0.0-beta.13\n\n### Added\n\n- `body::AnyBody::empty` for quickly creating an empty body. [#2446]\n- `body::AnyBody::none` for quickly creating a \"none\" body. [#2456]\n- `impl Clone` for `body::AnyBody<S> where S: Clone`. [#2448]\n- `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448]\n\n### Changed\n\n- Rename `body::AnyBody::{Message => Body}`. [#2446]\n- Rename `body::AnyBody::{from_message => new_boxed}`. [#2448]\n- Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448]\n- Rename `body::{BoxAnyBody => BoxBody}`. [#2448]\n- Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448]\n- `Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]\n\n### Removed\n\n- `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446]\n- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]\n- `EncoderError::Boxed`; it is no longer required. [#2446]\n- `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446]\n\n[#2446]: https://github.com/actix/actix-web/pull/2446\n[#2448]: https://github.com/actix/actix-web/pull/2448\n[#2456]: https://github.com/actix/actix-web/pull/2456\n\n## 3.0.0-beta.12\n\n### Changed\n\n- Update `actix-server` to `2.0.0-beta.9`. [#2442]\n\n### Removed\n\n- `client` module. [#2425]\n- `trust-dns` feature. [#2425]\n\n[#2425]: https://github.com/actix/actix-web/pull/2425\n[#2442]: https://github.com/actix/actix-web/pull/2442\n\n## 3.0.0-beta.11\n\n### Changed\n\n- Updated rustls to v0.20. [#2414]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n[#2414]: https://github.com/actix/actix-web/pull/2414\n\n## 3.0.0-beta.10\n\n### Changed\n\n- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n### Fixed\n\n- Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364]\n- Remove `Into<Error>` bound on `Encoder` body types. [#2375]\n- Fix quality parse error in Accept-Encoding header. [#2344]\n\n[#2364]: https://github.com/actix/actix-web/pull/2364\n[#2375]: https://github.com/actix/actix-web/pull/2375\n[#2344]: https://github.com/actix/actix-web/pull/2344\n[#2377]: https://github.com/actix/actix-web/pull/2377\n\n## 3.0.0-beta.9\n\n### Fixed\n\n- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)\n\n## 3.0.0-beta.8\n\n### Changed\n\n- Change compression algorithm features flags. [#2250]\n\n### Removed\n\n- `downcast` and `downcast_get_type_id` macros. [#2291]\n\n[#2291]: https://github.com/actix/actix-web/pull/2291\n[#2250]: https://github.com/actix/actix-web/pull/2250\n\n## 3.0.0-beta.7\n\n### Added\n\n- Alias `body::Body` as `body::AnyBody`. [#2215]\n- `BoxAnyBody`: a boxed message body with boxed errors. [#2183]\n- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]\n- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]\n- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]\n- `Response::into_body` that consumes response and returns body type. [#2201]\n- `impl Default` for `Response`. [#2201]\n- Add zstd support for `ContentEncoding`. [#2244]\n\n### Changed\n\n- The `MessageBody` trait now has an associated `Error` type. [#2183]\n- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<AnyBody>>`. [#2253]\n- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]\n- Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]\n- `header` mod is now public. [#2171]\n- `uri` mod is now public. [#2171]\n- Update `language-tags` to `0.3`.\n- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]\n- `ResponseBuilder::message_body` now returns a `Result`. [#2201]\n- Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253]\n- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226]\n\n### Removed\n\n- Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]\n- Down-casting for `MessageBody` types. [#2183]\n- `error::Result` alias. [#2201]\n- Error field from `Response` and `Response::error`. [#2205]\n- `impl Future` for `Response`. [#2201]\n- `Response::take_body` and old `Response::into_body` method that casted body type. [#2201]\n- `InternalError` and all the error types it constructed. [#2215]\n- Conversion (`impl Into`) of `Response<Body>` and `ResponseBuilder` to `Error`. [#2215]\n\n[#2171]: https://github.com/actix/actix-web/pull/2171\n[#2183]: https://github.com/actix/actix-web/pull/2183\n[#2196]: https://github.com/actix/actix-web/pull/2196\n[#2201]: https://github.com/actix/actix-web/pull/2201\n[#2205]: https://github.com/actix/actix-web/pull/2205\n[#2215]: https://github.com/actix/actix-web/pull/2215\n[#2253]: https://github.com/actix/actix-web/pull/2253\n[#2244]: https://github.com/actix/actix-web/pull/2244\n\n## 3.0.0-beta.6\n\n### Added\n\n- `impl<T: MessageBody> MessageBody for Pin<Box<T>>`. [#2152]\n- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159]\n- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158]\n\n### Changed\n\n- The type parameter of `Response` no longer has a default. [#2152]\n- The `Message` variant of `body::Body` is now `Pin<Box<dyn MessageBody>>`. [#2152]\n- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152]\n- Error enum types are marked `#[non_exhaustive]`. [#2161]\n\n### Removed\n\n- `cookies` feature flag. [#2065]\n- Top-level `cookies` mod (re-export). [#2065]\n- `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065]\n- `impl ResponseError for CookieParseError`. [#2065]\n- Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148]\n- `ResponseBuilder::json`. [#2148]\n- `ResponseBuilder::{set_header, header}`. [#2148]\n- `impl From<serde_json::Value> for Body`. [#2148]\n- `Response::build_from`. [#2159]\n- Most of the status code builders on `Response`. [#2159]\n\n[#2065]: https://github.com/actix/actix-web/pull/2065\n[#2148]: https://github.com/actix/actix-web/pull/2148\n[#2152]: https://github.com/actix/actix-web/pull/2152\n[#2159]: https://github.com/actix/actix-web/pull/2159\n[#2158]: https://github.com/actix/actix-web/pull/2158\n[#2161]: https://github.com/actix/actix-web/pull/2161\n\n## 3.0.0-beta.5\n\n### Added\n\n- `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081]\n- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]\n- `client::ConnectionIo` trait alias [#2081]\n\n### Changed\n\n- `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]\n\n### Removed\n\n- Common typed HTTP headers were moved to actix-web. [2094]\n- `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]\n\n[#2063]: https://github.com/actix/actix-web/pull/2063\n[#2081]: https://github.com/actix/actix-web/pull/2081\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2127]: https://github.com/actix/actix-web/pull/2127\n\n## 3.0.0-beta.4\n\n### Changed\n\n- Feature `cookies` is now optional and disabled by default. [#1981]\n- `ws::hash_key` now returns array. [#2035]\n- `ResponseBuilder::json` now takes `impl Serialize`. [#2052]\n\n### Removed\n\n- Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994]\n- `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994]\n\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#1994]: https://github.com/actix/actix-web/pull/1994\n[#2035]: https://github.com/actix/actix-web/pull/2035\n[#2052]: https://github.com/actix/actix-web/pull/2052\n\n## 3.0.0-beta.3\n\n- No notable changes.\n\n## 3.0.0-beta.2\n\n### Added\n\n- `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869]\n- `ResponseBuilder::insert_header` method which allows using typed headers. [#1869]\n- `ResponseBuilder::append_header` method which allows using typed headers. [#1869]\n- `TestRequest::insert_header` method which allows using typed headers. [#1869]\n- `ContentEncoding` implements all necessary header traits. [#1912]\n- `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964]\n- `HeaderMap::drain` as an efficient draining iterator. [#1964]\n- Implement `IntoIterator` for owned `HeaderMap`. [#1964]\n- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]\n\n### Changed\n\n- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894]\n- Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894]\n- `Extensions::insert` returns Option of replaced item. [#1904]\n- Remove `HttpResponseBuilder::json2()`. [#1903]\n- Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903]\n- `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type. [#1905]\n- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]\n- Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool is dead. [#1957]\n- `HeaderMap::len` now returns number of values instead of number of keys. [#1964]\n- `HeaderMap::insert` now returns iterator of removed values. [#1964]\n- `HeaderMap::remove` now returns iterator of removed values. [#1964]\n\n### Removed\n\n- `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]\n- `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869]\n- `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869]\n- `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869]\n- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869]\n- `actors` optional feature. [#1969]\n- `ResponseError` impl for `actix::MailboxError`. [#1969]\n\n### Documentation\n\n- Vastly improve docs and add examples for `HeaderMap`. [#1964]\n\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1894]: https://github.com/actix/actix-web/pull/1894\n[#1903]: https://github.com/actix/actix-web/pull/1903\n[#1904]: https://github.com/actix/actix-web/pull/1904\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1912]: https://github.com/actix/actix-web/pull/1912\n[#1957]: https://github.com/actix/actix-web/pull/1957\n[#1964]: https://github.com/actix/actix-web/pull/1964\n[#1969]: https://github.com/actix/actix-web/pull/1969\n\n## 3.0.0-beta.1\n\n### Added\n\n- Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`.\n\n### Changed\n\n- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813]\n- Bumped `rand` to `0.8`.\n- Update `bytes` to `1.0`. [#1813]\n- Update `h2` to `0.3`. [#1813]\n- The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864]\n\n### Removed\n\n- Deprecated `on_connect` methods have been removed. Prefer the new `on_connect_ext` technique. [#1857]\n- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` due to deprecate of resolver actor. [#1813]\n- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. due to the removal of this type from `tokio-openssl` crate. openssl handshake error would return as `ConnectError::SslError`. [#1813]\n- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. Due to this change `actix_threadpool::BlockingError` type is moved into `actix_http::error` module. [#1878]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1857]: https://github.com/actix/actix-web/pull/1857\n[#1864]: https://github.com/actix/actix-web/pull/1864\n[#1878]: https://github.com/actix/actix-web/pull/1878\n\n</details>\n\n## 2.2.2\n\n### Changed\n\n- Migrate to `brotli` crate. [ad7e3c06]\n\n[ad7e3c06]: https://github.com/actix/actix-web/commit/ad7e3c06\n\n## 2.2.1\n\n### Fixed\n\n- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)\n\n## 2.2.0\n\n### Added\n\n- HttpResponse builders for 1xx status codes. [#1768]\n- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793]\n- `TryFrom<u16>` and `TryFrom<f32>` for `http::header::Quality`. [#1797]\n\n### Fixed\n\n- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]\n\n### Changed\n\n- Upgrade `serde_urlencoded` to `0.7`. [#1773]\n\n[#1773]: https://github.com/actix/actix-web/pull/1773\n[#1767]: https://github.com/actix/actix-web/pull/1767\n[#1768]: https://github.com/actix/actix-web/pull/1768\n[#1793]: https://github.com/actix/actix-web/pull/1793\n[#1797]: https://github.com/actix/actix-web/pull/1797\n\n## 2.1.0\n\n### Added\n\n- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754]\n\n### Changed\n\n- Upgrade `base64` to `0.13`. [#1744]\n- Upgrade `pin-project` to `1.0`. [#1733]\n- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760]\n\n[#1760]: https://github.com/actix/actix-web/pull/1760\n[#1754]: https://github.com/actix/actix-web/pull/1754\n[#1733]: https://github.com/actix/actix-web/pull/1733\n[#1744]: https://github.com/actix/actix-web/pull/1744\n\n## 2.0.0\n\n- No significant changes from `2.0.0-beta.4`.\n\n## 2.0.0-beta.4\n\n### Changed\n\n- Update actix-codec and actix-utils dependencies.\n- Update actix-connect and actix-tls dependencies.\n\n## 2.0.0-beta.3\n\n### Fixed\n\n- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626]\n\n[#1626]: https://github.com/actix/actix-web/pull/1626\n\n## 2.0.0-beta.2\n\n### Fixed\n\n- Potential UB in h1 decoder using uninitialized memory. [#1614]\n\n### Changed\n\n- Fix illegal chunked encoding. [#1615]\n\n[#1614]: https://github.com/actix/actix-web/pull/1614\n[#1615]: https://github.com/actix/actix-web/pull/1615\n\n## 2.0.0-beta.1\n\n### Changed\n\n- Migrate cookie handling to `cookie` crate. [#1558]\n- Update `sha-1` to 0.9. [#1586]\n- Fix leak in client pool. [#1580]\n- MSRV is now 1.41.1.\n\n[#1558]: https://github.com/actix/actix-web/pull/1558\n[#1586]: https://github.com/actix/actix-web/pull/1586\n[#1580]: https://github.com/actix/actix-web/pull/1580\n\n## 2.0.0-alpha.4\n\n### Changed\n\n- Bump minimum supported Rust version to 1.40\n- content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439]\n- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`.\n- Update `base64` dependency to 0.12\n\n### Fixed\n\n- Support parsing of `SameSite=None` [#1503]\n\n[#1439]: https://github.com/actix/actix-web/pull/1439\n[#1503]: https://github.com/actix/actix-web/pull/1503\n\n## 2.0.0-alpha.3\n\n### Fixed\n\n- Correct spelling of ConnectError::Unresolved [#1487]\n- Fix a mistake in the encoding of websocket continuation messages wherein Item::FirstText and Item::FirstBinary are each encoded as the other.\n\n### Changed\n\n- Implement `std::error::Error` for our custom errors [#1422]\n- Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future.\n\n[#1422]: https://github.com/actix/actix-web/pull/1422\n[#1487]: https://github.com/actix/actix-web/pull/1487\n\n## 2.0.0-alpha.2\n\n### Changed\n\n- Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395]\n- Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. [#1394]\n- client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#1394]\n- client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394]\n\n[#1394]: https://github.com/actix/actix-web/pull/1394\n[#1395]: https://github.com/actix/actix-web/pull/1395\n\n## 2.0.0-alpha.1\n\n### Changed\n\n- Update the `time` dependency to 0.2.7.\n- Moved actors messages support from actix crate, enabled with feature `actors`.\n- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of `&mut self` in the poll_next().\n- MessageBody is not implemented for &'static [u8] anymore.\n\n### Fixed\n\n- Allow `SameSite=None` cookies to be sent in a response.\n\n## 1.0.1\n\n### Fixed\n\n- Poll upgrade service's readiness from HTTP service handlers\n- Replace brotli with brotli2 #1224\n\n## 1.0.0\n\n### Added\n\n- Add websockets continuation frame support\n\n### Changed\n\n- Replace `flate2-xxx` features with `compress`\n\n## 1.0.0-alpha.5\n\n### Fixed\n\n- Check `Upgrade` service readiness before calling it\n- Fix buffer remaining capacity calculation\n\n### Changed\n\n- Websockets: Ping and Pong should have binary data #1049\n\n## 1.0.0-alpha.4\n\n### Added\n\n- Add impl ResponseBuilder for Error\n\n### Changed\n\n- Use rust based brotli compression library\n\n## 1.0.0-alpha.3\n\n### Changed\n\n- Migrate to tokio 0.2\n- Migrate to `std::future`\n\n## 0.2.11\n\n### Added\n\n- Add support for serde_json::Value to be passed as argument to ResponseBuilder.body()\n- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)\n- Allow to use `std::convert::Infallible` as `actix_http::error::Error`\n\n### Fixed\n\n- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header [#1118]\n\n[#1878]: https://github.com/actix/actix-web/pull/1878\n\n## 0.2.10\n\n### Added\n\n- Add support for sending HTTP requests with `Rc<RequestHead>` in addition to sending HTTP requests with `RequestHead`\n\n### Fixed\n\n- h2 will use error response #1080\n- on_connect result isn't added to request extensions for http2 requests #1009\n\n## 0.2.9\n\n### Changed\n\n- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation\n- Update percent-encoding to 2.1\n- Update serde_urlencoded to 0.6.1\n\n### Fixed\n\n- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031)\n\n## 0.2.8\n\n### Added\n\n- Add `rustls` support\n- Add `Clone` impl for `HeaderMap`\n\n### Fixed\n\n- awc client panic #1016\n- Invalid response with compression middleware enabled, but compression-related features disabled #997\n\n## 0.2.7\n\n### Added\n\n- Add support for downcasting response errors #986\n\n## 0.2.6\n\n### Changed\n\n- Replace `ClonableService` with local copy\n- Upgrade `rand` dependency version to 0.7\n\n## 0.2.5\n\n### Added\n\n- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946\n\n### Changed\n\n- Use `encoding_rs` crate instead of unmaintained `encoding` crate\n- Add `Copy` and `Clone` impls for `ws::Codec`\n\n## 0.2.4\n\n### Fixed\n\n- Do not compress NoContent (204) responses #918\n\n## 0.2.3\n\n### Added\n\n- Debug impl for ResponseBuilder\n- From SizedStream and BodyStream for Body\n\n### Changed\n\n- SizedStream uses u64\n\n## 0.2.2\n\n### Fixed\n\n- Parse incoming stream before closing stream on disconnect #868\n\n## 0.2.1\n\n### Fixed\n\n- Handle socket read disconnect\n\n## 0.2.0\n\n### Changed\n\n- Update actix-service to 0.4\n- Expect and upgrade services accept `ServerConfig` config.\n\n### Deleted\n\n- `OneRequest` service\n\n## 0.1.5\n\n### Fixed\n\n- Clean up response extensions in response pool #817\n\n## 0.1.4\n\n### Added\n\n- Allow to render h1 request headers in `Camel-Case`\n\n### Fixed\n\n- Read until eof for http/1.0 responses #771\n\n## 0.1.3\n\n### Fixed\n\n- Fix http client pool management\n- Fix http client wait queue management #794\n\n## 0.1.2\n\n### Fixed\n\n- Fix BorrowMutError panic in client connector #793\n\n## 0.1.1\n\n### Changed\n\n- Cookie::max_age() accepts value in seconds\n- Cookie::max_age_time() accepts value in time::Duration\n- Allow to specify server address for client connector\n\n## 0.1.0\n\n### Added\n\n- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr`\n\n### Changed\n\n- `actix_http::encoding` always available\n- use trust-dns-resolver 0.11.0\n\n## 0.1.0-alpha.5\n\n### Added\n\n- Allow to use custom service for upgrade requests\n- Added `h1::SendResponse` future.\n\n### Changed\n\n- MessageBody::length() renamed to MessageBody::size() for consistency\n- ws handshake verification functions take RequestHead instead of Request\n\n## 0.1.0-alpha.4\n\n### Added\n\n- Allow to use custom `Expect` handler\n- Add minimal `std::error::Error` impl for `Error`\n\n### Changed\n\n- Export IntoHeaderValue\n- Render error and return as response body\n- Use thread pool for response body compression\n\n### Deleted\n\n- Removed PayloadBuffer\n\n## 0.1.0-alpha.3\n\n### Added\n\n- Warn when an unsealed private cookie isn't valid UTF-8\n\n### Fixed\n\n- Rust 1.31.0 compatibility\n- Preallocate read buffer for h1 codec\n- Detect socket disconnection during protocol selection\n\n## 0.1.0-alpha.2\n\n### Added\n\n- Added ws::Message::Nop, no-op websockets message\n\n### Changed\n\n- Do not use thread pool for decompression if chunk size is smaller than 2048.\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "actix-http/Cargo.toml",
    "content": "[package]\nname = \"actix-http\"\nversion = \"3.12.0\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\", \"Rob Ede <robjtede@icloud.com>\"]\ndescription = \"HTTP types and services for the Actix ecosystem\"\nkeywords = [\"actix\", \"http\", \"framework\", \"async\", \"futures\"]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\ncategories = [\n  \"network-programming\",\n  \"asynchronous\",\n  \"web-programming::http-server\",\n  \"web-programming::websocket\",\n]\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[package.metadata.docs.rs]\nfeatures = [\n  \"http2\",\n  \"ws\",\n  \"openssl\",\n  \"rustls-0_20\",\n  \"rustls-0_21\",\n  \"rustls-0_22\",\n  \"rustls-0_23\",\n  \"compress-brotli\",\n  \"compress-gzip\",\n  \"compress-zstd\",\n]\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix_codec::*\",\n  \"actix_service::*\",\n  \"actix_tls::*\",\n  \"actix_utils::*\",\n  \"bytes::*\",\n  \"bytestring::*\",\n  \"encoding_rs::*\",\n  \"futures_core::*\",\n  \"h2::*\",\n  \"http::*\",\n  \"httparse::*\",\n  \"language_tags::*\",\n  \"mime::*\",\n  \"openssl::*\",\n  \"rustls::*\",\n  \"tokio_util::*\",\n  \"tokio::*\",\n]\n\n[features]\ndefault = []\n\n# HTTP/2 protocol support\nhttp2 = [\"dep:h2\"]\n\n# WebSocket protocol implementation\nws = [\"dep:local-channel\", \"dep:base64\", \"dep:rand\", \"dep:sha1\"]\n\n# TLS via OpenSSL\nopenssl = [\"__tls\", \"actix-tls/accept\", \"actix-tls/openssl\"]\n\n# TLS via Rustls v0.20\nrustls = [\"__tls\", \"rustls-0_20\"]\n\n# TLS via Rustls v0.20\nrustls-0_20 = [\"__tls\", \"actix-tls/accept\", \"actix-tls/rustls-0_20\"]\n\n# TLS via Rustls v0.21\nrustls-0_21 = [\"__tls\", \"actix-tls/accept\", \"actix-tls/rustls-0_21\"]\n\n# TLS via Rustls v0.22\nrustls-0_22 = [\"__tls\", \"actix-tls/accept\", \"actix-tls/rustls-0_22\"]\n\n# TLS via Rustls v0.23\nrustls-0_23 = [\"__tls\", \"actix-tls/accept\", \"actix-tls/rustls-0_23\"]\n\n# Compression codecs\ncompress-brotli = [\"__compress\", \"dep:brotli\"]\ncompress-gzip = [\"__compress\", \"dep:flate2\"]\ncompress-zstd = [\"__compress\", \"dep:zstd\"]\n\n# Internal (PRIVATE!) features used to aid testing and checking feature status.\n# Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime.\n__compress = []\n\n# Internal (PRIVATE!) features used to aid checking feature status.\n# Don't rely on these whatsoever. They may disappear at anytime.\n__tls = []\n\n[dependencies]\nactix-codec = \"0.5\"\nactix-rt = { version = \"2.2\", default-features = false }\nactix-service = \"2\"\nactix-utils = \"3\"\n\nbitflags = \"2\"\nbytes = \"1\"\nbytestring = \"1\"\nderive_more = { version = \"2\", features = [\"as_ref\", \"deref\", \"deref_mut\", \"display\", \"error\", \"from\"] }\nencoding_rs = \"0.8\"\nfoldhash = \"0.1\"\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nhttp = \"0.2.7\"\nhttparse = \"1.5.1\"\nhttpdate = \"1.0.1\"\nitoa = \"1\"\nlanguage-tags = \"0.3\"\nmime = \"0.3.4\"\npercent-encoding = \"2.1\"\npin-project-lite = \"0.2\"\nsmallvec = \"1.6.1\"\ntokio = { version = \"1.38.2\", features = [] }\ntokio-util = { version = \"0.7\", features = [\"io\", \"codec\"] }\ntracing = { version = \"0.1.30\", default-features = false, features = [\"log\"] }\n\n# http2\nh2 = { version = \"0.3.27\", optional = true }\n\n# websockets\nbase64 = { version = \"0.22\", optional = true }\nlocal-channel = { version = \"0.1\", optional = true }\nrand = { version = \"0.9\", optional = true }\nsha1 = { version = \"0.10\", optional = true }\n\n# openssl/rustls\nactix-tls = { version = \"3.4\", default-features = false, optional = true }\n\n# compress-*\nbrotli = { version = \"8\", optional = true }\nflate2 = { version = \"1.0.13\", optional = true }\nzstd = { version = \"0.13\", optional = true }\n\n[dev-dependencies]\nactix-http-test = { version = \"3\", features = [\"openssl\"] }\nactix-server = \"2\"\nactix-tls = { version = \"3.4\", features = [\"openssl\", \"rustls-0_23-webpki-roots\"] }\nactix-web = \"4\"\nawc = { version = \"3\", default-features = false, features = [\"openssl\"] }\n\nasync-stream = \"0.3\"\ncriterion = { version = \"0.5\", features = [\"html_reports\"] }\ndivan = \"0.1.8\"\nenv_logger = \"0.11\"\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nmemchr = \"2.4\"\nonce_cell = \"1.21\"\nrcgen = \"0.13\"\nregex = \"1.3\"\nrustls-pki-types = \"1.13.1\"\nrustversion = \"1\"\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1.0\"\nstatic_assertions = \"1\"\ntls-openssl = { package = \"openssl\", version = \"0.10.55\" }\ntls-rustls_023 = { package = \"rustls\", version = \"0.23\" }\ntokio = { version = \"1.38.2\", features = [\"net\", \"rt\", \"macros\", \"sync\"] }\n\n[lints]\nworkspace = true\n\n[[example]]\nname = \"ws\"\nrequired-features = [\"ws\", \"rustls-0_23\"]\n\n[[example]]\nname = \"tls_rustls\"\nrequired-features = [\"http2\", \"rustls-0_23\"]\n\n[[bench]]\nname = \"response-body-compression\"\nharness = false\nrequired-features = [\"compress-brotli\", \"compress-gzip\", \"compress-zstd\"]\n\n[[bench]]\nname = \"date-formatting\"\nharness = false\n"
  },
  {
    "path": "actix-http/README.md",
    "content": "# `actix-http`\n\n> HTTP types and services for the Actix ecosystem.\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)\n[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.12.0)](https://docs.rs/actix-http/3.12.0)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-http/3.12.0/status.svg)](https://deps.rs/crate/actix-http/3.12.0)\n[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n## Examples\n\n```rust\nuse std::{env, io};\n\nuse actix_http::{HttpService, Response};\nuse actix_server::Server;\nuse futures_util::future;\nuse http::header::HeaderValue;\nuse tracing::info;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env::set_var(\"RUST_LOG\", \"hello_world=info\");\n    env_logger::init();\n\n    Server::build()\n        .bind(\"hello-world\", \"127.0.0.1:8080\", || {\n            HttpService::build()\n                .client_timeout(1000)\n                .client_disconnect(1000)\n                .finish(|_req| {\n                    info!(\"{:?}\", _req);\n                    let mut res = Response::Ok();\n                    res.header(\"x-head\", HeaderValue::from_static(\"dummy value!\"));\n                    future::ok::<_, ()>(res.body(\"Hello world!\"))\n                })\n                .tcp()\n        })?\n        .run()\n        .await\n}\n```\n"
  },
  {
    "path": "actix-http/benches/date-formatting.rs",
    "content": "use std::time::SystemTime;\n\nuse actix_http::header::HttpDate;\nuse divan::{black_box, AllocProfiler, Bencher};\n\n#[global_allocator]\nstatic ALLOC: AllocProfiler = AllocProfiler::system();\n\n#[divan::bench]\nfn date_formatting(b: Bencher<'_, '_>) {\n    let now = SystemTime::now();\n\n    b.bench(|| {\n        black_box(HttpDate::from(black_box(now)).to_string());\n    })\n}\n\nfn main() {\n    divan::main();\n}\n"
  },
  {
    "path": "actix-http/benches/response-body-compression.rs",
    "content": "use std::convert::Infallible;\n\nuse actix_http::{encoding::Encoder, ContentEncoding, Request, Response, StatusCode};\nuse actix_service::{fn_service, Service as _};\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nstatic BODY: &[u8] = include_bytes!(\"../Cargo.toml\");\n\nfn compression_responses(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"compression responses\");\n\n    group.bench_function(\"identity\", |b| {\n        let rt = actix_rt::Runtime::new().unwrap();\n\n        let identity_svc = fn_service(|_: Request| async move {\n            let mut res = Response::with_body(StatusCode::OK, ());\n            let body = black_box(Encoder::response(\n                ContentEncoding::Identity,\n                res.head_mut(),\n                BODY,\n            ));\n            Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))\n        });\n\n        b.iter(|| {\n            rt.block_on(identity_svc.call(Request::new())).unwrap();\n        });\n    });\n\n    group.bench_function(\"gzip\", |b| {\n        let rt = actix_rt::Runtime::new().unwrap();\n\n        let identity_svc = fn_service(|_: Request| async move {\n            let mut res = Response::with_body(StatusCode::OK, ());\n            let body = black_box(Encoder::response(\n                ContentEncoding::Gzip,\n                res.head_mut(),\n                BODY,\n            ));\n            Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))\n        });\n\n        b.iter(|| {\n            rt.block_on(identity_svc.call(Request::new())).unwrap();\n        });\n    });\n\n    group.bench_function(\"br\", |b| {\n        let rt = actix_rt::Runtime::new().unwrap();\n\n        let identity_svc = fn_service(|_: Request| async move {\n            let mut res = Response::with_body(StatusCode::OK, ());\n            let body = black_box(Encoder::response(\n                ContentEncoding::Brotli,\n                res.head_mut(),\n                BODY,\n            ));\n            Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))\n        });\n\n        b.iter(|| {\n            rt.block_on(identity_svc.call(Request::new())).unwrap();\n        });\n    });\n\n    group.bench_function(\"zstd\", |b| {\n        let rt = actix_rt::Runtime::new().unwrap();\n\n        let identity_svc = fn_service(|_: Request| async move {\n            let mut res = Response::with_body(StatusCode::OK, ());\n            let body = black_box(Encoder::response(\n                ContentEncoding::Zstd,\n                res.head_mut(),\n                BODY,\n            ));\n            Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))\n        });\n\n        b.iter(|| {\n            rt.block_on(identity_svc.call(Request::new())).unwrap();\n        });\n    });\n\n    group.finish();\n}\n\ncriterion_group!(benches, compression_responses);\ncriterion_main!(benches);\n"
  },
  {
    "path": "actix-http/examples/actix-web.rs",
    "content": "use actix_http::HttpService;\nuse actix_server::Server;\nuse actix_service::map_config;\nuse actix_web::{dev::AppConfig, get, App, Responder};\n\n#[get(\"/\")]\nasync fn index() -> impl Responder {\n    \"Hello, world. From Actix Web!\"\n}\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() -> std::io::Result<()> {\n    Server::build()\n        .bind(\"hello-world\", \"127.0.0.1:8080\", || {\n            // construct actix-web app\n            let app = App::new().service(index);\n\n            HttpService::build()\n                // pass the app to service builder\n                // map_config is used to map App's configuration to ServiceBuilder\n                // h1 will configure server to only use HTTP/1.1\n                .h1(map_config(app, |_| AppConfig::default()))\n                .tcp()\n        })?\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/bench.rs",
    "content": "use std::{convert::Infallible, io, time::Duration};\n\nuse actix_http::{HttpService, Request, Response, StatusCode};\nuse actix_server::Server;\nuse once_cell::sync::Lazy;\n\nstatic STR: Lazy<String> = Lazy::new(|| \"HELLO WORLD \".repeat(20));\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"dispatcher-benchmark\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .client_request_timeout(Duration::from_secs(1))\n                .finish(|_: Request| async move {\n                    let mut res = Response::build(StatusCode::OK);\n                    Ok::<_, Infallible>(res.body(&**STR))\n                })\n                .tcp()\n        })?\n        // limiting number of workers so that bench client is not sharing as many resources\n        .workers(4)\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/echo.rs",
    "content": "use std::{io, time::Duration};\n\nuse actix_http::{Error, HttpService, Request, Response, StatusCode};\nuse actix_server::Server;\nuse bytes::BytesMut;\nuse futures_util::StreamExt as _;\nuse http::header::HeaderValue;\nuse tracing::info;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"echo\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .client_request_timeout(Duration::from_secs(1))\n                .client_disconnect_timeout(Duration::from_secs(1))\n                // handles HTTP/1.1 and HTTP/2\n                .finish(|mut req: Request| async move {\n                    let mut body = BytesMut::new();\n                    while let Some(item) = req.payload().next().await {\n                        body.extend_from_slice(&item?);\n                    }\n\n                    info!(\"request body: {body:?}\");\n\n                    let res = Response::build(StatusCode::OK)\n                        .insert_header((\"x-head\", HeaderValue::from_static(\"dummy value!\")))\n                        .body(body);\n\n                    Ok::<_, Error>(res)\n                })\n                .tcp() // No TLS\n        })?\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/echo2.rs",
    "content": "use std::io;\n\nuse actix_http::{\n    body::{BodyStream, MessageBody},\n    header, Error, HttpMessage, HttpService, Request, Response, StatusCode,\n};\n\nasync fn handle_request(mut req: Request) -> Result<Response<impl MessageBody>, Error> {\n    let mut res = Response::build(StatusCode::OK);\n\n    if let Some(ct) = req.headers().get(header::CONTENT_TYPE) {\n        res.insert_header((header::CONTENT_TYPE, ct));\n    }\n\n    // echo request payload stream as (chunked) response body\n    let res = res.message_body(BodyStream::new(req.payload().take()))?;\n\n    Ok(res)\n}\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    actix_server::Server::build()\n        .bind(\"echo\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                // handles HTTP/1.1 only\n                .h1(handle_request)\n                // No TLS\n                .tcp()\n        })?\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/h2c-detect.rs",
    "content": "//! An example that supports automatic selection of plaintext h1/h2c connections.\n//!\n//! Notably, both the following commands will work.\n//! ```console\n//! $ curl --http1.1 'http://localhost:8080/'\n//! $ curl --http2-prior-knowledge 'http://localhost:8080/'\n//! ```\n\nuse std::{convert::Infallible, io};\n\nuse actix_http::{body::BodyStream, HttpService, Request, Response, StatusCode};\nuse actix_server::Server;\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"h2c-detect\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .finish(|_req: Request| async move {\n                    Ok::<_, Infallible>(Response::build(StatusCode::OK).body(BodyStream::new(\n                        futures_util::stream::iter([\n                            Ok::<_, String>(\"123\".into()),\n                            Err(\"wertyuikmnbvcxdfty6t\".to_owned()),\n                        ]),\n                    )))\n                })\n                .tcp_auto_h2c()\n        })?\n        .workers(2)\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/h2spec.rs",
    "content": "use std::{convert::Infallible, io};\n\nuse actix_http::{HttpService, Request, Response, StatusCode};\nuse actix_server::Server;\nuse once_cell::sync::Lazy;\n\nstatic STR: Lazy<String> = Lazy::new(|| \"HELLO WORLD \".repeat(100));\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"h2spec\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .h2(|_: Request| async move {\n                    let mut res = Response::build(StatusCode::OK);\n                    Ok::<_, Infallible>(res.body(&**STR))\n                })\n                .tcp()\n        })?\n        .workers(4)\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/hello-world.rs",
    "content": "use std::{convert::Infallible, io, time::Duration};\n\nuse actix_http::{header::HeaderValue, HttpService, Request, Response, StatusCode};\nuse actix_server::Server;\nuse tracing::info;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"hello-world\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .client_request_timeout(Duration::from_secs(1))\n                .client_disconnect_timeout(Duration::from_secs(1))\n                .on_connect_ext(|_, ext| {\n                    ext.insert(42u32);\n                })\n                .finish(|req: Request| async move {\n                    info!(\"{req:?}\");\n\n                    let mut res = Response::build(StatusCode::OK);\n                    res.insert_header((\"x-head\", HeaderValue::from_static(\"dummy value!\")));\n\n                    let forty_two = req.conn_data::<u32>().unwrap().to_string();\n                    res.insert_header((\"x-forty-two\", HeaderValue::from_str(&forty_two).unwrap()));\n\n                    Ok::<_, Infallible>(res.body(\"Hello world!\"))\n                })\n                .tcp()\n        })?\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/streaming-error.rs",
    "content": "//! Example showing response body (chunked) stream erroring.\n//!\n//! Test using `nc` or `curl`.\n//! ```sh\n//! $ curl -vN 127.0.0.1:8080\n//! $ echo 'GET / HTTP/1.1\\n\\n' | nc 127.0.0.1 8080\n//! ```\n\nuse std::{convert::Infallible, io, time::Duration};\n\nuse actix_http::{body::BodyStream, HttpService, Response};\nuse actix_server::Server;\nuse async_stream::stream;\nuse bytes::Bytes;\nuse tracing::info;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"streaming-error\", (\"127.0.0.1\", 8080), || {\n            HttpService::build()\n                .finish(|req| async move {\n                    info!(\"{req:?}\");\n                    let res = Response::ok();\n\n                    Ok::<_, Infallible>(res.set_body(BodyStream::new(stream! {\n                        yield Ok(Bytes::from(\"123\"));\n                        yield Ok(Bytes::from(\"456\"));\n\n                        actix_rt::time::sleep(Duration::from_secs(1)).await;\n\n                        yield Err(io::Error::other(\"abc\"));\n                    })))\n                })\n                .tcp()\n        })?\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-http/examples/tls_rustls.rs",
    "content": "//! Demonstrates TLS configuration (via Rustls) for HTTP/1.1 and HTTP/2 connections.\n//!\n//! Test using cURL:\n//!\n//! ```console\n//! $ curl --insecure https://127.0.0.1:8443\n//! Hello World!\n//! Protocol: HTTP/2.0\n//!\n//! $ curl --insecure --http1.1 https://127.0.0.1:8443\n//! Hello World!\n//! Protocol: HTTP/1.1\n//! ```\n\nextern crate tls_rustls_023 as rustls;\n\nuse std::io;\n\nuse actix_http::{Error, HttpService, Request, Response};\nuse actix_utils::future::ok;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    tracing::info!(\"starting HTTP server at https://127.0.0.1:8443\");\n\n    actix_server::Server::build()\n        .bind(\"echo\", (\"127.0.0.1\", 8443), || {\n            HttpService::build()\n                .finish(|req: Request| {\n                    let body = format!(\n                        \"Hello World!\\n\\\n                        Protocol: {:?}\",\n                        req.head().version\n                    );\n                    ok::<_, Error>(Response::ok().set_body(body))\n                })\n                .rustls_0_23(rustls_config())\n        })?\n        .run()\n        .await\n}\n\nfn rustls_config() -> rustls::ServerConfig {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_chain = vec![cert.der().clone()];\n    let key_der = rustls_pki_types::PrivateKeyDer::Pkcs8(\n        rustls_pki_types::PrivatePkcs8KeyDer::from(key_pair.serialize_der()),\n    );\n\n    let mut config = rustls::ServerConfig::builder()\n        .with_no_client_auth()\n        .with_single_cert(cert_chain, key_der)\n        .unwrap();\n\n    const H1_ALPN: &[u8] = b\"http/1.1\";\n    const H2_ALPN: &[u8] = b\"h2\";\n\n    config.alpn_protocols.push(H2_ALPN.to_vec());\n    config.alpn_protocols.push(H1_ALPN.to_vec());\n\n    config\n}\n"
  },
  {
    "path": "actix-http/examples/ws.rs",
    "content": "//! Sets up a WebSocket server over TCP and TLS.\n//! Sends a heartbeat message every 4 seconds but does not respond to any incoming frames.\n\nextern crate tls_rustls_023 as rustls;\n\nuse std::{\n    io,\n    pin::Pin,\n    task::{Context, Poll},\n    time::Duration,\n};\n\nuse actix_http::{body::BodyStream, error::Error, ws, HttpService, Request, Response};\nuse actix_rt::time::{interval, Interval};\nuse actix_server::Server;\nuse bytes::{Bytes, BytesMut};\nuse bytestring::ByteString;\nuse futures_core::{ready, Stream};\nuse tokio_util::codec::Encoder;\n\n#[actix_rt::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    Server::build()\n        .bind(\"tcp\", (\"127.0.0.1\", 8080), || {\n            HttpService::build().h1(handler).tcp()\n        })?\n        .bind(\"tls\", (\"127.0.0.1\", 8443), || {\n            HttpService::build()\n                .finish(handler)\n                .rustls_0_23(tls_config())\n        })?\n        .run()\n        .await\n}\n\nasync fn handler(req: Request) -> Result<Response<BodyStream<Heartbeat>>, Error> {\n    tracing::info!(\"handshaking\");\n    let mut res = ws::handshake(req.head())?;\n\n    // handshake will always fail under HTTP/2\n\n    tracing::info!(\"responding\");\n    res.message_body(BodyStream::new(Heartbeat::new(ws::Codec::new())))\n}\n\nstruct Heartbeat {\n    codec: ws::Codec,\n    interval: Interval,\n}\n\nimpl Heartbeat {\n    fn new(codec: ws::Codec) -> Self {\n        Self {\n            codec,\n            interval: interval(Duration::from_secs(4)),\n        }\n    }\n}\n\nimpl Stream for Heartbeat {\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        tracing::trace!(\"poll\");\n\n        ready!(self.as_mut().interval.poll_tick(cx));\n\n        let mut buffer = BytesMut::new();\n\n        self.as_mut()\n            .codec\n            .encode(\n                ws::Message::Text(ByteString::from_static(\"hello world\")),\n                &mut buffer,\n            )\n            .unwrap();\n\n        Poll::Ready(Some(Ok(buffer.freeze())))\n    }\n}\n\nfn tls_config() -> rustls::ServerConfig {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_chain = vec![cert.der().clone()];\n    let key_der = rustls_pki_types::PrivateKeyDer::Pkcs8(\n        rustls_pki_types::PrivatePkcs8KeyDer::from(key_pair.serialize_der()),\n    );\n\n    let mut config = rustls::ServerConfig::builder()\n        .with_no_client_auth()\n        .with_single_cert(cert_chain, key_der)\n        .unwrap();\n\n    config.alpn_protocols.push(b\"http/1.1\".to_vec());\n    config.alpn_protocols.push(b\"h2\".to_vec());\n\n    config\n}\n"
  },
  {
    "path": "actix-http/src/body/body_stream.rs",
    "content": "use std::{\n    error::Error as StdError,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\nuse futures_core::{ready, Stream};\nuse pin_project_lite::pin_project;\n\nuse super::{BodySize, MessageBody};\n\npin_project! {\n    /// Streaming response wrapper.\n    ///\n    /// Response does not contain `Content-Length` header and appropriate transfer encoding is used.\n    pub struct BodyStream<S> {\n        #[pin]\n        stream: S,\n    }\n}\n\n// TODO: from_infallible method\n\nimpl<S, E> BodyStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, E>>,\n    E: Into<Box<dyn StdError>> + 'static,\n{\n    #[inline]\n    pub fn new(stream: S) -> Self {\n        BodyStream { stream }\n    }\n}\n\nimpl<S, E> MessageBody for BodyStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, E>>,\n    E: Into<Box<dyn StdError>> + 'static,\n{\n    type Error = E;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        BodySize::Stream\n    }\n\n    /// Attempts to pull out the next value of the underlying [`Stream`].\n    ///\n    /// Empty values are skipped to prevent [`BodyStream`]'s transmission being ended on a\n    /// zero-length chunk, but rather proceed until the underlying [`Stream`] ends.\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        loop {\n            let stream = self.as_mut().project().stream;\n\n            let chunk = match ready!(stream.poll_next(cx)) {\n                Some(Ok(ref bytes)) if bytes.is_empty() => continue,\n                opt => opt,\n            };\n\n            return Poll::Ready(chunk);\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{convert::Infallible, time::Duration};\n\n    use actix_rt::{\n        pin,\n        time::{sleep, Sleep},\n    };\n    use actix_utils::future::poll_fn;\n    use derive_more::{Display, Error};\n    use futures_core::ready;\n    use futures_util::{stream, FutureExt as _};\n    use pin_project_lite::pin_project;\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n\n    use super::*;\n    use crate::body::to_bytes;\n\n    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, crate::Error>>>: MessageBody);\n    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, &'static str>>>: MessageBody);\n    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, &'static str>>>: MessageBody);\n    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);\n    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);\n\n    assert_not_impl_any!(BodyStream<stream::Empty<Bytes>>: MessageBody);\n    assert_not_impl_any!(BodyStream<stream::Repeat<Bytes>>: MessageBody);\n    // crate::Error is not Clone\n    assert_not_impl_any!(BodyStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);\n\n    #[actix_rt::test]\n    async fn skips_empty_chunks() {\n        let body = BodyStream::new(stream::iter(\n            [\"1\", \"\", \"2\"]\n                .iter()\n                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),\n        ));\n        pin!(body);\n\n        assert_eq!(\n            poll_fn(|cx| body.as_mut().poll_next(cx))\n                .await\n                .unwrap()\n                .ok(),\n            Some(Bytes::from(\"1\")),\n        );\n        assert_eq!(\n            poll_fn(|cx| body.as_mut().poll_next(cx))\n                .await\n                .unwrap()\n                .ok(),\n            Some(Bytes::from(\"2\")),\n        );\n    }\n\n    #[actix_rt::test]\n    async fn read_to_bytes() {\n        let body = BodyStream::new(stream::iter(\n            [\"1\", \"\", \"2\"]\n                .iter()\n                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),\n        ));\n\n        assert_eq!(to_bytes(body).await.ok(), Some(Bytes::from(\"12\")));\n    }\n    #[derive(Debug, Display, Error)]\n    #[display(\"stream error\")]\n    struct StreamErr;\n\n    #[actix_rt::test]\n    async fn stream_immediate_error() {\n        let body = BodyStream::new(stream::once(async { Err(StreamErr) }));\n        assert!(matches!(to_bytes(body).await, Err(StreamErr)));\n    }\n\n    #[actix_rt::test]\n    async fn stream_string_error() {\n        // `&'static str` does not impl `Error`\n        // but it does impl `Into<Box<dyn Error>>`\n\n        let body = BodyStream::new(stream::once(async { Err(\"stringy error\") }));\n        assert!(matches!(to_bytes(body).await, Err(\"stringy error\")));\n    }\n\n    #[actix_rt::test]\n    async fn stream_boxed_error() {\n        // `Box<dyn Error>` does not impl `Error`\n        // but it does impl `Into<Box<dyn Error>>`\n\n        let body = BodyStream::new(stream::once(async {\n            Err(Box::<dyn StdError>::from(\"stringy error\"))\n        }));\n\n        assert_eq!(\n            to_bytes(body).await.unwrap_err().to_string(),\n            \"stringy error\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn stream_delayed_error() {\n        let body = BodyStream::new(stream::iter(vec![Ok(Bytes::from(\"1\")), Err(StreamErr)]));\n        assert!(matches!(to_bytes(body).await, Err(StreamErr)));\n\n        pin_project! {\n            #[derive(Debug)]\n            #[project = TimeDelayStreamProj]\n            enum TimeDelayStream {\n                Start,\n                Sleep { delay: Pin<Box<Sleep>> },\n                Done,\n            }\n        }\n\n        impl Stream for TimeDelayStream {\n            type Item = Result<Bytes, StreamErr>;\n\n            fn poll_next(\n                mut self: Pin<&mut Self>,\n                cx: &mut Context<'_>,\n            ) -> Poll<Option<Self::Item>> {\n                match self.as_mut().get_mut() {\n                    TimeDelayStream::Start => {\n                        let sleep = sleep(Duration::from_millis(1));\n                        self.as_mut().set(TimeDelayStream::Sleep {\n                            delay: Box::pin(sleep),\n                        });\n                        cx.waker().wake_by_ref();\n                        Poll::Pending\n                    }\n\n                    TimeDelayStream::Sleep { ref mut delay } => {\n                        ready!(delay.poll_unpin(cx));\n                        self.set(TimeDelayStream::Done);\n                        cx.waker().wake_by_ref();\n                        Poll::Pending\n                    }\n\n                    TimeDelayStream::Done => Poll::Ready(Some(Err(StreamErr))),\n                }\n            }\n        }\n\n        let body = BodyStream::new(TimeDelayStream::Start);\n        assert!(matches!(to_bytes(body).await, Err(StreamErr)));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/boxed.rs",
    "content": "use std::{\n    error::Error as StdError,\n    fmt,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\n\nuse super::{BodySize, MessageBody, MessageBodyMapErr};\nuse crate::body;\n\n/// A boxed message body with boxed errors.\n#[derive(Debug)]\npub struct BoxBody(BoxBodyInner);\n\nenum BoxBodyInner {\n    None(body::None),\n    Bytes(Bytes),\n    Stream(Pin<Box<dyn MessageBody<Error = Box<dyn StdError>>>>),\n}\n\nimpl fmt::Debug for BoxBodyInner {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::None(arg0) => f.debug_tuple(\"None\").field(arg0).finish(),\n            Self::Bytes(arg0) => f.debug_tuple(\"Bytes\").field(arg0).finish(),\n            Self::Stream(_) => f.debug_tuple(\"Stream\").field(&\"dyn MessageBody\").finish(),\n        }\n    }\n}\n\nimpl BoxBody {\n    /// Boxes body type, erasing type information.\n    ///\n    /// If the body type to wrap is unknown or generic it is better to use [`MessageBody::boxed`] to\n    /// avoid double boxing.\n    #[inline]\n    pub fn new<B>(body: B) -> Self\n    where\n        B: MessageBody + 'static,\n    {\n        match body.size() {\n            BodySize::None => Self(BoxBodyInner::None(body::None)),\n            _ => match body.try_into_bytes() {\n                Ok(bytes) => Self(BoxBodyInner::Bytes(bytes)),\n                Err(body) => {\n                    let body = MessageBodyMapErr::new(body, Into::into);\n                    Self(BoxBodyInner::Stream(Box::pin(body)))\n                }\n            },\n        }\n    }\n\n    /// Returns a mutable pinned reference to the inner message body type.\n    #[inline]\n    pub fn as_pin_mut(&mut self) -> Pin<&mut Self> {\n        Pin::new(self)\n    }\n}\n\nimpl MessageBody for BoxBody {\n    type Error = Box<dyn StdError>;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        match &self.0 {\n            BoxBodyInner::None(none) => none.size(),\n            BoxBodyInner::Bytes(bytes) => bytes.size(),\n            BoxBodyInner::Stream(stream) => stream.size(),\n        }\n    }\n\n    #[inline]\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        match &mut self.0 {\n            BoxBodyInner::None(body) => Pin::new(body).poll_next(cx).map_err(|err| match err {}),\n            BoxBodyInner::Bytes(body) => Pin::new(body).poll_next(cx).map_err(|err| match err {}),\n            BoxBodyInner::Stream(body) => Pin::new(body).poll_next(cx),\n        }\n    }\n\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self> {\n        match self.0 {\n            BoxBodyInner::None(body) => Ok(body.try_into_bytes().unwrap()),\n            BoxBodyInner::Bytes(body) => Ok(body.try_into_bytes().unwrap()),\n            _ => Err(self),\n        }\n    }\n\n    #[inline]\n    fn boxed(self) -> BoxBody {\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n\n    use super::*;\n    use crate::body::to_bytes;\n\n    assert_impl_all!(BoxBody: fmt::Debug, MessageBody, Unpin);\n    assert_not_impl_any!(BoxBody: Send, Sync);\n\n    #[actix_rt::test]\n    async fn nested_boxed_body() {\n        let body = Bytes::from_static(&[1, 2, 3]);\n        let boxed_body = BoxBody::new(BoxBody::new(body));\n\n        assert_eq!(\n            to_bytes(boxed_body).await.unwrap(),\n            Bytes::from(vec![1, 2, 3]),\n        );\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/either.rs",
    "content": "use std::{\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\nuse pin_project_lite::pin_project;\n\nuse super::{BodySize, BoxBody, MessageBody};\nuse crate::Error;\n\npin_project! {\n    /// An \"either\" type specialized for body types.\n    ///\n    /// It is common, in middleware especially, to conditionally return an inner service's unknown/\n    /// generic body `B` type or return early with a new response. This type's \"right\" variant\n    /// defaults to `BoxBody` since error responses are the common case.\n    ///\n    /// For example, middleware will often have `type Response = ServiceResponse<EitherBody<B>>`.\n    /// This means that the inner service's response body type maps to the `Left` variant and the\n    /// middleware's own error responses use the default `Right` variant of `BoxBody`. Of course,\n    /// there's no reason it couldn't use `EitherBody<B, String>` instead if its alternative\n    /// responses have a known type.\n    #[project = EitherBodyProj]\n    #[derive(Debug, Clone)]\n    pub enum EitherBody<L, R = BoxBody> {\n        /// A body of type `L`.\n        Left { #[pin] body: L },\n\n        /// A body of type `R`.\n        Right { #[pin] body: R },\n    }\n}\n\nimpl<L> EitherBody<L, BoxBody> {\n    /// Creates new `EitherBody` left variant with a boxed right variant.\n    ///\n    /// If the expected `R` type will be inferred and is not `BoxBody` then use the\n    /// [`left`](Self::left) constructor instead.\n    #[inline]\n    pub fn new(body: L) -> Self {\n        Self::Left { body }\n    }\n}\n\nimpl<L, R> EitherBody<L, R> {\n    /// Creates new `EitherBody` using left variant.\n    #[inline]\n    pub fn left(body: L) -> Self {\n        Self::Left { body }\n    }\n\n    /// Creates new `EitherBody` using right variant.\n    #[inline]\n    pub fn right(body: R) -> Self {\n        Self::Right { body }\n    }\n}\n\nimpl<L, R> MessageBody for EitherBody<L, R>\nwhere\n    L: MessageBody + 'static,\n    R: MessageBody + 'static,\n{\n    type Error = Error;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        match self {\n            EitherBody::Left { body } => body.size(),\n            EitherBody::Right { body } => body.size(),\n        }\n    }\n\n    #[inline]\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        match self.project() {\n            EitherBodyProj::Left { body } => body\n                .poll_next(cx)\n                .map_err(|err| Error::new_body().with_cause(err)),\n            EitherBodyProj::Right { body } => body\n                .poll_next(cx)\n                .map_err(|err| Error::new_body().with_cause(err)),\n        }\n    }\n\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self> {\n        match self {\n            EitherBody::Left { body } => body\n                .try_into_bytes()\n                .map_err(|body| EitherBody::Left { body }),\n            EitherBody::Right { body } => body\n                .try_into_bytes()\n                .map_err(|body| EitherBody::Right { body }),\n        }\n    }\n\n    #[inline]\n    fn boxed(self) -> BoxBody {\n        match self {\n            EitherBody::Left { body } => body.boxed(),\n            EitherBody::Right { body } => body.boxed(),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn type_parameter_inference() {\n        let _body: EitherBody<(), _> = EitherBody::new(());\n\n        let _body: EitherBody<_, ()> = EitherBody::left(());\n        let _body: EitherBody<(), _> = EitherBody::right(());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/message_body.rs",
    "content": "//! [`MessageBody`] trait and foreign implementations.\n\nuse std::{\n    convert::Infallible,\n    error::Error as StdError,\n    mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::{Bytes, BytesMut};\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\n\nuse super::{BodySize, BoxBody};\n\n/// An interface for types that can be used as a response body.\n///\n/// It is not usually necessary to create custom body types, this trait is already [implemented for\n/// a large number of sensible body types](#foreign-impls) including:\n/// - Empty body: `()`\n/// - Text-based: `String`, `&'static str`, [`ByteString`](https://docs.rs/bytestring/1).\n/// - Byte-based: `Bytes`, `BytesMut`, `Vec<u8>`, `&'static [u8]`;\n/// - Streams: [`BodyStream`](super::BodyStream), [`SizedStream`](super::SizedStream)\n///\n/// # Examples\n/// ```\n/// # use std::convert::Infallible;\n/// # use std::task::{Poll, Context};\n/// # use std::pin::Pin;\n/// # use bytes::Bytes;\n/// # use actix_http::body::{BodySize, MessageBody};\n/// struct Repeat {\n///     chunk: String,\n///     n_times: usize,\n/// }\n///\n/// impl MessageBody for Repeat {\n///     type Error = Infallible;\n///\n///     fn size(&self) -> BodySize {\n///         BodySize::Sized((self.chunk.len() * self.n_times) as u64)\n///     }\n///\n///     fn poll_next(\n///         self: Pin<&mut Self>,\n///         _cx: &mut Context<'_>,\n///     ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n///         let payload_string = self.chunk.repeat(self.n_times);\n///         let payload_bytes = Bytes::from(payload_string);\n///         Poll::Ready(Some(Ok(payload_bytes)))\n///     }\n/// }\n/// ```\npub trait MessageBody {\n    /// The type of error that will be returned if streaming body fails.\n    ///\n    /// Since it is not appropriate to generate a response mid-stream, it only requires `Error` for\n    /// internal use and logging.\n    type Error: Into<Box<dyn StdError>>;\n\n    /// Body size hint.\n    ///\n    /// If [`BodySize::None`] is returned, optimizations that skip reading the body are allowed.\n    fn size(&self) -> BodySize;\n\n    /// Attempt to pull out the next chunk of body bytes.\n    ///\n    /// # Return Value\n    /// Similar to the `Stream` interface, there are several possible return values, each indicating\n    /// a distinct state:\n    /// - `Poll::Pending` means that this body's next chunk is not ready yet. Implementations must\n    ///   ensure that the current task will be notified when the next chunk may be ready.\n    /// - `Poll::Ready(Some(val))` means that the body has successfully produced a chunk, `val`,\n    ///   and may produce further values on subsequent `poll_next` calls.\n    /// - `Poll::Ready(None)` means that the body is complete, and `poll_next` should not be\n    ///   invoked again.\n    ///\n    /// # Panics\n    /// Once a body is complete (i.e., `poll_next` returned `Ready(None)`), calling its `poll_next`\n    /// method again may panic, block forever, or cause other kinds of problems; this trait places\n    /// no requirements on the effects of such a call. However, as the `poll_next` method is not\n    /// marked unsafe, Rust’s usual rules apply: calls must never cause UB, regardless of its state.\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>>;\n\n    /// Try to convert into the complete chunk of body bytes.\n    ///\n    /// Override this method if the complete body can be trivially extracted. This is useful for\n    /// optimizations where `poll_next` calls can be avoided.\n    ///\n    /// Body types with [`BodySize::None`] are allowed to return empty `Bytes`. Although, if calling\n    /// this method, it is recommended to check `size` first and return early.\n    ///\n    /// # Errors\n    /// The default implementation will error and return the original type back to the caller for\n    /// further use.\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self>\n    where\n        Self: Sized,\n    {\n        Err(self)\n    }\n\n    /// Wraps this body into a `BoxBody`.\n    ///\n    /// No-op when called on a `BoxBody`, meaning there is no risk of double boxing when calling\n    /// this on a generic `MessageBody`. Prefer this over [`BoxBody::new`] when a boxed body\n    /// is required.\n    #[inline]\n    fn boxed(self) -> BoxBody\n    where\n        Self: Sized + 'static,\n    {\n        BoxBody::new(self)\n    }\n}\n\nmod foreign_impls {\n    use std::{borrow::Cow, ops::DerefMut};\n\n    use super::*;\n\n    impl<B> MessageBody for &mut B\n    where\n        B: MessageBody + Unpin + ?Sized,\n    {\n        type Error = B::Error;\n\n        fn size(&self) -> BodySize {\n            (**self).size()\n        }\n\n        fn poll_next(\n            mut self: Pin<&mut Self>,\n            cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            Pin::new(&mut **self).poll_next(cx)\n        }\n    }\n\n    impl MessageBody for Infallible {\n        type Error = Infallible;\n\n        fn size(&self) -> BodySize {\n            match *self {}\n        }\n\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            match *self {}\n        }\n    }\n\n    impl MessageBody for () {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(0)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            Poll::Ready(None)\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(Bytes::new())\n        }\n    }\n\n    impl<B> MessageBody for Box<B>\n    where\n        B: MessageBody + Unpin + ?Sized,\n    {\n        type Error = B::Error;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            self.as_ref().size()\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            Pin::new(self.get_mut().as_mut()).poll_next(cx)\n        }\n    }\n\n    impl<T, B> MessageBody for Pin<T>\n    where\n        T: DerefMut<Target = B> + Unpin,\n        B: MessageBody + ?Sized,\n    {\n        type Error = B::Error;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            self.as_ref().size()\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            self.get_mut().as_mut().poll_next(cx)\n        }\n    }\n\n    impl MessageBody for &'static [u8] {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                Poll::Ready(Some(Ok(Bytes::from_static(mem::take(self.get_mut())))))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(Bytes::from_static(self))\n        }\n    }\n\n    impl MessageBody for Bytes {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                Poll::Ready(Some(Ok(mem::take(self.get_mut()))))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(self)\n        }\n    }\n\n    impl MessageBody for BytesMut {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze())))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(self.freeze())\n        }\n    }\n\n    impl MessageBody for Vec<u8> {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                Poll::Ready(Some(Ok(mem::take(self.get_mut()).into())))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(Bytes::from(self))\n        }\n    }\n\n    impl MessageBody for Cow<'static, [u8]> {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                let bytes = match mem::take(self.get_mut()) {\n                    Cow::Borrowed(b) => Bytes::from_static(b),\n                    Cow::Owned(b) => Bytes::from(b),\n                };\n                Poll::Ready(Some(Ok(bytes)))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            match self {\n                Cow::Borrowed(b) => Ok(Bytes::from_static(b)),\n                Cow::Owned(b) => Ok(Bytes::from(b)),\n            }\n        }\n    }\n\n    impl MessageBody for &'static str {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                let string = mem::take(self.get_mut());\n                let bytes = Bytes::from_static(string.as_bytes());\n                Poll::Ready(Some(Ok(bytes)))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(Bytes::from_static(self.as_bytes()))\n        }\n    }\n\n    impl MessageBody for String {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                let string = mem::take(self.get_mut());\n                Poll::Ready(Some(Ok(Bytes::from(string))))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(Bytes::from(self))\n        }\n    }\n\n    impl MessageBody for Cow<'static, str> {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            if self.is_empty() {\n                Poll::Ready(None)\n            } else {\n                let bytes = match mem::take(self.get_mut()) {\n                    Cow::Borrowed(s) => Bytes::from_static(s.as_bytes()),\n                    Cow::Owned(s) => Bytes::from(s.into_bytes()),\n                };\n                Poll::Ready(Some(Ok(bytes)))\n            }\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            match self {\n                Cow::Borrowed(s) => Ok(Bytes::from_static(s.as_bytes())),\n                Cow::Owned(s) => Ok(Bytes::from(s.into_bytes())),\n            }\n        }\n    }\n\n    impl MessageBody for bytestring::ByteString {\n        type Error = Infallible;\n\n        #[inline]\n        fn size(&self) -> BodySize {\n            BodySize::Sized(self.len() as u64)\n        }\n\n        #[inline]\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            let string = mem::take(self.get_mut());\n            Poll::Ready(Some(Ok(string.into_bytes())))\n        }\n\n        #[inline]\n        fn try_into_bytes(self) -> Result<Bytes, Self> {\n            Ok(self.into_bytes())\n        }\n    }\n}\n\npin_project! {\n    pub(crate) struct MessageBodyMapErr<B, F> {\n        #[pin]\n        body: B,\n        mapper: Option<F>,\n    }\n}\n\nimpl<B, F, E> MessageBodyMapErr<B, F>\nwhere\n    B: MessageBody,\n    F: FnOnce(B::Error) -> E,\n{\n    pub(crate) fn new(body: B, mapper: F) -> Self {\n        Self {\n            body,\n            mapper: Some(mapper),\n        }\n    }\n}\n\nimpl<B, F, E> MessageBody for MessageBodyMapErr<B, F>\nwhere\n    B: MessageBody,\n    F: FnOnce(B::Error) -> E,\n    E: Into<Box<dyn StdError>>,\n{\n    type Error = E;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        self.body.size()\n    }\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        let this = self.as_mut().project();\n\n        match ready!(this.body.poll_next(cx)) {\n            Some(Err(err)) => {\n                let f = self.as_mut().project().mapper.take().unwrap();\n                let mapped_err = (f)(err);\n                Poll::Ready(Some(Err(mapped_err)))\n            }\n            Some(Ok(val)) => Poll::Ready(Some(Ok(val))),\n            None => Poll::Ready(None),\n        }\n    }\n\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self> {\n        let Self { body, mapper } = self;\n        body.try_into_bytes().map_err(|body| Self { body, mapper })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_rt::pin;\n    use actix_utils::future::poll_fn;\n    use futures_util::stream;\n\n    use super::*;\n    use crate::body::{self, EitherBody};\n\n    macro_rules! assert_poll_next {\n        ($pin:expr, $exp:expr) => {\n            assert_eq!(\n                poll_fn(|cx| $pin.as_mut().poll_next(cx))\n                    .await\n                    .unwrap() // unwrap option\n                    .unwrap(), // unwrap result\n                $exp\n            );\n        };\n    }\n\n    macro_rules! assert_poll_next_none {\n        ($pin:expr) => {\n            assert!(poll_fn(|cx| $pin.as_mut().poll_next(cx)).await.is_none());\n        };\n    }\n\n    #[allow(unused_allocation)] // triggered by `Box::new(()).size()`\n    #[actix_rt::test]\n    async fn boxing_equivalence() {\n        assert_eq!(().size(), BodySize::Sized(0));\n        assert_eq!(().size(), Box::new(()).size());\n        assert_eq!(().size(), Box::pin(()).size());\n\n        let pl = Box::new(());\n        pin!(pl);\n        assert_poll_next_none!(pl);\n\n        let mut pl = Box::pin(());\n        assert_poll_next_none!(pl);\n    }\n\n    #[actix_rt::test]\n    async fn mut_equivalence() {\n        assert_eq!(().size(), BodySize::Sized(0));\n        assert_eq!(().size(), (&(&mut ())).size());\n\n        let pl = &mut ();\n        pin!(pl);\n        assert_poll_next_none!(pl);\n\n        let pl = &mut Box::new(());\n        pin!(pl);\n        assert_poll_next_none!(pl);\n\n        let mut body = body::SizedStream::new(\n            8,\n            stream::iter([\n                Ok::<_, std::io::Error>(Bytes::from(\"1234\")),\n                Ok(Bytes::from(\"5678\")),\n            ]),\n        );\n        let body = &mut body;\n        assert_eq!(body.size(), BodySize::Sized(8));\n        pin!(body);\n        assert_poll_next!(body, Bytes::from_static(b\"1234\"));\n        assert_poll_next!(body, Bytes::from_static(b\"5678\"));\n        assert_poll_next_none!(body);\n    }\n\n    #[allow(clippy::let_unit_value)]\n    #[actix_rt::test]\n    async fn test_unit() {\n        let pl = ();\n        assert_eq!(pl.size(), BodySize::Sized(0));\n        pin!(pl);\n        assert_poll_next_none!(pl);\n    }\n\n    #[actix_rt::test]\n    async fn test_static_str() {\n        assert_eq!(\"\".size(), BodySize::Sized(0));\n        assert_eq!(\"test\".size(), BodySize::Sized(4));\n\n        let pl = \"test\";\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_static_bytes() {\n        assert_eq!(b\"\".as_ref().size(), BodySize::Sized(0));\n        assert_eq!(b\"test\".as_ref().size(), BodySize::Sized(4));\n\n        let pl = b\"test\".as_ref();\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_vec() {\n        assert_eq!(vec![0; 0].size(), BodySize::Sized(0));\n        assert_eq!(Vec::from(\"test\").size(), BodySize::Sized(4));\n\n        let pl = Vec::from(\"test\");\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_bytes() {\n        assert_eq!(Bytes::new().size(), BodySize::Sized(0));\n        assert_eq!(Bytes::from_static(b\"test\").size(), BodySize::Sized(4));\n\n        let pl = Bytes::from_static(b\"test\");\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_bytes_mut() {\n        assert_eq!(BytesMut::new().size(), BodySize::Sized(0));\n        assert_eq!(BytesMut::from(b\"test\".as_ref()).size(), BodySize::Sized(4));\n\n        let pl = BytesMut::from(\"test\");\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_string() {\n        assert_eq!(String::new().size(), BodySize::Sized(0));\n        assert_eq!(\"test\".to_owned().size(), BodySize::Sized(4));\n\n        let pl = \"test\".to_owned();\n        pin!(pl);\n        assert_poll_next!(pl, Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn complete_body_combinators() {\n        let body = Bytes::from_static(b\"test\");\n        let body = BoxBody::new(body);\n        let body = EitherBody::<_, ()>::left(body);\n        let body = EitherBody::<(), _>::right(body);\n        // Do not support try_into_bytes:\n        // let body = Box::new(body);\n        // let body = Box::pin(body);\n\n        assert_eq!(body.try_into_bytes().unwrap(), Bytes::from(\"test\"));\n    }\n\n    #[actix_rt::test]\n    async fn complete_body_combinators_poll() {\n        let body = Bytes::from_static(b\"test\");\n        let body = BoxBody::new(body);\n        let body = EitherBody::<_, ()>::left(body);\n        let body = EitherBody::<(), _>::right(body);\n        let mut body = body;\n\n        assert_eq!(body.size(), BodySize::Sized(4));\n        assert_poll_next!(Pin::new(&mut body), Bytes::from(\"test\"));\n        assert_poll_next_none!(Pin::new(&mut body));\n    }\n\n    #[actix_rt::test]\n    async fn none_body_combinators() {\n        fn none_body() -> BoxBody {\n            let body = body::None;\n            let body = BoxBody::new(body);\n            let body = EitherBody::<_, ()>::left(body);\n            let body = EitherBody::<(), _>::right(body);\n            body.boxed()\n        }\n\n        assert_eq!(none_body().size(), BodySize::None);\n        assert_eq!(none_body().try_into_bytes().unwrap(), Bytes::new());\n        assert_poll_next_none!(Pin::new(&mut none_body()));\n    }\n\n    // down-casting used to be done with a method on MessageBody trait\n    // test is kept to demonstrate equivalence of Any trait\n    #[actix_rt::test]\n    async fn test_body_casting() {\n        let mut body = String::from(\"hello cast\");\n        // let mut resp_body: &mut dyn MessageBody<Error = Error> = &mut body;\n        let resp_body: &mut dyn std::any::Any = &mut body;\n        let body = resp_body.downcast_ref::<String>().unwrap();\n        assert_eq!(body, \"hello cast\");\n        let body = &mut resp_body.downcast_mut::<String>().unwrap();\n        body.push('!');\n        let body = resp_body.downcast_ref::<String>().unwrap();\n        assert_eq!(body, \"hello cast!\");\n        let not_body = resp_body.downcast_ref::<()>();\n        assert!(not_body.is_none());\n    }\n\n    #[actix_rt::test]\n    async fn non_owning_to_bytes() {\n        let mut body = BoxBody::new(());\n        let bytes = body::to_bytes(&mut body).await.unwrap();\n        assert_eq!(bytes, Bytes::new());\n\n        let mut body = body::BodyStream::new(stream::iter([\n            Ok::<_, std::io::Error>(Bytes::from(\"1234\")),\n            Ok(Bytes::from(\"5678\")),\n        ]));\n        let bytes = body::to_bytes(&mut body).await.unwrap();\n        assert_eq!(bytes, Bytes::from_static(b\"12345678\"));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/mod.rs",
    "content": "//! Traits and structures to aid consuming and writing HTTP payloads.\n//!\n//! \"Body\" and \"payload\" are used somewhat interchangeably in this documentation.\n\n// Though the spec kinda reads like \"payload\" is the possibly-transfer-encoded part of the message\n// and the \"body\" is the intended possibly-decoded version of that.\n\nmod body_stream;\nmod boxed;\nmod either;\nmod message_body;\nmod none;\nmod size;\nmod sized_stream;\nmod utils;\n\npub(crate) use self::message_body::MessageBodyMapErr;\npub use self::{\n    body_stream::BodyStream,\n    boxed::BoxBody,\n    either::EitherBody,\n    message_body::MessageBody,\n    none::None,\n    size::BodySize,\n    sized_stream::SizedStream,\n    utils::{to_bytes, to_bytes_limited, BodyLimitExceeded},\n};\n"
  },
  {
    "path": "actix-http/src/body/none.rs",
    "content": "use std::{\n    convert::Infallible,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\n\nuse super::{BodySize, MessageBody};\n\n/// Body type for responses that forbid payloads.\n///\n/// This is distinct from an \"empty\" response which _would_ contain a `Content-Length` header.\n/// For an \"empty\" body, use `()` or `Bytes::new()`.\n///\n/// For example, the HTTP spec forbids a payload to be sent with a `204 No Content` response.\n/// In this case, the payload (or lack thereof) is implicit from the status code, so a\n/// `Content-Length` header is not required.\n#[derive(Debug, Clone, Copy, Default)]\n#[non_exhaustive]\npub struct None;\n\nimpl None {\n    /// Constructs new \"none\" body.\n    #[inline]\n    pub fn new() -> Self {\n        None\n    }\n}\n\nimpl MessageBody for None {\n    type Error = Infallible;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        BodySize::None\n    }\n\n    #[inline]\n    fn poll_next(\n        self: Pin<&mut Self>,\n        _cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        Poll::Ready(Option::None)\n    }\n\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self> {\n        Ok(Bytes::new())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/size.rs",
    "content": "/// Body size hint.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum BodySize {\n    /// Implicitly empty body.\n    ///\n    /// Will omit the Content-Length header. Used for responses to certain methods (e.g., `HEAD`) or\n    /// with particular status codes (e.g., 204 No Content). Consumers that read this as a body size\n    /// hint are allowed to make optimizations that skip reading or writing the payload.\n    None,\n\n    /// Known size body.\n    ///\n    /// Will write `Content-Length: N` header.\n    Sized(u64),\n\n    /// Unknown size body.\n    ///\n    /// Will not write Content-Length header. Can be used with chunked Transfer-Encoding.\n    Stream,\n}\n\nimpl BodySize {\n    /// Equivalent to `BodySize::Sized(0)`;\n    pub const ZERO: Self = Self::Sized(0);\n\n    /// Returns true if size hint indicates omitted or empty body.\n    ///\n    /// Streams will return false because it cannot be known without reading the stream.\n    ///\n    /// ```\n    /// # use actix_http::body::BodySize;\n    /// assert!(BodySize::None.is_eof());\n    /// assert!(BodySize::Sized(0).is_eof());\n    ///\n    /// assert!(!BodySize::Sized(64).is_eof());\n    /// assert!(!BodySize::Stream.is_eof());\n    /// ```\n    pub fn is_eof(&self) -> bool {\n        matches!(self, BodySize::None | BodySize::Sized(0))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/sized_stream.rs",
    "content": "use std::{\n    error::Error as StdError,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\nuse futures_core::{ready, Stream};\nuse pin_project_lite::pin_project;\n\nuse super::{BodySize, MessageBody};\n\npin_project! {\n    /// Known sized streaming response wrapper.\n    ///\n    /// This body implementation should be used if total size of stream is known. Data is sent as-is\n    /// without using chunked transfer encoding.\n    pub struct SizedStream<S> {\n        size: u64,\n        #[pin]\n        stream: S,\n    }\n}\n\nimpl<S, E> SizedStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, E>>,\n    E: Into<Box<dyn StdError>> + 'static,\n{\n    #[inline]\n    pub fn new(size: u64, stream: S) -> Self {\n        SizedStream { size, stream }\n    }\n}\n\n// TODO: from_infallible method\n\nimpl<S, E> MessageBody for SizedStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, E>>,\n    E: Into<Box<dyn StdError>> + 'static,\n{\n    type Error = E;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        BodySize::Sized(self.size)\n    }\n\n    /// Attempts to pull out the next value of the underlying [`Stream`].\n    ///\n    /// Empty values are skipped to prevent [`SizedStream`]'s transmission being\n    /// ended on a zero-length chunk, but rather proceed until the underlying\n    /// [`Stream`] ends.\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        loop {\n            let stream = self.as_mut().project().stream;\n\n            let chunk = match ready!(stream.poll_next(cx)) {\n                Some(Ok(ref bytes)) if bytes.is_empty() => continue,\n                val => val,\n            };\n\n            return Poll::Ready(chunk);\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::convert::Infallible;\n\n    use actix_rt::pin;\n    use actix_utils::future::poll_fn;\n    use futures_util::stream;\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n\n    use super::*;\n    use crate::body::to_bytes;\n\n    assert_impl_all!(SizedStream<stream::Empty<Result<Bytes, crate::Error>>>: MessageBody);\n    assert_impl_all!(SizedStream<stream::Empty<Result<Bytes, &'static str>>>: MessageBody);\n    assert_impl_all!(SizedStream<stream::Repeat<Result<Bytes, &'static str>>>: MessageBody);\n    assert_impl_all!(SizedStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);\n    assert_impl_all!(SizedStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);\n\n    assert_not_impl_any!(SizedStream<stream::Empty<Bytes>>: MessageBody);\n    assert_not_impl_any!(SizedStream<stream::Repeat<Bytes>>: MessageBody);\n    // crate::Error is not Clone\n    assert_not_impl_any!(SizedStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);\n\n    #[actix_rt::test]\n    async fn skips_empty_chunks() {\n        let body = SizedStream::new(\n            2,\n            stream::iter(\n                [\"1\", \"\", \"2\"]\n                    .iter()\n                    .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),\n            ),\n        );\n\n        pin!(body);\n\n        assert_eq!(\n            poll_fn(|cx| body.as_mut().poll_next(cx))\n                .await\n                .unwrap()\n                .ok(),\n            Some(Bytes::from(\"1\")),\n        );\n\n        assert_eq!(\n            poll_fn(|cx| body.as_mut().poll_next(cx))\n                .await\n                .unwrap()\n                .ok(),\n            Some(Bytes::from(\"2\")),\n        );\n    }\n\n    #[actix_rt::test]\n    async fn read_to_bytes() {\n        let body = SizedStream::new(\n            2,\n            stream::iter(\n                [\"1\", \"\", \"2\"]\n                    .iter()\n                    .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),\n            ),\n        );\n\n        assert_eq!(to_bytes(body).await.ok(), Some(Bytes::from(\"12\")));\n    }\n\n    #[actix_rt::test]\n    async fn stream_string_error() {\n        // `&'static str` does not impl `Error`\n        // but it does impl `Into<Box<dyn Error>>`\n\n        let body = SizedStream::new(0, stream::once(async { Err(\"stringy error\") }));\n        assert_eq!(to_bytes(body).await, Ok(Bytes::new()));\n\n        let body = SizedStream::new(1, stream::once(async { Err(\"stringy error\") }));\n        assert!(matches!(to_bytes(body).await, Err(\"stringy error\")));\n    }\n\n    #[actix_rt::test]\n    async fn stream_boxed_error() {\n        // `Box<dyn Error>` does not impl `Error`\n        // but it does impl `Into<Box<dyn Error>>`\n\n        let body = SizedStream::new(\n            0,\n            stream::once(async { Err(Box::<dyn StdError>::from(\"stringy error\")) }),\n        );\n        assert_eq!(to_bytes(body).await.unwrap(), Bytes::new());\n\n        let body = SizedStream::new(\n            1,\n            stream::once(async { Err(Box::<dyn StdError>::from(\"stringy error\")) }),\n        );\n        assert_eq!(\n            to_bytes(body).await.unwrap_err().to_string(),\n            \"stringy error\"\n        );\n    }\n}\n"
  },
  {
    "path": "actix-http/src/body/utils.rs",
    "content": "use std::task::Poll;\n\nuse actix_rt::pin;\nuse actix_utils::future::poll_fn;\nuse bytes::{Bytes, BytesMut};\nuse derive_more::{Display, Error};\nuse futures_core::ready;\n\nuse super::{BodySize, MessageBody};\n\n/// Collects all the bytes produced by `body`.\n///\n/// Any errors produced by the body stream are returned immediately.\n///\n/// Consider using [`to_bytes_limited`] instead to protect against memory exhaustion.\n///\n/// # Examples\n///\n/// ```\n/// use actix_http::body::{self, to_bytes};\n/// use bytes::Bytes;\n///\n/// # actix_rt::System::new().block_on(async {\n/// let body = body::None::new();\n/// let bytes = to_bytes(body).await.unwrap();\n/// assert!(bytes.is_empty());\n///\n/// let body = Bytes::from_static(b\"123\");\n/// let bytes = to_bytes(body).await.unwrap();\n/// assert_eq!(bytes, \"123\");\n/// # });\n/// ```\npub async fn to_bytes<B: MessageBody>(body: B) -> Result<Bytes, B::Error> {\n    to_bytes_limited(body, usize::MAX)\n        .await\n        .expect(\"body should never yield more than usize::MAX bytes\")\n}\n\n/// Error type returned from [`to_bytes_limited`] when body produced exceeds limit.\n#[derive(Debug, Display, Error)]\n#[display(\"limit exceeded while collecting body bytes\")]\n#[non_exhaustive]\npub struct BodyLimitExceeded;\n\n/// Collects the bytes produced by `body`, up to `limit` bytes.\n///\n/// If a chunk read from `poll_next` causes the total number of bytes read to exceed `limit`, an\n/// `Err(BodyLimitExceeded)` is returned.\n///\n/// Any errors produced by the body stream are returned immediately as `Ok(Err(B::Error))`.\n///\n/// # Examples\n///\n/// ```\n/// use actix_http::body::{self, to_bytes_limited};\n/// use bytes::Bytes;\n///\n/// # actix_rt::System::new().block_on(async {\n/// let body = body::None::new();\n/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();\n/// assert!(bytes.is_empty());\n///\n/// let body = Bytes::from_static(b\"123\");\n/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();\n/// assert_eq!(bytes, \"123\");\n///\n/// let body = Bytes::from_static(b\"123\");\n/// assert!(to_bytes_limited(body, 2).await.is_err());\n/// # });\n/// ```\npub async fn to_bytes_limited<B: MessageBody>(\n    body: B,\n    limit: usize,\n) -> Result<Result<Bytes, B::Error>, BodyLimitExceeded> {\n    /// Sensible default (32kB) for initial, bounded allocation when collecting body bytes.\n    const INITIAL_ALLOC_BYTES: usize = 32 * 1024;\n\n    let cap = match body.size() {\n        BodySize::None | BodySize::Sized(0) => return Ok(Ok(Bytes::new())),\n        BodySize::Sized(size) if size as usize > limit => return Err(BodyLimitExceeded),\n        BodySize::Sized(size) => (size as usize).min(INITIAL_ALLOC_BYTES),\n        BodySize::Stream => INITIAL_ALLOC_BYTES,\n    };\n\n    let mut exceeded_limit = false;\n    let mut buf = BytesMut::with_capacity(cap);\n\n    pin!(body);\n\n    match poll_fn(|cx| loop {\n        let body = body.as_mut();\n\n        match ready!(body.poll_next(cx)) {\n            Some(Ok(bytes)) => {\n                // if limit is exceeded...\n                if buf.len() + bytes.len() > limit {\n                    // ...set flag to true and break out of poll_fn\n                    exceeded_limit = true;\n                    return Poll::Ready(Ok(()));\n                }\n\n                buf.extend_from_slice(&bytes)\n            }\n            None => return Poll::Ready(Ok(())),\n            Some(Err(err)) => return Poll::Ready(Err(err)),\n        }\n    })\n    .await\n    {\n        // propagate error returned from body poll\n        Err(err) => Ok(Err(err)),\n\n        // limit was exceeded while reading body\n        Ok(()) if exceeded_limit => Err(BodyLimitExceeded),\n\n        // otherwise return body buffer\n        Ok(()) => Ok(Ok(buf.freeze())),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io;\n\n    use futures_util::{stream, StreamExt as _};\n\n    use super::*;\n    use crate::{\n        body::{BodyStream, SizedStream},\n        Error,\n    };\n\n    #[actix_rt::test]\n    async fn to_bytes_complete() {\n        let bytes = to_bytes(()).await.unwrap();\n        assert!(bytes.is_empty());\n\n        let body = Bytes::from_static(b\"123\");\n        let bytes = to_bytes(body).await.unwrap();\n        assert_eq!(bytes, b\"123\"[..]);\n    }\n\n    #[actix_rt::test]\n    async fn to_bytes_streams() {\n        let stream = stream::iter(vec![Bytes::from_static(b\"123\"), Bytes::from_static(b\"abc\")])\n            .map(Ok::<_, Error>);\n        let body = BodyStream::new(stream);\n        let bytes = to_bytes(body).await.unwrap();\n        assert_eq!(bytes, b\"123abc\"[..]);\n    }\n\n    #[actix_rt::test]\n    async fn to_bytes_limited_complete() {\n        let bytes = to_bytes_limited((), 0).await.unwrap().unwrap();\n        assert!(bytes.is_empty());\n\n        let bytes = to_bytes_limited((), 1).await.unwrap().unwrap();\n        assert!(bytes.is_empty());\n\n        assert!(to_bytes_limited(Bytes::from_static(b\"12\"), 0)\n            .await\n            .is_err());\n        assert!(to_bytes_limited(Bytes::from_static(b\"12\"), 1)\n            .await\n            .is_err());\n        assert!(to_bytes_limited(Bytes::from_static(b\"12\"), 2).await.is_ok());\n        assert!(to_bytes_limited(Bytes::from_static(b\"12\"), 3).await.is_ok());\n    }\n\n    #[actix_rt::test]\n    async fn to_bytes_limited_streams() {\n        // hinting a larger body fails\n        let body = SizedStream::new(8, stream::empty().map(Ok::<_, Error>));\n        assert!(to_bytes_limited(body, 3).await.is_err());\n\n        // hinting a smaller body is okay\n        let body = SizedStream::new(3, stream::empty().map(Ok::<_, Error>));\n        assert!(to_bytes_limited(body, 3).await.unwrap().unwrap().is_empty());\n\n        // hinting a smaller body then returning a larger one fails\n        let stream = stream::iter(vec![Bytes::from_static(b\"1234\")]).map(Ok::<_, Error>);\n        let body = SizedStream::new(3, stream);\n        assert!(to_bytes_limited(body, 3).await.is_err());\n\n        let stream = stream::iter(vec![Bytes::from_static(b\"123\"), Bytes::from_static(b\"abc\")])\n            .map(Ok::<_, Error>);\n        let body = BodyStream::new(stream);\n        assert!(to_bytes_limited(body, 3).await.is_err());\n    }\n\n    #[actix_rt::test]\n    async fn to_body_limit_error() {\n        let err_stream = stream::once(async { Err(io::Error::other(\"\")) });\n        let body = SizedStream::new(8, err_stream);\n        // not too big, but propagates error from body stream\n        assert!(to_bytes_limited(body, 10).await.unwrap().is_err());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/builder.rs",
    "content": "use std::{fmt, marker::PhantomData, net, rc::Rc, time::Duration};\n\nuse actix_codec::Framed;\nuse actix_service::{IntoServiceFactory, Service, ServiceFactory};\n\nuse crate::{\n    body::{BoxBody, MessageBody},\n    config::{DEFAULT_H2_CONN_WINDOW_SIZE, DEFAULT_H2_STREAM_WINDOW_SIZE},\n    h1::{self, ExpectHandler, H1Service, UpgradeHandler},\n    service::HttpService,\n    ConnectCallback, Extensions, KeepAlive, Request, Response, ServiceConfigBuilder,\n};\n\n/// An HTTP service builder.\n///\n/// This type can construct an instance of [`HttpService`] through a builder-like pattern.\npub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler> {\n    keep_alive: KeepAlive,\n    client_request_timeout: Duration,\n    client_disconnect_timeout: Duration,\n    tcp_nodelay: Option<bool>,\n    secure: bool,\n    local_addr: Option<net::SocketAddr>,\n    h1_allow_half_closed: bool,\n    h2_conn_window_size: u32,\n    h2_stream_window_size: u32,\n    expect: X,\n    upgrade: Option<U>,\n    on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<S>,\n}\n\nimpl<T, S> Default for HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    <S::Service as Service<Request>>::Future: 'static,\n{\n    fn default() -> Self {\n        HttpServiceBuilder {\n            // ServiceConfig parts (make sure defaults match)\n            keep_alive: KeepAlive::default(),\n            client_request_timeout: Duration::from_secs(5),\n            client_disconnect_timeout: Duration::ZERO,\n            tcp_nodelay: None,\n            secure: false,\n            local_addr: None,\n            h1_allow_half_closed: true,\n            h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,\n            h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,\n\n            // dispatcher parts\n            expect: ExpectHandler,\n            upgrade: None,\n            on_connect_ext: None,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<T, S, X, U> HttpServiceBuilder<T, S, X, U>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    <S::Service as Service<Request>>::Future: 'static,\n    X: ServiceFactory<Request, Config = (), Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n    X::InitError: fmt::Debug,\n    U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,\n    U::Error: fmt::Display,\n    U::InitError: fmt::Debug,\n{\n    /// Set connection keep-alive setting.\n    ///\n    /// Applies to HTTP/1.1 keep-alive and HTTP/2 ping-pong.\n    ///\n    /// By default keep-alive is 5 seconds.\n    pub fn keep_alive<W: Into<KeepAlive>>(mut self, val: W) -> Self {\n        self.keep_alive = val.into();\n        self\n    }\n\n    /// Set connection secure state\n    pub fn secure(mut self) -> Self {\n        self.secure = true;\n        self\n    }\n\n    /// Set the local address that this service is bound to.\n    pub fn local_addr(mut self, addr: net::SocketAddr) -> Self {\n        self.local_addr = Some(addr);\n        self\n    }\n\n    /// Set client request timeout (for first request).\n    ///\n    /// Defines a timeout for reading client request header. If the client does not transmit the\n    /// request head within this duration, the connection is terminated with a `408 Request Timeout`\n    /// response error.\n    ///\n    /// A duration of zero disables the timeout.\n    ///\n    /// By default, the client timeout is 5 seconds.\n    pub fn client_request_timeout(mut self, dur: Duration) -> Self {\n        self.client_request_timeout = dur;\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"3.0.0\", note = \"Renamed to `client_request_timeout`.\")]\n    pub fn client_timeout(self, dur: Duration) -> Self {\n        self.client_request_timeout(dur)\n    }\n\n    /// Set client connection disconnect timeout.\n    ///\n    /// Defines a timeout for disconnect connection. If a disconnect procedure does not complete\n    /// within this time, the request get dropped. This timeout affects secure connections.\n    ///\n    /// A duration of zero disables the timeout.\n    ///\n    /// By default, the disconnect timeout is disabled.\n    pub fn client_disconnect_timeout(mut self, dur: Duration) -> Self {\n        self.client_disconnect_timeout = dur;\n        self\n    }\n\n    /// Sets `TCP_NODELAY` value on accepted TCP connections.\n    pub fn tcp_nodelay(mut self, nodelay: bool) -> Self {\n        self.tcp_nodelay = Some(nodelay);\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"3.0.0\", note = \"Renamed to `client_disconnect_timeout`.\")]\n    pub fn client_disconnect(self, dur: Duration) -> Self {\n        self.client_disconnect_timeout(dur)\n    }\n\n    /// Sets whether HTTP/1 connections should support half-closures.\n    ///\n    /// Clients can choose to shutdown their writer-side of the connection after completing their\n    /// request and while waiting for the server response. Setting this to `false` will cause the\n    /// server to abort the connection handling as soon as it detects an EOF from the client.\n    ///\n    /// The default behavior is to allow, i.e. `true`\n    pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {\n        self.h1_allow_half_closed = allow;\n        self\n    }\n\n    /// Sets initial stream-level flow control window size for HTTP/2 connections.\n    ///\n    /// See [`ServiceConfigBuilder::h2_initial_window_size`] for more details.\n    pub fn h2_initial_window_size(mut self, size: u32) -> Self {\n        self.h2_stream_window_size = size;\n        self\n    }\n\n    /// Sets initial connection-level flow control window size for HTTP/2 connections.\n    ///\n    /// See [`ServiceConfigBuilder::h2_initial_connection_window_size`] for more details.\n    pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {\n        self.h2_conn_window_size = size;\n        self\n    }\n\n    /// Provide service for `EXPECT: 100-Continue` support.\n    ///\n    /// Service get called with request that contains `EXPECT` header.\n    /// Service must return request in case of success, in that case\n    /// request will be forwarded to main service.\n    pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>\n    where\n        F: IntoServiceFactory<X1, Request>,\n        X1: ServiceFactory<Request, Config = (), Response = Request>,\n        X1::Error: Into<Response<BoxBody>>,\n        X1::InitError: fmt::Debug,\n    {\n        HttpServiceBuilder {\n            keep_alive: self.keep_alive,\n            client_request_timeout: self.client_request_timeout,\n            client_disconnect_timeout: self.client_disconnect_timeout,\n            tcp_nodelay: self.tcp_nodelay,\n            secure: self.secure,\n            local_addr: self.local_addr,\n            h1_allow_half_closed: self.h1_allow_half_closed,\n            h2_conn_window_size: self.h2_conn_window_size,\n            h2_stream_window_size: self.h2_stream_window_size,\n            expect: expect.into_factory(),\n            upgrade: self.upgrade,\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Provide service for custom `Connection: UPGRADE` support.\n    ///\n    /// If service is provided then normal requests handling get halted\n    /// and this service get called with original request and framed object.\n    pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1>\n    where\n        F: IntoServiceFactory<U1, (Request, Framed<T, h1::Codec>)>,\n        U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,\n        U1::Error: fmt::Display,\n        U1::InitError: fmt::Debug,\n    {\n        HttpServiceBuilder {\n            keep_alive: self.keep_alive,\n            client_request_timeout: self.client_request_timeout,\n            client_disconnect_timeout: self.client_disconnect_timeout,\n            tcp_nodelay: self.tcp_nodelay,\n            secure: self.secure,\n            local_addr: self.local_addr,\n            h1_allow_half_closed: self.h1_allow_half_closed,\n            h2_conn_window_size: self.h2_conn_window_size,\n            h2_stream_window_size: self.h2_stream_window_size,\n            expect: self.expect,\n            upgrade: Some(upgrade.into_factory()),\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Sets the callback to be run on connection establishment.\n    ///\n    /// Has mutable access to a data container that will be merged into request extensions.\n    /// This enables transport layer data (like client certificates) to be accessed in middleware\n    /// and handlers.\n    pub fn on_connect_ext<F>(mut self, f: F) -> Self\n    where\n        F: Fn(&T, &mut Extensions) + 'static,\n    {\n        self.on_connect_ext = Some(Rc::new(f));\n        self\n    }\n\n    /// Finish service configuration and create a service for the HTTP/1 protocol.\n    pub fn h1<F, B>(self, service: F) -> H1Service<T, S, B, X, U>\n    where\n        B: MessageBody,\n        F: IntoServiceFactory<S, Request>,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n    {\n        let cfg = ServiceConfigBuilder::new()\n            .keep_alive(self.keep_alive)\n            .client_request_timeout(self.client_request_timeout)\n            .client_disconnect_timeout(self.client_disconnect_timeout)\n            .tcp_nodelay(self.tcp_nodelay)\n            .secure(self.secure)\n            .local_addr(self.local_addr)\n            .h1_allow_half_closed(self.h1_allow_half_closed)\n            .h2_initial_window_size(self.h2_stream_window_size)\n            .h2_initial_connection_window_size(self.h2_conn_window_size)\n            .build();\n\n        H1Service::with_config(cfg, service.into_factory())\n            .expect(self.expect)\n            .upgrade(self.upgrade)\n            .on_connect_ext(self.on_connect_ext)\n    }\n\n    /// Finish service configuration and create a service for the HTTP/2 protocol.\n    #[cfg(feature = \"http2\")]\n    pub fn h2<F, B>(self, service: F) -> crate::h2::H2Service<T, S, B>\n    where\n        F: IntoServiceFactory<S, Request>,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n\n        B: MessageBody + 'static,\n    {\n        let cfg = ServiceConfigBuilder::new()\n            .keep_alive(self.keep_alive)\n            .client_request_timeout(self.client_request_timeout)\n            .client_disconnect_timeout(self.client_disconnect_timeout)\n            .tcp_nodelay(self.tcp_nodelay)\n            .secure(self.secure)\n            .local_addr(self.local_addr)\n            .h1_allow_half_closed(self.h1_allow_half_closed)\n            .h2_initial_window_size(self.h2_stream_window_size)\n            .h2_initial_connection_window_size(self.h2_conn_window_size)\n            .build();\n\n        crate::h2::H2Service::with_config(cfg, service.into_factory())\n            .on_connect_ext(self.on_connect_ext)\n    }\n\n    /// Finish service configuration and create `HttpService` instance.\n    pub fn finish<F, B>(self, service: F) -> HttpService<T, S, B, X, U>\n    where\n        F: IntoServiceFactory<S, Request>,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n\n        B: MessageBody + 'static,\n    {\n        let cfg = ServiceConfigBuilder::new()\n            .keep_alive(self.keep_alive)\n            .client_request_timeout(self.client_request_timeout)\n            .client_disconnect_timeout(self.client_disconnect_timeout)\n            .tcp_nodelay(self.tcp_nodelay)\n            .secure(self.secure)\n            .local_addr(self.local_addr)\n            .h1_allow_half_closed(self.h1_allow_half_closed)\n            .h2_initial_window_size(self.h2_stream_window_size)\n            .h2_initial_connection_window_size(self.h2_conn_window_size)\n            .build();\n\n        HttpService::with_config(cfg, service.into_factory())\n            .expect(self.expect)\n            .upgrade(self.upgrade)\n            .on_connect_ext(self.on_connect_ext)\n    }\n}\n"
  },
  {
    "path": "actix-http/src/config.rs",
    "content": "use std::{\n    net::SocketAddr,\n    rc::Rc,\n    time::{Duration, Instant},\n};\n\nuse bytes::BytesMut;\n\nuse crate::{date::DateService, KeepAlive};\n\n/// Default HTTP/2 initial connection-level flow control window size.\n///\n/// Matches awc's defaults to avoid poor throughput on high-BDP links.\npub(crate) const DEFAULT_H2_CONN_WINDOW_SIZE: u32 = 1024 * 1024 * 2; // 2MiB\n\n/// Default HTTP/2 initial stream-level flow control window size.\n///\n/// Matches awc's defaults to avoid poor throughput on high-BDP links.\npub(crate) const DEFAULT_H2_STREAM_WINDOW_SIZE: u32 = 1024 * 1024; // 1MiB\n\n/// A builder for creating a [`ServiceConfig`]\n#[derive(Default, Debug)]\npub struct ServiceConfigBuilder {\n    inner: Inner,\n}\n\nimpl ServiceConfigBuilder {\n    /// Creates a new, default, [`ServiceConfigBuilder`]\n    ///\n    /// It uses the following default values:\n    ///\n    /// - [`KeepAlive::default`] for the connection keep-alive setting\n    /// - 5 seconds for the client request timeout\n    /// - 0 seconds for the client shutdown timeout\n    /// - secure value of `false`\n    /// - [`None`] for the local address setting\n    /// - Allow for half closed HTTP/1 connections\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Sets the `secure` attribute for this configuration\n    pub fn secure(mut self, secure: bool) -> Self {\n        self.inner.secure = secure;\n        self\n    }\n\n    /// Sets the local address for this configuration\n    pub fn local_addr(mut self, local_addr: Option<SocketAddr>) -> Self {\n        self.inner.local_addr = local_addr;\n        self\n    }\n\n    /// Sets connection keep-alive setting\n    pub fn keep_alive(mut self, keep_alive: KeepAlive) -> Self {\n        self.inner.keep_alive = keep_alive;\n        self\n    }\n\n    /// Sets the timeout for the client to finish sending the head of its first request\n    pub fn client_request_timeout(mut self, timeout: Duration) -> Self {\n        self.inner.client_request_timeout = timeout;\n        self\n    }\n\n    /// Sets the timeout for cleanly disconnecting from the client after connection shutdown has\n    /// started\n    pub fn client_disconnect_timeout(mut self, timeout: Duration) -> Self {\n        self.inner.client_disconnect_timeout = timeout;\n        self\n    }\n\n    /// Sets `TCP_NODELAY` preference for accepted TCP connections.\n    pub fn tcp_nodelay(mut self, nodelay: Option<bool>) -> Self {\n        self.inner.tcp_nodelay = nodelay;\n        self\n    }\n\n    /// Sets whether HTTP/1 connections should support half-closures.\n    ///\n    /// Clients can choose to shutdown their writer-side of the connection after completing their\n    /// request and while waiting for the server response. Setting this to `false` will cause the\n    /// server to abort the connection handling as soon as it detects an EOF from the client\n    pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {\n        self.inner.h1_allow_half_closed = allow;\n        self\n    }\n\n    /// Sets initial stream-level flow control window size for HTTP/2 connections.\n    ///\n    /// Higher values can improve upload performance on high-latency links at the cost of higher\n    /// worst-case memory usage per connection.\n    ///\n    /// The default value is 1MiB.\n    pub fn h2_initial_window_size(mut self, size: u32) -> Self {\n        self.inner.h2_stream_window_size = size;\n        self\n    }\n\n    /// Sets initial connection-level flow control window size for HTTP/2 connections.\n    ///\n    /// Higher values can improve upload performance on high-latency links at the cost of higher\n    /// worst-case memory usage per connection.\n    ///\n    /// The default value is 2MiB.\n    pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {\n        self.inner.h2_conn_window_size = size;\n        self\n    }\n\n    /// Builds a [`ServiceConfig`] from this [`ServiceConfigBuilder`] instance\n    pub fn build(self) -> ServiceConfig {\n        ServiceConfig(Rc::new(self.inner))\n    }\n}\n\n/// HTTP service configuration.\n#[derive(Debug, Clone, Default)]\npub struct ServiceConfig(Rc<Inner>);\n\n#[derive(Debug)]\nstruct Inner {\n    keep_alive: KeepAlive,\n    client_request_timeout: Duration,\n    client_disconnect_timeout: Duration,\n    secure: bool,\n    local_addr: Option<SocketAddr>,\n    tcp_nodelay: Option<bool>,\n    date_service: DateService,\n    h1_allow_half_closed: bool,\n    h2_conn_window_size: u32,\n    h2_stream_window_size: u32,\n}\n\nimpl Default for Inner {\n    fn default() -> Self {\n        Self {\n            keep_alive: KeepAlive::default(),\n            client_request_timeout: Duration::from_secs(5),\n            client_disconnect_timeout: Duration::ZERO,\n            secure: false,\n            local_addr: None,\n            tcp_nodelay: None,\n            date_service: DateService::new(),\n            h1_allow_half_closed: true,\n            h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,\n            h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,\n        }\n    }\n}\n\nimpl ServiceConfig {\n    /// Create instance of `ServiceConfig`.\n    pub fn new(\n        keep_alive: KeepAlive,\n        client_request_timeout: Duration,\n        client_disconnect_timeout: Duration,\n        secure: bool,\n        local_addr: Option<SocketAddr>,\n    ) -> ServiceConfig {\n        ServiceConfig(Rc::new(Inner {\n            keep_alive: keep_alive.normalize(),\n            client_request_timeout,\n            client_disconnect_timeout,\n            secure,\n            local_addr,\n            tcp_nodelay: None,\n            date_service: DateService::new(),\n            h1_allow_half_closed: true,\n            h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,\n            h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,\n        }))\n    }\n\n    /// Returns `true` if connection is secure (i.e., using TLS / HTTPS).\n    #[inline]\n    pub fn secure(&self) -> bool {\n        self.0.secure\n    }\n\n    /// Returns the local address that this server is bound to.\n    ///\n    /// Returns `None` for connections via UDS (Unix Domain Socket).\n    #[inline]\n    pub fn local_addr(&self) -> Option<SocketAddr> {\n        self.0.local_addr\n    }\n\n    /// Connection keep-alive setting.\n    #[inline]\n    pub fn keep_alive(&self) -> KeepAlive {\n        self.0.keep_alive\n    }\n\n    /// Creates a time object representing the deadline for this connection's keep-alive period, if\n    /// enabled.\n    ///\n    /// When [`KeepAlive::Os`] or [`KeepAlive::Disabled`] is set, this will return `None`.\n    pub fn keep_alive_deadline(&self) -> Option<Instant> {\n        match self.keep_alive() {\n            KeepAlive::Timeout(dur) => Some(self.now() + dur),\n            KeepAlive::Os => None,\n            KeepAlive::Disabled => None,\n        }\n    }\n\n    /// Creates a time object representing the deadline for the client to finish sending the head of\n    /// its first request.\n    ///\n    /// Returns `None` if this `ServiceConfig was` constructed with `client_request_timeout: 0`.\n    pub fn client_request_deadline(&self) -> Option<Instant> {\n        let timeout = self.0.client_request_timeout;\n        (timeout != Duration::ZERO).then(|| self.now() + timeout)\n    }\n\n    /// Creates a time object representing the deadline for the client to disconnect.\n    pub fn client_disconnect_deadline(&self) -> Option<Instant> {\n        let timeout = self.0.client_disconnect_timeout;\n        (timeout != Duration::ZERO).then(|| self.now() + timeout)\n    }\n\n    /// Whether HTTP/1 connections should support half-closures.\n    ///\n    /// Clients can choose to shutdown their writer-side of the connection after completing their\n    /// request and while waiting for the server response. If this configuration is `false`, the\n    /// server will abort the connection handling as soon as it detects an EOF from the client\n    pub fn h1_allow_half_closed(&self) -> bool {\n        self.0.h1_allow_half_closed\n    }\n\n    /// Returns configured `TCP_NODELAY` setting for accepted TCP connections.\n    pub fn tcp_nodelay(&self) -> Option<bool> {\n        self.0.tcp_nodelay\n    }\n\n    /// HTTP/2 initial stream-level flow control window size (in bytes).\n    pub fn h2_initial_window_size(&self) -> u32 {\n        self.0.h2_stream_window_size\n    }\n\n    /// HTTP/2 initial connection-level flow control window size (in bytes).\n    pub fn h2_initial_connection_window_size(&self) -> u32 {\n        self.0.h2_conn_window_size\n    }\n\n    pub(crate) fn now(&self) -> Instant {\n        self.0.date_service.now()\n    }\n\n    /// Writes date header to `dst` buffer.\n    ///\n    /// Low-level method that utilizes the built-in efficient date service, requiring fewer syscalls\n    /// than normal. Note that a CRLF (`\\r\\n`) is included in what is written.\n    #[doc(hidden)]\n    pub fn write_date_header(&self, dst: &mut BytesMut, camel_case: bool) {\n        let mut buf: [u8; 37] = [0; 37];\n\n        buf[..6].copy_from_slice(if camel_case { b\"Date: \" } else { b\"date: \" });\n\n        self.0\n            .date_service\n            .with_date(|date| buf[6..35].copy_from_slice(&date.bytes));\n\n        buf[35..].copy_from_slice(b\"\\r\\n\");\n        dst.extend_from_slice(&buf);\n    }\n\n    #[allow(unused)] // used with `http2` feature flag\n    pub(crate) fn write_date_header_value(&self, dst: &mut BytesMut) {\n        self.0\n            .date_service\n            .with_date(|date| dst.extend_from_slice(&date.bytes));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_rt::{\n        task::yield_now,\n        time::{sleep, sleep_until},\n    };\n    use memchr::memmem;\n\n    use super::*;\n    use crate::{date::DATE_VALUE_LENGTH, notify_on_drop};\n\n    #[actix_rt::test]\n    async fn test_date_service_update() {\n        let settings =\n            ServiceConfig::new(KeepAlive::Os, Duration::ZERO, Duration::ZERO, false, None);\n\n        yield_now().await;\n\n        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf1, false);\n        let now1 = settings.now();\n\n        sleep_until((Instant::now() + Duration::from_secs(2)).into()).await;\n        yield_now().await;\n\n        let now2 = settings.now();\n        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf2, false);\n\n        assert_ne!(now1, now2);\n\n        assert_ne!(buf1, buf2);\n\n        drop(settings);\n\n        // Ensure the task will drop eventually\n        let mut times = 0;\n        while !notify_on_drop::is_dropped() {\n            sleep(Duration::from_millis(100)).await;\n            times += 1;\n            assert!(times < 10, \"Timeout waiting for task drop\");\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_date_service_drop() {\n        let service = Rc::new(DateService::new());\n\n        // yield so date service have a chance to register the spawned timer update task.\n        yield_now().await;\n\n        let clone1 = service.clone();\n        let clone2 = service.clone();\n        let clone3 = service.clone();\n\n        drop(clone1);\n        assert!(!notify_on_drop::is_dropped());\n        drop(clone2);\n        assert!(!notify_on_drop::is_dropped());\n        drop(clone3);\n        assert!(!notify_on_drop::is_dropped());\n\n        drop(service);\n\n        // Ensure the task will drop eventually\n        let mut times = 0;\n        while !notify_on_drop::is_dropped() {\n            sleep(Duration::from_millis(100)).await;\n            times += 1;\n            assert!(times < 10, \"Timeout waiting for task drop\");\n        }\n    }\n\n    #[test]\n    fn test_date_len() {\n        assert_eq!(DATE_VALUE_LENGTH, \"Sun, 06 Nov 1994 08:49:37 GMT\".len());\n    }\n\n    #[actix_rt::test]\n    async fn test_date() {\n        let settings = ServiceConfig::default();\n\n        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf1, false);\n\n        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf2, false);\n\n        assert_eq!(buf1, buf2);\n    }\n\n    #[actix_rt::test]\n    async fn test_date_camel_case() {\n        let settings = ServiceConfig::default();\n\n        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf, false);\n        assert!(memmem::find(&buf, b\"date:\").is_some());\n\n        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);\n        settings.write_date_header(&mut buf, true);\n        assert!(memmem::find(&buf, b\"Date:\").is_some());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/date.rs",
    "content": "use std::{\n    cell::Cell,\n    fmt::{self, Write},\n    rc::Rc,\n    time::{Duration, Instant, SystemTime},\n};\n\nuse actix_rt::{task::JoinHandle, time::interval};\n\n/// \"Thu, 01 Jan 1970 00:00:00 GMT\".len()\npub(crate) const DATE_VALUE_LENGTH: usize = 29;\n\n#[derive(Clone, Copy)]\npub(crate) struct Date {\n    pub(crate) bytes: [u8; DATE_VALUE_LENGTH],\n    pos: usize,\n}\n\nimpl Date {\n    fn new() -> Date {\n        let mut date = Date {\n            bytes: [0; DATE_VALUE_LENGTH],\n            pos: 0,\n        };\n        date.update();\n        date\n    }\n\n    fn update(&mut self) {\n        self.pos = 0;\n        write!(self, \"{}\", httpdate::HttpDate::from(SystemTime::now())).unwrap();\n    }\n}\n\nimpl fmt::Write for Date {\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        let len = s.len();\n        self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());\n        self.pos += len;\n        Ok(())\n    }\n}\n\n/// Service for update Date and Instant periodically at 500 millis interval.\npub(crate) struct DateService {\n    current: Rc<Cell<(Date, Instant)>>,\n    handle: JoinHandle<()>,\n}\n\nimpl DateService {\n    pub(crate) fn new() -> Self {\n        // shared date and timer for DateService and update async task.\n        let current = Rc::new(Cell::new((Date::new(), Instant::now())));\n        let current_clone = Rc::clone(&current);\n        // spawn an async task sleep for 500 millis and update current date/timer in a loop.\n        // handle is used to stop the task on DateService drop.\n        let handle = actix_rt::spawn(async move {\n            #[cfg(test)]\n            let _notify = crate::notify_on_drop::NotifyOnDrop::new();\n\n            let mut interval = interval(Duration::from_millis(500));\n            loop {\n                let now = interval.tick().await;\n                let date = Date::new();\n                current_clone.set((date, now.into_std()));\n            }\n        });\n\n        DateService { current, handle }\n    }\n\n    pub(crate) fn now(&self) -> Instant {\n        self.current.get().1\n    }\n\n    pub(crate) fn with_date<F: FnMut(&Date)>(&self, mut f: F) {\n        f(&self.current.get().0);\n    }\n}\n\nimpl fmt::Debug for DateService {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"DateService\").finish_non_exhaustive()\n    }\n}\n\nimpl Drop for DateService {\n    fn drop(&mut self) {\n        // stop the timer update async task on drop.\n        self.handle.abort();\n    }\n}\n"
  },
  {
    "path": "actix-http/src/encoding/decoder.rs",
    "content": "//! Stream decoders.\n\nuse std::{\n    future::Future,\n    io::{self, Write as _},\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_rt::task::{spawn_blocking, JoinHandle};\nuse bytes::Bytes;\n#[cfg(feature = \"compress-gzip\")]\nuse flate2::write::{GzDecoder, ZlibDecoder};\nuse futures_core::{ready, Stream};\n#[cfg(feature = \"compress-zstd\")]\nuse zstd::stream::write::Decoder as ZstdDecoder;\n\nuse crate::{\n    encoding::Writer,\n    error::PayloadError,\n    header::{ContentEncoding, HeaderMap, CONTENT_ENCODING},\n};\n\nconst MAX_CHUNK_SIZE_DECODE_IN_PLACE: usize = 2049;\n\npin_project_lite::pin_project! {\n    pub struct Decoder<S> {\n        decoder: Option<ContentDecoder>,\n        #[pin]\n        stream: S,\n        eof: bool,\n        fut: Option<JoinHandle<Result<(Option<Bytes>, ContentDecoder), io::Error>>>,\n    }\n}\n\nimpl<S> Decoder<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    /// Construct a decoder.\n    #[inline]\n    pub fn new(stream: S, encoding: ContentEncoding) -> Decoder<S> {\n        let decoder = match encoding {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentEncoding::Brotli => Some(ContentDecoder::Brotli(Box::new(\n                brotli::DecompressorWriter::new(Writer::new(), 8_096),\n            ))),\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(ZlibDecoder::new(\n                Writer::new(),\n            )))),\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(GzDecoder::new(\n                Writer::new(),\n            )))),\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentEncoding::Zstd => Some(ContentDecoder::Zstd(Box::new(\n                ZstdDecoder::new(Writer::new()).expect(\n                    \"Failed to create zstd decoder. This is a bug. \\\n                         Please report it to the actix-web repository.\",\n                ),\n            ))),\n            _ => None,\n        };\n\n        Decoder {\n            decoder,\n            stream,\n            fut: None,\n            eof: false,\n        }\n    }\n\n    /// Construct decoder based on headers.\n    #[inline]\n    pub fn from_headers(stream: S, headers: &HeaderMap) -> Decoder<S> {\n        // check content-encoding\n        let encoding = headers\n            .get(&CONTENT_ENCODING)\n            .and_then(|val| val.to_str().ok())\n            .and_then(|x| x.parse().ok())\n            .unwrap_or(ContentEncoding::Identity);\n\n        Self::new(stream, encoding)\n    }\n}\n\nimpl<S> Stream for Decoder<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    type Item = Result<Bytes, PayloadError>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let mut this = self.project();\n\n        loop {\n            if let Some(ref mut fut) = this.fut {\n                let (chunk, decoder) = ready!(Pin::new(fut).poll(cx)).map_err(|_| {\n                    PayloadError::Io(io::Error::other(\"Blocking task was cancelled unexpectedly\"))\n                })??;\n\n                *this.decoder = Some(decoder);\n                this.fut.take();\n\n                if let Some(chunk) = chunk {\n                    return Poll::Ready(Some(Ok(chunk)));\n                }\n            }\n\n            if *this.eof {\n                return Poll::Ready(None);\n            }\n\n            match ready!(this.stream.as_mut().poll_next(cx)) {\n                Some(Err(err)) => return Poll::Ready(Some(Err(err))),\n\n                Some(Ok(chunk)) => {\n                    if let Some(mut decoder) = this.decoder.take() {\n                        if chunk.len() < MAX_CHUNK_SIZE_DECODE_IN_PLACE {\n                            let chunk = decoder.feed_data(chunk)?;\n                            *this.decoder = Some(decoder);\n\n                            if let Some(chunk) = chunk {\n                                return Poll::Ready(Some(Ok(chunk)));\n                            }\n                        } else {\n                            *this.fut = Some(spawn_blocking(move || {\n                                let chunk = decoder.feed_data(chunk)?;\n                                Ok((chunk, decoder))\n                            }));\n                        }\n\n                        continue;\n                    } else {\n                        return Poll::Ready(Some(Ok(chunk)));\n                    }\n                }\n\n                None => {\n                    *this.eof = true;\n\n                    return if let Some(mut decoder) = this.decoder.take() {\n                        match decoder.feed_eof() {\n                            Ok(Some(res)) => Poll::Ready(Some(Ok(res))),\n                            Ok(None) => Poll::Ready(None),\n                            Err(err) => Poll::Ready(Some(Err(err.into()))),\n                        }\n                    } else {\n                        Poll::Ready(None)\n                    };\n                }\n            }\n        }\n    }\n}\n\nenum ContentDecoder {\n    #[cfg(feature = \"compress-gzip\")]\n    Deflate(Box<ZlibDecoder<Writer>>),\n\n    #[cfg(feature = \"compress-gzip\")]\n    Gzip(Box<GzDecoder<Writer>>),\n\n    #[cfg(feature = \"compress-brotli\")]\n    Brotli(Box<brotli::DecompressorWriter<Writer>>),\n\n    // We need explicit 'static lifetime here because ZstdDecoder need lifetime\n    // argument, and we use `spawn_blocking` in `Decoder::poll_next` that require `FnOnce() -> R + Send + 'static`\n    #[cfg(feature = \"compress-zstd\")]\n    Zstd(Box<ZstdDecoder<'static, Writer>>),\n}\n\nimpl ContentDecoder {\n    fn feed_eof(&mut self) -> io::Result<Option<Bytes>> {\n        match self {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentDecoder::Brotli(ref mut decoder) => match decoder.flush() {\n                Ok(()) => {\n                    let b = decoder.get_mut().take();\n\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentDecoder::Gzip(ref mut decoder) => match decoder.try_finish() {\n                Ok(_) => {\n                    let b = decoder.get_mut().take();\n\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentDecoder::Deflate(ref mut decoder) => match decoder.try_finish() {\n                Ok(_) => {\n                    let b = decoder.get_mut().take();\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentDecoder::Zstd(ref mut decoder) => match decoder.flush() {\n                Ok(_) => {\n                    let b = decoder.get_mut().take();\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n        }\n    }\n\n    fn feed_data(&mut self, data: Bytes) -> io::Result<Option<Bytes>> {\n        match self {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentDecoder::Brotli(ref mut decoder) => match decoder.write_all(&data) {\n                Ok(_) => {\n                    decoder.flush()?;\n                    let b = decoder.get_mut().take();\n\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentDecoder::Gzip(ref mut decoder) => match decoder.write_all(&data) {\n                Ok(_) => {\n                    decoder.flush()?;\n                    let b = decoder.get_mut().take();\n\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentDecoder::Deflate(ref mut decoder) => match decoder.write_all(&data) {\n                Ok(_) => {\n                    decoder.flush()?;\n\n                    let b = decoder.get_mut().take();\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentDecoder::Zstd(ref mut decoder) => match decoder.write_all(&data) {\n                Ok(_) => {\n                    decoder.flush()?;\n\n                    let b = decoder.get_mut().take();\n                    if !b.is_empty() {\n                        Ok(Some(b))\n                    } else {\n                        Ok(None)\n                    }\n                }\n                Err(err) => Err(err),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/encoding/encoder.rs",
    "content": "//! Stream encoders.\n\nuse std::{\n    error::Error as StdError,\n    future::Future,\n    io::{self, Write as _},\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_rt::task::{spawn_blocking, JoinHandle};\nuse bytes::Bytes;\nuse derive_more::Display;\n#[cfg(feature = \"compress-gzip\")]\nuse flate2::write::{GzEncoder, ZlibEncoder};\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\nuse tracing::trace;\n#[cfg(feature = \"compress-zstd\")]\nuse zstd::stream::write::Encoder as ZstdEncoder;\n\nuse super::Writer;\nuse crate::{\n    body::{self, BodySize, MessageBody},\n    header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING},\n    ResponseHead, StatusCode,\n};\n\nconst MAX_CHUNK_SIZE_ENCODE_IN_PLACE: usize = 1024;\n\npin_project! {\n    pub struct Encoder<B> {\n        #[pin]\n        body: EncoderBody<B>,\n        encoder: Option<ContentEncoder>,\n        fut: Option<JoinHandle<Result<ContentEncoder, io::Error>>>,\n        eof: bool,\n    }\n}\n\nimpl<B: MessageBody> Encoder<B> {\n    fn none() -> Self {\n        Encoder {\n            body: EncoderBody::None {\n                body: body::None::new(),\n            },\n            encoder: None,\n            fut: None,\n            eof: true,\n        }\n    }\n\n    fn empty() -> Self {\n        Encoder {\n            body: EncoderBody::Full { body: Bytes::new() },\n            encoder: None,\n            fut: None,\n            eof: true,\n        }\n    }\n\n    pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self {\n        // no need to compress empty bodies\n        match body.size() {\n            BodySize::None => return Self::none(),\n            BodySize::Sized(0) => return Self::empty(),\n            _ => {}\n        }\n\n        let should_encode = !(head.headers().contains_key(&CONTENT_ENCODING)\n            || head.status == StatusCode::SWITCHING_PROTOCOLS\n            || head.status == StatusCode::NO_CONTENT\n            || head.status == StatusCode::PARTIAL_CONTENT\n            || encoding == ContentEncoding::Identity);\n\n        let body = match body.try_into_bytes() {\n            Ok(body) => EncoderBody::Full { body },\n            Err(body) => EncoderBody::Stream { body },\n        };\n\n        if should_encode {\n            // wrap body only if encoder is feature-enabled\n            if let Some(enc) = ContentEncoder::select(encoding) {\n                update_head(encoding, head);\n\n                return Encoder {\n                    body,\n                    encoder: Some(enc),\n                    fut: None,\n                    eof: false,\n                };\n            }\n        }\n\n        Encoder {\n            body,\n            encoder: None,\n            fut: None,\n            eof: false,\n        }\n    }\n}\n\npin_project! {\n    #[project = EncoderBodyProj]\n    enum EncoderBody<B> {\n        None { body: body::None },\n        Full { body: Bytes },\n        Stream { #[pin] body: B },\n    }\n}\n\nimpl<B> MessageBody for EncoderBody<B>\nwhere\n    B: MessageBody,\n{\n    type Error = EncoderError;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        match self {\n            EncoderBody::None { body } => body.size(),\n            EncoderBody::Full { body } => body.size(),\n            EncoderBody::Stream { body } => body.size(),\n        }\n    }\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        match self.project() {\n            EncoderBodyProj::None { body } => {\n                Pin::new(body).poll_next(cx).map_err(|err| match err {})\n            }\n            EncoderBodyProj::Full { body } => {\n                Pin::new(body).poll_next(cx).map_err(|err| match err {})\n            }\n            EncoderBodyProj::Stream { body } => body\n                .poll_next(cx)\n                .map_err(|err| EncoderError::Body(err.into())),\n        }\n    }\n\n    #[inline]\n    fn try_into_bytes(self) -> Result<Bytes, Self>\n    where\n        Self: Sized,\n    {\n        match self {\n            EncoderBody::None { body } => Ok(body.try_into_bytes().unwrap()),\n            EncoderBody::Full { body } => Ok(body.try_into_bytes().unwrap()),\n            _ => Err(self),\n        }\n    }\n}\n\nimpl<B> MessageBody for Encoder<B>\nwhere\n    B: MessageBody,\n{\n    type Error = EncoderError;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        if self.encoder.is_some() {\n            BodySize::Stream\n        } else {\n            self.body.size()\n        }\n    }\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        let mut this = self.project();\n\n        loop {\n            if *this.eof {\n                return Poll::Ready(None);\n            }\n\n            if let Some(ref mut fut) = this.fut {\n                let mut encoder = ready!(Pin::new(fut).poll(cx))\n                    .map_err(|_| {\n                        EncoderError::Io(io::Error::other(\n                            \"Blocking task was cancelled unexpectedly\",\n                        ))\n                    })?\n                    .map_err(EncoderError::Io)?;\n\n                let chunk = encoder.take();\n                *this.encoder = Some(encoder);\n                this.fut.take();\n\n                if !chunk.is_empty() {\n                    return Poll::Ready(Some(Ok(chunk)));\n                }\n            }\n\n            let result = ready!(this.body.as_mut().poll_next(cx));\n\n            match result {\n                Some(Err(err)) => return Poll::Ready(Some(Err(err))),\n\n                Some(Ok(chunk)) => {\n                    if let Some(mut encoder) = this.encoder.take() {\n                        if chunk.len() < MAX_CHUNK_SIZE_ENCODE_IN_PLACE {\n                            encoder.write(&chunk).map_err(EncoderError::Io)?;\n                            let chunk = encoder.take();\n                            *this.encoder = Some(encoder);\n\n                            if !chunk.is_empty() {\n                                return Poll::Ready(Some(Ok(chunk)));\n                            }\n                        } else {\n                            *this.fut = Some(spawn_blocking(move || {\n                                encoder.write(&chunk)?;\n                                Ok(encoder)\n                            }));\n                        }\n                    } else {\n                        return Poll::Ready(Some(Ok(chunk)));\n                    }\n                }\n\n                None => {\n                    if let Some(encoder) = this.encoder.take() {\n                        let chunk = encoder.finish().map_err(EncoderError::Io)?;\n\n                        if chunk.is_empty() {\n                            return Poll::Ready(None);\n                        } else {\n                            *this.eof = true;\n                            return Poll::Ready(Some(Ok(chunk)));\n                        }\n                    } else {\n                        return Poll::Ready(None);\n                    }\n                }\n            }\n        }\n    }\n\n    #[inline]\n    fn try_into_bytes(mut self) -> Result<Bytes, Self>\n    where\n        Self: Sized,\n    {\n        if self.encoder.is_some() {\n            Err(self)\n        } else {\n            match self.body.try_into_bytes() {\n                Ok(body) => Ok(body),\n                Err(body) => {\n                    self.body = body;\n                    Err(self)\n                }\n            }\n        }\n    }\n}\n\nfn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {\n    head.headers_mut()\n        .insert(header::CONTENT_ENCODING, encoding.to_header_value());\n    head.headers_mut()\n        .append(header::VARY, HeaderValue::from_static(\"accept-encoding\"));\n\n    head.no_chunking(false);\n}\n\nenum ContentEncoder {\n    #[cfg(feature = \"compress-gzip\")]\n    Deflate(ZlibEncoder<Writer>),\n\n    #[cfg(feature = \"compress-gzip\")]\n    Gzip(GzEncoder<Writer>),\n\n    #[cfg(feature = \"compress-brotli\")]\n    Brotli(Box<brotli::CompressorWriter<Writer>>),\n\n    // Wwe need explicit 'static lifetime here because ZstdEncoder needs a lifetime argument and we\n    // use `spawn_blocking` in `Encoder::poll_next` that requires `FnOnce() -> R + Send + 'static`.\n    #[cfg(feature = \"compress-zstd\")]\n    Zstd(ZstdEncoder<'static, Writer>),\n}\n\nimpl ContentEncoder {\n    fn select(encoding: ContentEncoding) -> Option<Self> {\n        match encoding {\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoding::Deflate => Some(ContentEncoder::Deflate(ZlibEncoder::new(\n                Writer::new(),\n                flate2::Compression::fast(),\n            ))),\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoding::Gzip => Some(ContentEncoder::Gzip(GzEncoder::new(\n                Writer::new(),\n                flate2::Compression::fast(),\n            ))),\n\n            #[cfg(feature = \"compress-brotli\")]\n            ContentEncoding::Brotli => Some(ContentEncoder::Brotli(new_brotli_compressor())),\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentEncoding::Zstd => {\n                let encoder = ZstdEncoder::new(Writer::new(), 3).ok()?;\n                Some(ContentEncoder::Zstd(encoder))\n            }\n\n            _ => None,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn take(&mut self) -> Bytes {\n        match *self {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentEncoder::Brotli(ref mut encoder) => encoder.get_mut().take(),\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(),\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Gzip(ref mut encoder) => encoder.get_mut().take(),\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentEncoder::Zstd(ref mut encoder) => encoder.get_mut().take(),\n        }\n    }\n\n    fn finish(self) -> Result<Bytes, io::Error> {\n        match self {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentEncoder::Brotli(mut encoder) => match encoder.flush() {\n                Ok(()) => Ok(encoder.into_inner().buf.freeze()),\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Gzip(encoder) => match encoder.finish() {\n                Ok(writer) => Ok(writer.buf.freeze()),\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Deflate(encoder) => match encoder.finish() {\n                Ok(writer) => Ok(writer.buf.freeze()),\n                Err(err) => Err(err),\n            },\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentEncoder::Zstd(encoder) => match encoder.finish() {\n                Ok(writer) => Ok(writer.buf.freeze()),\n                Err(err) => Err(err),\n            },\n        }\n    }\n\n    fn write(&mut self, data: &[u8]) -> Result<(), io::Error> {\n        match *self {\n            #[cfg(feature = \"compress-brotli\")]\n            ContentEncoder::Brotli(ref mut encoder) => match encoder.write_all(data) {\n                Ok(_) => Ok(()),\n                Err(err) => {\n                    trace!(\"Error decoding br encoding: {}\", err);\n                    Err(err)\n                }\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Gzip(ref mut encoder) => match encoder.write_all(data) {\n                Ok(_) => Ok(()),\n                Err(err) => {\n                    trace!(\"Error decoding gzip encoding: {}\", err);\n                    Err(err)\n                }\n            },\n\n            #[cfg(feature = \"compress-gzip\")]\n            ContentEncoder::Deflate(ref mut encoder) => match encoder.write_all(data) {\n                Ok(_) => Ok(()),\n                Err(err) => {\n                    trace!(\"Error decoding deflate encoding: {}\", err);\n                    Err(err)\n                }\n            },\n\n            #[cfg(feature = \"compress-zstd\")]\n            ContentEncoder::Zstd(ref mut encoder) => match encoder.write_all(data) {\n                Ok(_) => Ok(()),\n                Err(err) => {\n                    trace!(\"Error decoding ztsd encoding: {}\", err);\n                    Err(err)\n                }\n            },\n        }\n    }\n}\n\n#[cfg(feature = \"compress-brotli\")]\nfn new_brotli_compressor() -> Box<brotli::CompressorWriter<Writer>> {\n    Box::new(brotli::CompressorWriter::new(\n        Writer::new(),\n        32 * 1024, // 32 KiB buffer\n        3,         // BROTLI_PARAM_QUALITY\n        22,        // BROTLI_PARAM_LGWIN\n    ))\n}\n\n#[derive(Debug, Display)]\n#[non_exhaustive]\npub enum EncoderError {\n    /// Wrapped body stream error.\n    #[display(\"body\")]\n    Body(Box<dyn StdError>),\n\n    /// Generic I/O error.\n    #[display(\"io\")]\n    Io(io::Error),\n}\n\nimpl StdError for EncoderError {\n    fn source(&self) -> Option<&(dyn StdError + 'static)> {\n        match self {\n            EncoderError::Body(err) => Some(&**err),\n            EncoderError::Io(err) => Some(err),\n        }\n    }\n}\n\nimpl From<EncoderError> for crate::Error {\n    fn from(err: EncoderError) -> Self {\n        crate::Error::new_encoder().with_cause(err)\n    }\n}\n"
  },
  {
    "path": "actix-http/src/encoding/mod.rs",
    "content": "//! Content-Encoding support.\n\nuse std::io;\n\nuse bytes::{Bytes, BytesMut};\n\nmod decoder;\nmod encoder;\n\npub use self::{decoder::Decoder, encoder::Encoder};\n\n/// Special-purpose writer for streaming (de-)compression.\n///\n/// Pre-allocates 8KiB of capacity.\nstruct Writer {\n    buf: BytesMut,\n}\n\nimpl Writer {\n    fn new() -> Writer {\n        Writer {\n            buf: BytesMut::with_capacity(8192),\n        }\n    }\n\n    fn take(&mut self) -> Bytes {\n        self.buf.split().freeze()\n    }\n}\n\nimpl io::Write for Writer {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.buf.extend_from_slice(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/error.rs",
    "content": "//! Error and Result module\n\nuse std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};\n\nuse derive_more::{Display, Error, From};\npub use http::{status::InvalidStatusCode, Error as HttpError};\nuse http::{uri::InvalidUri, StatusCode};\n\nuse crate::{body::BoxBody, Response};\n\npub struct Error {\n    inner: Box<ErrorInner>,\n}\n\npub(crate) struct ErrorInner {\n    #[allow(dead_code)]\n    kind: Kind,\n    cause: Option<Box<dyn StdError>>,\n}\n\nimpl Error {\n    fn new(kind: Kind) -> Self {\n        Self {\n            inner: Box::new(ErrorInner { kind, cause: None }),\n        }\n    }\n\n    pub(crate) fn with_cause(mut self, cause: impl Into<Box<dyn StdError>>) -> Self {\n        self.inner.cause = Some(cause.into());\n        self\n    }\n\n    pub(crate) fn new_http() -> Self {\n        Self::new(Kind::Http)\n    }\n\n    pub(crate) fn new_parse() -> Self {\n        Self::new(Kind::Parse)\n    }\n\n    pub(crate) fn new_payload() -> Self {\n        Self::new(Kind::Payload)\n    }\n\n    pub(crate) fn new_body() -> Self {\n        Self::new(Kind::Body)\n    }\n\n    pub(crate) fn new_send_response() -> Self {\n        Self::new(Kind::SendResponse)\n    }\n\n    #[allow(unused)] // available for future use\n    pub(crate) fn new_io() -> Self {\n        Self::new(Kind::Io)\n    }\n\n    #[allow(unused)] // used in encoder behind feature flag so ignore unused warning\n    pub(crate) fn new_encoder() -> Self {\n        Self::new(Kind::Encoder)\n    }\n\n    #[allow(unused)] // used with `ws` feature flag\n    pub(crate) fn new_ws() -> Self {\n        Self::new(Kind::Ws)\n    }\n}\n\nimpl From<Error> for Response<BoxBody> {\n    fn from(err: Error) -> Self {\n        // TODO: more appropriate error status codes, usage assessment needed\n        let status_code = match err.inner.kind {\n            Kind::Parse => StatusCode::BAD_REQUEST,\n            _ => StatusCode::INTERNAL_SERVER_ERROR,\n        };\n\n        Response::new(status_code).set_body(BoxBody::new(err.to_string()))\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]\npub(crate) enum Kind {\n    #[display(\"error processing HTTP\")]\n    Http,\n\n    #[display(\"error parsing HTTP message\")]\n    Parse,\n\n    #[display(\"request payload read error\")]\n    Payload,\n\n    #[display(\"response body write error\")]\n    Body,\n\n    #[display(\"send response error\")]\n    SendResponse,\n\n    #[display(\"error in WebSocket process\")]\n    Ws,\n\n    #[display(\"connection error\")]\n    Io,\n\n    #[display(\"encoder error\")]\n    Encoder,\n}\n\nimpl fmt::Debug for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"actix_http::Error\")\n            .field(\"kind\", &self.inner.kind)\n            .field(\"cause\", &self.inner.cause)\n            .finish()\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.inner.cause.as_ref() {\n            Some(err) => write!(f, \"{}: {}\", &self.inner.kind, err),\n            None => write!(f, \"{}\", &self.inner.kind),\n        }\n    }\n}\n\nimpl StdError for Error {\n    fn source(&self) -> Option<&(dyn StdError + 'static)> {\n        self.inner.cause.as_ref().map(Box::as_ref)\n    }\n}\n\nimpl From<std::convert::Infallible> for Error {\n    fn from(err: std::convert::Infallible) -> Self {\n        match err {}\n    }\n}\n\nimpl From<HttpError> for Error {\n    fn from(err: HttpError) -> Self {\n        Self::new_http().with_cause(err)\n    }\n}\n\n#[cfg(feature = \"ws\")]\nimpl From<crate::ws::HandshakeError> for Error {\n    fn from(err: crate::ws::HandshakeError) -> Self {\n        Self::new_ws().with_cause(err)\n    }\n}\n\n#[cfg(feature = \"ws\")]\nimpl From<crate::ws::ProtocolError> for Error {\n    fn from(err: crate::ws::ProtocolError) -> Self {\n        Self::new_ws().with_cause(err)\n    }\n}\n\n/// A set of errors that can occur during parsing HTTP streams.\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum ParseError {\n    /// An invalid `Method`, such as `GE.T`.\n    #[display(\"invalid method specified\")]\n    Method,\n\n    /// An invalid `Uri`, such as `exam ple.domain`.\n    #[display(\"URI error: {}\", _0)]\n    Uri(InvalidUri),\n\n    /// An invalid `HttpVersion`, such as `HTP/1.1`\n    #[display(\"invalid HTTP version specified\")]\n    Version,\n\n    /// An invalid `Header`.\n    #[display(\"invalid Header provided\")]\n    Header,\n\n    /// A message head is too large to be reasonable.\n    #[display(\"message head is too large\")]\n    TooLarge,\n\n    /// A message reached EOF, but is not complete.\n    #[display(\"message is incomplete\")]\n    Incomplete,\n\n    /// An invalid `Status`, such as `1337 ELITE`.\n    #[display(\"invalid status provided\")]\n    Status,\n\n    /// A timeout occurred waiting for an IO event.\n    #[allow(dead_code)]\n    #[display(\"timeout\")]\n    Timeout,\n\n    /// An I/O error that occurred while trying to read or write to a network stream.\n    #[display(\"I/O error: {}\", _0)]\n    Io(io::Error),\n\n    /// Parsing a field as string failed.\n    #[display(\"UTF-8 error: {}\", _0)]\n    Utf8(Utf8Error),\n}\n\nimpl From<io::Error> for ParseError {\n    fn from(err: io::Error) -> ParseError {\n        ParseError::Io(err)\n    }\n}\n\nimpl From<InvalidUri> for ParseError {\n    fn from(err: InvalidUri) -> ParseError {\n        ParseError::Uri(err)\n    }\n}\n\nimpl From<Utf8Error> for ParseError {\n    fn from(err: Utf8Error) -> ParseError {\n        ParseError::Utf8(err)\n    }\n}\n\nimpl From<FromUtf8Error> for ParseError {\n    fn from(err: FromUtf8Error) -> ParseError {\n        ParseError::Utf8(err.utf8_error())\n    }\n}\n\nimpl From<httparse::Error> for ParseError {\n    fn from(err: httparse::Error) -> ParseError {\n        match err {\n            httparse::Error::HeaderName\n            | httparse::Error::HeaderValue\n            | httparse::Error::NewLine\n            | httparse::Error::Token => ParseError::Header,\n            httparse::Error::Status => ParseError::Status,\n            httparse::Error::TooManyHeaders => ParseError::TooLarge,\n            httparse::Error::Version => ParseError::Version,\n        }\n    }\n}\n\nimpl From<ParseError> for Error {\n    fn from(err: ParseError) -> Self {\n        Self::new_parse().with_cause(err)\n    }\n}\n\nimpl From<ParseError> for Response<BoxBody> {\n    fn from(err: ParseError) -> Self {\n        Error::from(err).into()\n    }\n}\n\n/// A set of errors that can occur during payload parsing.\n#[derive(Debug, Display)]\n#[non_exhaustive]\npub enum PayloadError {\n    /// A payload reached EOF, but is not complete.\n    #[display(\"payload reached EOF before completing: {:?}\", _0)]\n    Incomplete(Option<io::Error>),\n\n    /// Content encoding stream corruption.\n    #[display(\"can not decode content-encoding\")]\n    EncodingCorrupted,\n\n    /// Payload reached size limit.\n    #[display(\"payload reached size limit\")]\n    Overflow,\n\n    /// Payload length is unknown.\n    #[display(\"payload length is unknown\")]\n    UnknownLength,\n\n    /// HTTP/2 payload error.\n    #[cfg(feature = \"http2\")]\n    #[display(\"{}\", _0)]\n    Http2Payload(::h2::Error),\n\n    /// Generic I/O error.\n    #[display(\"{}\", _0)]\n    Io(io::Error),\n}\n\nimpl std::error::Error for PayloadError {\n    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n        match self {\n            PayloadError::Incomplete(None) => None,\n            PayloadError::Incomplete(Some(err)) => Some(err),\n            PayloadError::EncodingCorrupted => None,\n            PayloadError::Overflow => None,\n            PayloadError::UnknownLength => None,\n            #[cfg(feature = \"http2\")]\n            PayloadError::Http2Payload(err) => Some(err),\n            PayloadError::Io(err) => Some(err),\n        }\n    }\n}\n\n#[cfg(feature = \"http2\")]\nimpl From<::h2::Error> for PayloadError {\n    fn from(err: ::h2::Error) -> Self {\n        PayloadError::Http2Payload(err)\n    }\n}\n\nimpl From<Option<io::Error>> for PayloadError {\n    fn from(err: Option<io::Error>) -> Self {\n        PayloadError::Incomplete(err)\n    }\n}\n\nimpl From<io::Error> for PayloadError {\n    fn from(err: io::Error) -> Self {\n        PayloadError::Incomplete(Some(err))\n    }\n}\n\nimpl From<PayloadError> for Error {\n    fn from(err: PayloadError) -> Self {\n        Self::new_payload().with_cause(err)\n    }\n}\n\n/// A set of errors that can occur during dispatching HTTP requests.\n#[derive(Debug, Display, From)]\n#[non_exhaustive]\npub enum DispatchError {\n    /// Service error.\n    #[display(\"service error\")]\n    Service(Response<BoxBody>),\n\n    /// Body streaming error.\n    #[display(\"body error: {}\", _0)]\n    Body(Box<dyn StdError>),\n\n    /// Upgrade service error.\n    #[display(\"upgrade error\")]\n    Upgrade,\n\n    /// An `io::Error` that occurred while trying to read or write to a network stream.\n    #[display(\"I/O error: {}\", _0)]\n    Io(io::Error),\n\n    /// Request parse error.\n    #[display(\"request parse error: {}\", _0)]\n    Parse(ParseError),\n\n    /// HTTP/2 error.\n    #[display(\"{}\", _0)]\n    #[cfg(feature = \"http2\")]\n    H2(h2::Error),\n\n    /// The first request did not complete within the specified timeout.\n    #[display(\"request did not complete within the specified timeout\")]\n    SlowRequestTimeout,\n\n    /// Disconnect timeout. Makes sense for TLS streams.\n    #[display(\"connection shutdown timeout\")]\n    DisconnectTimeout,\n\n    /// Handler dropped payload before reading EOF.\n    #[display(\"handler dropped payload before reading EOF\")]\n    HandlerDroppedPayload,\n\n    /// Internal error.\n    #[display(\"internal error\")]\n    InternalError,\n}\n\nimpl StdError for DispatchError {\n    fn source(&self) -> Option<&(dyn StdError + 'static)> {\n        match self {\n            DispatchError::Service(_res) => None,\n            DispatchError::Body(err) => Some(&**err),\n            DispatchError::Io(err) => Some(err),\n            DispatchError::Parse(err) => Some(err),\n\n            #[cfg(feature = \"http2\")]\n            DispatchError::H2(err) => Some(err),\n\n            _ => None,\n        }\n    }\n}\n\n/// A set of error that can occur during parsing content type.\n#[derive(Debug, Display, Error)]\n#[cfg_attr(test, derive(PartialEq, Eq))]\n#[non_exhaustive]\npub enum ContentTypeError {\n    /// Can not parse content type.\n    #[display(\"could not parse content type\")]\n    ParseError,\n\n    /// Unknown content encoding.\n    #[display(\"unknown content encoding\")]\n    UnknownEncoding,\n}\n\n#[cfg(test)]\nmod tests {\n    use http::Error as HttpError;\n\n    use super::*;\n\n    #[test]\n    fn test_into_response() {\n        let resp: Response<BoxBody> = ParseError::Incomplete.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();\n        let resp: Response<BoxBody> = Error::new_http().with_cause(err).into();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    #[test]\n    fn test_as_response() {\n        let orig = io::Error::other(\"other\");\n        let err: Error = ParseError::Io(orig).into();\n        assert_eq!(\n            format!(\"{}\", err),\n            \"error parsing HTTP message: I/O error: other\"\n        );\n    }\n\n    #[test]\n    fn test_error_display() {\n        let orig = io::Error::other(\"other\");\n        let err = Error::new_io().with_cause(orig);\n        assert_eq!(\"connection error: other\", err.to_string());\n    }\n\n    #[test]\n    fn test_error_http_response() {\n        let orig = io::Error::other(\"other\");\n        let err = Error::new_io().with_cause(orig);\n        let resp: Response<BoxBody> = err.into();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    #[test]\n    fn test_payload_error() {\n        let err: PayloadError = io::Error::other(\"ParseError\").into();\n        assert!(err.to_string().contains(\"ParseError\"));\n\n        let err = PayloadError::Incomplete(None);\n        assert_eq!(\n            err.to_string(),\n            \"payload reached EOF before completing: None\"\n        );\n    }\n\n    macro_rules! from {\n        ($from:expr => $error:pat) => {\n            match ParseError::from($from) {\n                err @ $error => {\n                    assert!(err.to_string().len() >= 5);\n                }\n                err => unreachable!(\"{:?}\", err),\n            }\n        };\n    }\n\n    macro_rules! from_and_cause {\n        ($from:expr => $error:pat) => {\n            match ParseError::from($from) {\n                e @ $error => {\n                    let desc = format!(\"{}\", e);\n                    assert_eq!(desc, format!(\"I/O error: {}\", $from));\n                }\n                _ => unreachable!(\"{:?}\", $from),\n            }\n        };\n    }\n\n    #[test]\n    fn test_from() {\n        from_and_cause!(io::Error::other(\"other\") => ParseError::Io(..));\n        from!(httparse::Error::HeaderName => ParseError::Header);\n        from!(httparse::Error::HeaderName => ParseError::Header);\n        from!(httparse::Error::HeaderValue => ParseError::Header);\n        from!(httparse::Error::NewLine => ParseError::Header);\n        from!(httparse::Error::Status => ParseError::Status);\n        from!(httparse::Error::Token => ParseError::Header);\n        from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);\n        from!(httparse::Error::Version => ParseError::Version);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/extensions.rs",
    "content": "use std::{\n    any::{Any, TypeId},\n    collections::HashMap,\n    fmt,\n    hash::{BuildHasherDefault, Hasher},\n};\n\n/// A hasher for `TypeId`s that takes advantage of its known characteristics.\n///\n/// Author of `anymap` crate has done research on the topic:\n/// https://github.com/chris-morgan/anymap/blob/2e9a5704/src/lib.rs#L599\n#[derive(Debug, Default)]\nstruct NoOpHasher(u64);\n\nimpl Hasher for NoOpHasher {\n    fn write(&mut self, _bytes: &[u8]) {\n        unimplemented!(\"This NoOpHasher can only handle u64s\")\n    }\n\n    fn write_u64(&mut self, i: u64) {\n        self.0 = i;\n    }\n\n    fn finish(&self) -> u64 {\n        self.0\n    }\n}\n\n/// A type map for request extensions.\n///\n/// All entries into this map must be owned types (or static references).\n#[derive(Default)]\npub struct Extensions {\n    // use no-op hasher with a std HashMap with for faster lookups on the small `TypeId` keys\n    map: HashMap<TypeId, Box<dyn Any>, BuildHasherDefault<NoOpHasher>>,\n}\n\nimpl Extensions {\n    /// Creates an empty `Extensions`.\n    #[inline]\n    pub fn new() -> Extensions {\n        Extensions {\n            map: HashMap::default(),\n        }\n    }\n\n    /// Insert an item into the map.\n    ///\n    /// If an item of this type was already stored, it will be replaced and returned.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// assert_eq!(map.insert(\"\"), None);\n    /// assert_eq!(map.insert(1u32), None);\n    /// assert_eq!(map.insert(2u32), Some(1u32));\n    /// assert_eq!(*map.get::<u32>().unwrap(), 2u32);\n    /// ```\n    pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {\n        self.map\n            .insert(TypeId::of::<T>(), Box::new(val))\n            .and_then(downcast_owned)\n    }\n\n    /// Check if map contains an item of a given type.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// assert!(!map.contains::<u32>());\n    ///\n    /// assert_eq!(map.insert(1u32), None);\n    /// assert!(map.contains::<u32>());\n    /// ```\n    pub fn contains<T: 'static>(&self) -> bool {\n        self.map.contains_key(&TypeId::of::<T>())\n    }\n\n    /// Get a reference to an item of a given type.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// map.insert(1u32);\n    /// assert_eq!(map.get::<u32>(), Some(&1u32));\n    /// ```\n    pub fn get<T: 'static>(&self) -> Option<&T> {\n        self.map\n            .get(&TypeId::of::<T>())\n            .and_then(|boxed| boxed.downcast_ref())\n    }\n\n    /// Get a mutable reference to an item of a given type.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// map.insert(1u32);\n    /// assert_eq!(map.get_mut::<u32>(), Some(&mut 1u32));\n    /// ```\n    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {\n        self.map\n            .get_mut(&TypeId::of::<T>())\n            .and_then(|boxed| boxed.downcast_mut())\n    }\n\n    /// Inserts the given `value` into the extensions if it is not present, then returns a reference\n    /// to the value in the extensions.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// assert_eq!(map.get::<Vec<u32>>(), None);\n    ///\n    /// map.get_or_insert(Vec::<u32>::new()).push(1);\n    /// assert_eq!(map.get::<Vec<u32>>(), Some(&vec![1]));\n    ///\n    /// map.get_or_insert(Vec::<u32>::new()).push(2);\n    /// assert_eq!(map.get::<Vec<u32>>(), Some(&vec![1,2]));\n    /// ```\n    pub fn get_or_insert<T: 'static>(&mut self, value: T) -> &mut T {\n        self.get_or_insert_with(|| value)\n    }\n\n    /// Inserts a value computed from `f` into the extensions if the given `value` is not present,\n    /// then returns a reference to the value in the extensions.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    /// assert_eq!(map.get::<Vec<u32>>(), None);\n    ///\n    /// map.get_or_insert_with(Vec::<u32>::new).push(1);\n    /// assert_eq!(map.get::<Vec<u32>>(), Some(&vec![1]));\n    ///\n    /// map.get_or_insert_with(Vec::<u32>::new).push(2);\n    /// assert_eq!(map.get::<Vec<u32>>(), Some(&vec![1,2]));\n    /// ```\n    pub fn get_or_insert_with<T: 'static, F: FnOnce() -> T>(&mut self, default: F) -> &mut T {\n        self.map\n            .entry(TypeId::of::<T>())\n            .or_insert_with(|| Box::new(default()))\n            .downcast_mut()\n            .expect(\"extensions map should now contain a T value\")\n    }\n\n    /// Remove an item from the map of a given type.\n    ///\n    /// If an item of this type was already stored, it will be returned.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    ///\n    /// map.insert(1u32);\n    /// assert_eq!(map.get::<u32>(), Some(&1u32));\n    ///\n    /// assert_eq!(map.remove::<u32>(), Some(1u32));\n    /// assert!(!map.contains::<u32>());\n    /// ```\n    pub fn remove<T: 'static>(&mut self) -> Option<T> {\n        self.map.remove(&TypeId::of::<T>()).and_then(downcast_owned)\n    }\n\n    /// Clear the `Extensions` of all inserted extensions.\n    ///\n    /// ```\n    /// # use actix_http::Extensions;\n    /// let mut map = Extensions::new();\n    ///\n    /// map.insert(1u32);\n    /// assert!(map.contains::<u32>());\n    ///\n    /// map.clear();\n    /// assert!(!map.contains::<u32>());\n    /// ```\n    #[inline]\n    pub fn clear(&mut self) {\n        self.map.clear();\n    }\n\n    /// Extends self with the items from another `Extensions`.\n    pub fn extend(&mut self, other: Extensions) {\n        self.map.extend(other.map);\n    }\n}\n\nimpl fmt::Debug for Extensions {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"Extensions\").finish()\n    }\n}\n\nfn downcast_owned<T: 'static>(boxed: Box<dyn Any>) -> Option<T> {\n    boxed.downcast().ok().map(|boxed| *boxed)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_remove() {\n        let mut map = Extensions::new();\n\n        map.insert::<i8>(123);\n        assert!(map.get::<i8>().is_some());\n\n        map.remove::<i8>();\n        assert!(map.get::<i8>().is_none());\n    }\n\n    #[test]\n    fn test_clear() {\n        let mut map = Extensions::new();\n\n        map.insert::<i8>(8);\n        map.insert::<i16>(16);\n        map.insert::<i32>(32);\n\n        assert!(map.contains::<i8>());\n        assert!(map.contains::<i16>());\n        assert!(map.contains::<i32>());\n\n        map.clear();\n\n        assert!(!map.contains::<i8>());\n        assert!(!map.contains::<i16>());\n        assert!(!map.contains::<i32>());\n\n        map.insert::<i8>(10);\n        assert_eq!(*map.get::<i8>().unwrap(), 10);\n    }\n\n    #[test]\n    fn test_integers() {\n        static A: u32 = 8;\n\n        let mut map = Extensions::new();\n\n        map.insert::<i8>(8);\n        map.insert::<i16>(16);\n        map.insert::<i32>(32);\n        map.insert::<i64>(64);\n        map.insert::<i128>(128);\n        map.insert::<u8>(8);\n        map.insert::<u16>(16);\n        map.insert::<u32>(32);\n        map.insert::<u64>(64);\n        map.insert::<u128>(128);\n        map.insert::<&'static u32>(&A);\n        assert!(map.get::<i8>().is_some());\n        assert!(map.get::<i16>().is_some());\n        assert!(map.get::<i32>().is_some());\n        assert!(map.get::<i64>().is_some());\n        assert!(map.get::<i128>().is_some());\n        assert!(map.get::<u8>().is_some());\n        assert!(map.get::<u16>().is_some());\n        assert!(map.get::<u32>().is_some());\n        assert!(map.get::<u64>().is_some());\n        assert!(map.get::<u128>().is_some());\n        assert!(map.get::<&'static u32>().is_some());\n    }\n\n    #[test]\n    fn test_composition() {\n        struct Magi<T>(pub T);\n\n        struct Madoka {\n            pub god: bool,\n        }\n\n        struct Homura {\n            pub attempts: usize,\n        }\n\n        struct Mami {\n            pub guns: usize,\n        }\n\n        let mut map = Extensions::new();\n\n        map.insert(Magi(Madoka { god: false }));\n        map.insert(Magi(Homura { attempts: 0 }));\n        map.insert(Magi(Mami { guns: 999 }));\n\n        assert!(!map.get::<Magi<Madoka>>().unwrap().0.god);\n        assert_eq!(0, map.get::<Magi<Homura>>().unwrap().0.attempts);\n        assert_eq!(999, map.get::<Magi<Mami>>().unwrap().0.guns);\n    }\n\n    #[test]\n    fn test_extensions() {\n        #[derive(Debug, PartialEq)]\n        struct MyType(i32);\n\n        let mut extensions = Extensions::new();\n\n        extensions.insert(5i32);\n        extensions.insert(MyType(10));\n\n        assert_eq!(extensions.get(), Some(&5i32));\n        assert_eq!(extensions.get_mut(), Some(&mut 5i32));\n\n        assert_eq!(extensions.remove::<i32>(), Some(5i32));\n        assert!(extensions.get::<i32>().is_none());\n\n        assert_eq!(extensions.get::<bool>(), None);\n        assert_eq!(extensions.get(), Some(&MyType(10)));\n    }\n\n    #[test]\n    fn test_extend() {\n        #[derive(Debug, PartialEq)]\n        struct MyType(i32);\n\n        let mut extensions = Extensions::new();\n\n        extensions.insert(5i32);\n        extensions.insert(MyType(10));\n\n        let mut other = Extensions::new();\n\n        other.insert(15i32);\n        other.insert(20u8);\n\n        extensions.extend(other);\n\n        assert_eq!(extensions.get(), Some(&15i32));\n        assert_eq!(extensions.get_mut(), Some(&mut 15i32));\n\n        assert_eq!(extensions.remove::<i32>(), Some(15i32));\n        assert!(extensions.get::<i32>().is_none());\n\n        assert_eq!(extensions.get::<bool>(), None);\n        assert_eq!(extensions.get(), Some(&MyType(10)));\n\n        assert_eq!(extensions.get(), Some(&20u8));\n        assert_eq!(extensions.get_mut(), Some(&mut 20u8));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/chunked.rs",
    "content": "use std::{io, task::Poll};\n\nuse bytes::{Buf as _, Bytes, BytesMut};\nuse tracing::{debug, trace};\n\nmacro_rules! byte (\n    ($rdr:ident) => ({\n        if $rdr.len() > 0 {\n            let b = $rdr[0];\n            $rdr.advance(1);\n            b\n        } else {\n            return Poll::Pending\n        }\n    })\n);\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub(super) enum ChunkedState {\n    Size,\n    SizeLws,\n    Extension,\n    SizeLf,\n    Body,\n    BodyCr,\n    BodyLf,\n    EndCr,\n    EndLf,\n    End,\n}\n\nimpl ChunkedState {\n    pub(super) fn step(\n        &self,\n        body: &mut BytesMut,\n        size: &mut u64,\n        buf: &mut Option<Bytes>,\n    ) -> Poll<Result<ChunkedState, io::Error>> {\n        use self::ChunkedState::*;\n        match *self {\n            Size => ChunkedState::read_size(body, size),\n            SizeLws => ChunkedState::read_size_lws(body),\n            Extension => ChunkedState::read_extension(body),\n            SizeLf => ChunkedState::read_size_lf(body, *size),\n            Body => ChunkedState::read_body(body, size, buf),\n            BodyCr => ChunkedState::read_body_cr(body),\n            BodyLf => ChunkedState::read_body_lf(body),\n            EndCr => ChunkedState::read_end_cr(body),\n            EndLf => ChunkedState::read_end_lf(body),\n            End => Poll::Ready(Ok(ChunkedState::End)),\n        }\n    }\n\n    fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll<Result<ChunkedState, io::Error>> {\n        let radix = 16;\n\n        let rem = match byte!(rdr) {\n            b @ b'0'..=b'9' => b - b'0',\n            b @ b'a'..=b'f' => b + 10 - b'a',\n            b @ b'A'..=b'F' => b + 10 - b'A',\n            b'\\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),\n            b';' => return Poll::Ready(Ok(ChunkedState::Extension)),\n            b'\\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),\n            _ => {\n                return Poll::Ready(Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"Invalid chunk size line: Invalid Size\",\n                )));\n            }\n        };\n\n        match size.checked_mul(radix) {\n            Some(n) => {\n                *size = n;\n                *size += rem as u64;\n\n                Poll::Ready(Ok(ChunkedState::Size))\n            }\n            None => {\n                debug!(\"chunk size would overflow u64\");\n                Poll::Ready(Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"Invalid chunk size line: Size is too big\",\n                )))\n            }\n        }\n    }\n\n    fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            // LWS can follow the chunk size, but no more digits can come\n            b'\\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),\n            b';' => Poll::Ready(Ok(ChunkedState::Extension)),\n            b'\\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk size linear white space\",\n            ))),\n        }\n    }\n    fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),\n            // strictly 0x20 (space) should be disallowed but we don't parse quoted strings here\n            0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid character in chunk extension\",\n            ))),\n            _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions\n        }\n    }\n    fn read_size_lf(rdr: &mut BytesMut, size: u64) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\n' if size > 0 => Poll::Ready(Ok(ChunkedState::Body)),\n            b'\\n' if size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk size LF\",\n            ))),\n        }\n    }\n\n    fn read_body(\n        rdr: &mut BytesMut,\n        rem: &mut u64,\n        buf: &mut Option<Bytes>,\n    ) -> Poll<Result<ChunkedState, io::Error>> {\n        trace!(\"Chunked read, remaining={:?}\", rem);\n\n        let len = rdr.len() as u64;\n        if len == 0 {\n            Poll::Ready(Ok(ChunkedState::Body))\n        } else {\n            let slice;\n            if *rem > len {\n                slice = rdr.split().freeze();\n                *rem -= len;\n            } else {\n                slice = rdr.split_to(*rem as usize).freeze();\n                *rem = 0;\n            }\n            *buf = Some(slice);\n            if *rem > 0 {\n                Poll::Ready(Ok(ChunkedState::Body))\n            } else {\n                Poll::Ready(Ok(ChunkedState::BodyCr))\n            }\n        }\n    }\n\n    fn read_body_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk body CR\",\n            ))),\n        }\n    }\n    fn read_body_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\n' => Poll::Ready(Ok(ChunkedState::Size)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk body LF\",\n            ))),\n        }\n    }\n    fn read_end_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::EndLf)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk end CR\",\n            ))),\n        }\n    }\n    fn read_end_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\n' => Poll::Ready(Ok(ChunkedState::End)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk end LF\",\n            ))),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_codec::Decoder as _;\n    use bytes::{Bytes, BytesMut};\n    use http::Method;\n\n    use crate::{\n        error::ParseError,\n        h1::decoder::{MessageDecoder, PayloadItem},\n        HttpMessage as _, Request,\n    };\n\n    macro_rules! parse_ready {\n        ($e:expr) => {{\n            match MessageDecoder::<Request>::default().decode($e) {\n                Ok(Some((msg, _))) => msg,\n                Ok(_) => unreachable!(\"Eof during parsing http request\"),\n                Err(err) => unreachable!(\"Error during parsing http request: {:?}\", err),\n            }\n        }};\n    }\n\n    macro_rules! expect_parse_err {\n        ($e:expr) => {{\n            match MessageDecoder::<Request>::default().decode($e) {\n                Err(err) => match err {\n                    ParseError::Io(_) => unreachable!(\"Parse error expected\"),\n                    _ => {}\n                },\n                _ => unreachable!(\"Error expected\"),\n            }\n        }};\n    }\n\n    #[test]\n    fn test_parse_chunked_payload_chunk_extension() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n            transfer-encoding: chunked\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert!(msg.chunked().unwrap());\n\n        buf.extend(b\"4;test\\r\\ndata\\r\\n4\\r\\nline\\r\\n0\\r\\n\\r\\n\"); // test: test\\r\\n\\r\\n\")\n        let chunk = pl.decode(&mut buf).unwrap().unwrap().chunk();\n        assert_eq!(chunk, Bytes::from_static(b\"data\"));\n        let chunk = pl.decode(&mut buf).unwrap().unwrap().chunk();\n        assert_eq!(chunk, Bytes::from_static(b\"line\"));\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert!(msg.eof());\n    }\n\n    #[test]\n    fn test_request_chunked() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chunked\\r\\n\\r\\n\",\n        );\n        let req = parse_ready!(&mut buf);\n\n        if let Ok(val) = req.chunked() {\n            assert!(val);\n        } else {\n            unreachable!(\"Error\");\n        }\n\n        // intentional typo in \"chunked\"\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chnked\\r\\n\\r\\n\",\n        );\n        expect_parse_err!(&mut buf);\n    }\n\n    #[test]\n    fn test_http_request_chunked_payload() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chunked\\r\\n\\r\\n\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert!(req.chunked().unwrap());\n\n        buf.extend(b\"4\\r\\ndata\\r\\n4\\r\\nline\\r\\n0\\r\\n\\r\\n\");\n        assert_eq!(\n            pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),\n            b\"data\"\n        );\n        assert_eq!(\n            pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),\n            b\"line\"\n        );\n        assert!(pl.decode(&mut buf).unwrap().unwrap().eof());\n    }\n\n    #[test]\n    fn test_http_request_chunked_payload_and_next_message() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chunked\\r\\n\\r\\n\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert!(req.chunked().unwrap());\n\n        buf.extend(\n            b\"4\\r\\ndata\\r\\n4\\r\\nline\\r\\n0\\r\\n\\r\\n\\\n              POST /test2 HTTP/1.1\\r\\n\\\n              transfer-encoding: chunked\\r\\n\\r\\n\"\n                .iter(),\n        );\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"data\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"line\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert!(msg.eof());\n\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert!(req.chunked().unwrap());\n        assert_eq!(*req.method(), Method::POST);\n        assert!(req.chunked().unwrap());\n    }\n\n    #[test]\n    fn test_http_request_chunked_payload_chunks() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chunked\\r\\n\\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert!(req.chunked().unwrap());\n\n        buf.extend(b\"4\\r\\n1111\\r\\n\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"1111\");\n\n        buf.extend(b\"4\\r\\ndata\\r\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"data\");\n\n        buf.extend(b\"\\n4\");\n        assert!(pl.decode(&mut buf).unwrap().is_none());\n\n        buf.extend(b\"\\r\");\n        assert!(pl.decode(&mut buf).unwrap().is_none());\n        buf.extend(b\"\\n\");\n        assert!(pl.decode(&mut buf).unwrap().is_none());\n\n        buf.extend(b\"li\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"li\");\n\n        //trailers\n        //buf.feed_data(\"test: test\\r\\n\");\n        //not_ready!(reader.parse(&mut buf, &mut readbuf));\n\n        buf.extend(b\"ne\\r\\n0\\r\\n\");\n        let msg = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"ne\");\n        assert!(pl.decode(&mut buf).unwrap().is_none());\n\n        buf.extend(b\"\\r\\n\");\n        assert!(pl.decode(&mut buf).unwrap().unwrap().eof());\n    }\n\n    #[test]\n    fn chunk_extension_quoted() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n            Host: localhost:8080\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            2;hello=b;one=\\\"1 2 3\\\"\\r\\n\\\n            xx\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n\n        let chunk = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(chunk, PayloadItem::Chunk(Bytes::from_static(b\"xx\")));\n    }\n\n    #[test]\n    fn hrs_chunk_extension_invalid() {\n        let mut buf = BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: localhost:8080\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            2;x\\nx\\r\\n\\\n            4c\\r\\n\\\n            0\\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n\n        let err = pl.decode(&mut buf).unwrap_err();\n        assert!(err\n            .to_string()\n            .contains(\"Invalid character in chunk extension\"));\n    }\n\n    #[test]\n    fn hrs_chunk_size_overflow() {\n        let mut buf = BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            f0000000000000003\\r\\n\\\n            abc\\r\\n\\\n            0\\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n\n        let err = pl.decode(&mut buf).unwrap_err();\n        assert!(err\n            .to_string()\n            .contains(\"Invalid chunk size line: Size is too big\"));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/client.rs",
    "content": "use std::{fmt, io};\n\nuse bitflags::bitflags;\nuse bytes::{Bytes, BytesMut};\nuse http::{Method, Version};\nuse tokio_util::codec::{Decoder, Encoder};\n\nuse super::{\n    decoder::{self, PayloadDecoder, PayloadItem, PayloadType},\n    encoder, reserve_readbuf, Message, MessageType,\n};\nuse crate::{\n    body::BodySize,\n    error::{ParseError, PayloadError},\n    ConnectionType, RequestHeadType, ResponseHead, ServiceConfig,\n};\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    struct Flags: u8 {\n        const HEAD               = 0b0000_0001;\n        const KEEP_ALIVE_ENABLED = 0b0000_1000;\n        const STREAM             = 0b0001_0000;\n    }\n}\n\n/// HTTP/1 Codec\npub struct ClientCodec {\n    inner: ClientCodecInner,\n}\n\n/// HTTP/1 Payload Codec\npub struct ClientPayloadCodec {\n    inner: ClientCodecInner,\n}\n\nstruct ClientCodecInner {\n    config: ServiceConfig,\n    decoder: decoder::MessageDecoder<ResponseHead>,\n    payload: Option<PayloadDecoder>,\n    version: Version,\n    conn_type: ConnectionType,\n\n    // encoder part\n    flags: Flags,\n    encoder: encoder::MessageEncoder<RequestHeadType>,\n}\n\nimpl Default for ClientCodec {\n    fn default() -> Self {\n        ClientCodec::new(ServiceConfig::default())\n    }\n}\n\nimpl fmt::Debug for ClientCodec {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"h1::ClientCodec\")\n            .field(\"flags\", &self.inner.flags)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl ClientCodec {\n    /// Create HTTP/1 codec.\n    ///\n    /// `keepalive_enabled` how response `connection` header get generated.\n    pub fn new(config: ServiceConfig) -> Self {\n        let flags = if config.keep_alive().enabled() {\n            Flags::KEEP_ALIVE_ENABLED\n        } else {\n            Flags::empty()\n        };\n\n        ClientCodec {\n            inner: ClientCodecInner {\n                config,\n                decoder: decoder::MessageDecoder::default(),\n                payload: None,\n                version: Version::HTTP_11,\n                conn_type: ConnectionType::Close,\n\n                flags,\n                encoder: encoder::MessageEncoder::default(),\n            },\n        }\n    }\n\n    /// Check if request is upgrade\n    pub fn upgrade(&self) -> bool {\n        self.inner.conn_type == ConnectionType::Upgrade\n    }\n\n    /// Check if last response is keep-alive\n    pub fn keep_alive(&self) -> bool {\n        self.inner.conn_type == ConnectionType::KeepAlive\n    }\n\n    /// Check last request's message type\n    pub fn message_type(&self) -> MessageType {\n        if self.inner.flags.contains(Flags::STREAM) {\n            MessageType::Stream\n        } else if self.inner.payload.is_none() {\n            MessageType::None\n        } else {\n            MessageType::Payload\n        }\n    }\n\n    /// Convert message codec to a payload codec\n    pub fn into_payload_codec(self) -> ClientPayloadCodec {\n        ClientPayloadCodec { inner: self.inner }\n    }\n}\n\nimpl ClientPayloadCodec {\n    /// Check if last response is keep-alive\n    pub fn keep_alive(&self) -> bool {\n        self.inner.conn_type == ConnectionType::KeepAlive\n    }\n\n    /// Transform payload codec to a message codec\n    pub fn into_message_codec(self) -> ClientCodec {\n        ClientCodec { inner: self.inner }\n    }\n}\n\nimpl Decoder for ClientCodec {\n    type Item = ResponseHead;\n    type Error = ParseError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        debug_assert!(\n            self.inner.payload.is_none(),\n            \"Payload decoder should not be set\"\n        );\n\n        if let Some((req, payload)) = self.inner.decoder.decode(src)? {\n            if let Some(conn_type) = req.conn_type() {\n                // do not use peer's keep-alive\n                self.inner.conn_type = if conn_type == ConnectionType::KeepAlive {\n                    self.inner.conn_type\n                } else {\n                    conn_type\n                };\n            }\n\n            if !self.inner.flags.contains(Flags::HEAD) {\n                match payload {\n                    PayloadType::None => self.inner.payload = None,\n                    PayloadType::Payload(pl) => self.inner.payload = Some(pl),\n                    PayloadType::Stream(pl) => {\n                        self.inner.payload = Some(pl);\n                        self.inner.flags.insert(Flags::STREAM);\n                    }\n                }\n            } else {\n                self.inner.payload = None;\n            }\n            reserve_readbuf(src);\n            Ok(Some(req))\n        } else {\n            Ok(None)\n        }\n    }\n}\n\nimpl Decoder for ClientPayloadCodec {\n    type Item = Option<Bytes>;\n    type Error = PayloadError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        debug_assert!(\n            self.inner.payload.is_some(),\n            \"Payload decoder is not specified\"\n        );\n\n        Ok(match self.inner.payload.as_mut().unwrap().decode(src)? {\n            Some(PayloadItem::Chunk(chunk)) => {\n                reserve_readbuf(src);\n                Some(Some(chunk))\n            }\n            Some(PayloadItem::Eof) => {\n                self.inner.payload.take();\n                Some(None)\n            }\n            None => None,\n        })\n    }\n}\n\nimpl Encoder<Message<(RequestHeadType, BodySize)>> for ClientCodec {\n    type Error = io::Error;\n\n    fn encode(\n        &mut self,\n        item: Message<(RequestHeadType, BodySize)>,\n        dst: &mut BytesMut,\n    ) -> Result<(), Self::Error> {\n        match item {\n            Message::Item((mut head, length)) => {\n                let inner = &mut self.inner;\n                inner.version = head.as_ref().version;\n                inner\n                    .flags\n                    .set(Flags::HEAD, head.as_ref().method == Method::HEAD);\n\n                // connection status\n                inner.conn_type = match head.as_ref().connection_type() {\n                    ConnectionType::KeepAlive => {\n                        if inner.flags.contains(Flags::KEEP_ALIVE_ENABLED) {\n                            ConnectionType::KeepAlive\n                        } else {\n                            ConnectionType::Close\n                        }\n                    }\n                    ConnectionType::Upgrade => ConnectionType::Upgrade,\n                    ConnectionType::Close => ConnectionType::Close,\n                };\n\n                inner.encoder.encode(\n                    dst,\n                    &mut head,\n                    false,\n                    false,\n                    inner.version,\n                    length,\n                    inner.conn_type,\n                    &inner.config,\n                )?;\n            }\n            Message::Chunk(Some(bytes)) => {\n                self.inner.encoder.encode_chunk(bytes.as_ref(), dst)?;\n            }\n            Message::Chunk(None) => {\n                self.inner.encoder.encode_eof(dst)?;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/codec.rs",
    "content": "use std::{fmt, io};\n\nuse bitflags::bitflags;\nuse bytes::BytesMut;\nuse http::{Method, Version};\nuse tokio_util::codec::{Decoder, Encoder};\n\nuse super::{\n    decoder::{self, PayloadDecoder, PayloadItem, PayloadType},\n    encoder, Message, MessageType,\n};\nuse crate::{body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig};\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    struct Flags: u8 {\n        const HEAD               = 0b0000_0001;\n        const KEEP_ALIVE_ENABLED = 0b0000_0010;\n        const STREAM             = 0b0000_0100;\n    }\n}\n\n/// HTTP/1 Codec\npub struct Codec {\n    config: ServiceConfig,\n    decoder: decoder::MessageDecoder<Request>,\n    payload: Option<PayloadDecoder>,\n    version: Version,\n    conn_type: ConnectionType,\n\n    // encoder part\n    flags: Flags,\n    encoder: encoder::MessageEncoder<Response<()>>,\n}\n\nimpl Default for Codec {\n    fn default() -> Self {\n        Codec::new(ServiceConfig::default())\n    }\n}\n\nimpl fmt::Debug for Codec {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"h1::Codec\")\n            .field(\"flags\", &self.flags)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl Codec {\n    /// Create HTTP/1 codec.\n    ///\n    /// `keepalive_enabled` how response `connection` header get generated.\n    pub fn new(config: ServiceConfig) -> Self {\n        let flags = if config.keep_alive().enabled() {\n            Flags::KEEP_ALIVE_ENABLED\n        } else {\n            Flags::empty()\n        };\n\n        Codec {\n            config,\n            flags,\n            decoder: decoder::MessageDecoder::default(),\n            payload: None,\n            version: Version::HTTP_11,\n            conn_type: ConnectionType::Close,\n            encoder: encoder::MessageEncoder::default(),\n        }\n    }\n\n    /// Check if request is upgrade.\n    #[inline]\n    pub fn upgrade(&self) -> bool {\n        self.conn_type == ConnectionType::Upgrade\n    }\n\n    /// Check if last response is keep-alive.\n    #[inline]\n    pub fn keep_alive(&self) -> bool {\n        self.conn_type == ConnectionType::KeepAlive\n    }\n\n    /// Check if keep-alive enabled on server level.\n    #[inline]\n    pub fn keep_alive_enabled(&self) -> bool {\n        self.flags.contains(Flags::KEEP_ALIVE_ENABLED)\n    }\n\n    /// Check last request's message type.\n    #[inline]\n    pub fn message_type(&self) -> MessageType {\n        if self.flags.contains(Flags::STREAM) {\n            MessageType::Stream\n        } else if self.payload.is_none() {\n            MessageType::None\n        } else {\n            MessageType::Payload\n        }\n    }\n\n    #[inline]\n    pub fn config(&self) -> &ServiceConfig {\n        &self.config\n    }\n}\n\nimpl Decoder for Codec {\n    type Item = Message<Request>;\n    type Error = ParseError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        if let Some(ref mut payload) = self.payload {\n            Ok(match payload.decode(src)? {\n                Some(PayloadItem::Chunk(chunk)) => Some(Message::Chunk(Some(chunk))),\n                Some(PayloadItem::Eof) => {\n                    self.payload.take();\n                    Some(Message::Chunk(None))\n                }\n                None => None,\n            })\n        } else if let Some((req, payload)) = self.decoder.decode(src)? {\n            let head = req.head();\n            self.flags.set(Flags::HEAD, head.method == Method::HEAD);\n            self.version = head.version;\n            self.conn_type = head.connection_type();\n\n            if self.conn_type == ConnectionType::KeepAlive\n                && !self.flags.contains(Flags::KEEP_ALIVE_ENABLED)\n            {\n                self.conn_type = ConnectionType::Close\n            }\n\n            match payload {\n                PayloadType::None => self.payload = None,\n                PayloadType::Payload(pl) => self.payload = Some(pl),\n                PayloadType::Stream(pl) => {\n                    self.payload = Some(pl);\n                    self.flags.insert(Flags::STREAM);\n                }\n            }\n            Ok(Some(Message::Item(req)))\n        } else {\n            Ok(None)\n        }\n    }\n}\n\nimpl Encoder<Message<(Response<()>, BodySize)>> for Codec {\n    type Error = io::Error;\n\n    fn encode(\n        &mut self,\n        item: Message<(Response<()>, BodySize)>,\n        dst: &mut BytesMut,\n    ) -> Result<(), Self::Error> {\n        match item {\n            Message::Item((mut res, length)) => {\n                // set response version\n                res.head_mut().version = self.version;\n\n                // connection status\n                self.conn_type = if let Some(ct) = res.head().conn_type() {\n                    if ct == ConnectionType::KeepAlive {\n                        self.conn_type\n                    } else {\n                        ct\n                    }\n                } else {\n                    self.conn_type\n                };\n\n                // encode message\n                self.encoder.encode(\n                    dst,\n                    &mut res,\n                    self.flags.contains(Flags::HEAD),\n                    self.flags.contains(Flags::STREAM),\n                    self.version,\n                    length,\n                    self.conn_type,\n                    &self.config,\n                )?;\n            }\n\n            Message::Chunk(Some(bytes)) => {\n                self.encoder.encode_chunk(bytes.as_ref(), dst)?;\n            }\n\n            Message::Chunk(None) => {\n                self.encoder.encode_eof(dst)?;\n            }\n        }\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::HttpMessage as _;\n\n    #[actix_rt::test]\n    async fn test_http_request_chunked_payload_and_next_message() {\n        let mut codec = Codec::default();\n\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             transfer-encoding: chunked\\r\\n\\r\\n\",\n        );\n        let item = codec.decode(&mut buf).unwrap().unwrap();\n        let req = item.message();\n\n        assert_eq!(req.method(), Method::GET);\n        assert!(req.chunked().unwrap());\n\n        buf.extend(\n            b\"4\\r\\ndata\\r\\n4\\r\\nline\\r\\n0\\r\\n\\r\\n\\\n               POST /test2 HTTP/1.1\\r\\n\\\n               transfer-encoding: chunked\\r\\n\\r\\n\"\n                .iter(),\n        );\n\n        let msg = codec.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"data\");\n\n        let msg = codec.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(msg.chunk().as_ref(), b\"line\");\n\n        let msg = codec.decode(&mut buf).unwrap().unwrap();\n        assert!(msg.eof());\n\n        // decode next message\n        let item = codec.decode(&mut buf).unwrap().unwrap();\n        let req = item.message();\n        assert_eq!(*req.method(), Method::POST);\n        assert!(req.chunked().unwrap());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/decoder.rs",
    "content": "use std::{io, marker::PhantomData, mem::MaybeUninit, task::Poll};\n\nuse actix_codec::Decoder;\nuse bytes::{Bytes, BytesMut};\nuse http::{\n    header::{self, HeaderName, HeaderValue},\n    Method, StatusCode, Uri, Version,\n};\nuse tracing::{debug, error, trace};\n\nuse super::chunked::ChunkedState;\nuse crate::{error::ParseError, header::HeaderMap, ConnectionType, Request, ResponseHead};\n\npub(crate) const MAX_BUFFER_SIZE: usize = 131_072;\nconst MAX_HEADERS: usize = 96;\n\n/// Incoming message decoder\npub(crate) struct MessageDecoder<T: MessageType>(PhantomData<T>);\n\n#[derive(Debug)]\n/// Incoming request type\npub(crate) enum PayloadType {\n    None,\n    Payload(PayloadDecoder),\n    Stream(PayloadDecoder),\n}\n\nimpl<T: MessageType> Default for MessageDecoder<T> {\n    fn default() -> Self {\n        MessageDecoder(PhantomData)\n    }\n}\n\nimpl<T: MessageType> Decoder for MessageDecoder<T> {\n    type Item = (T, PayloadType);\n    type Error = ParseError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        T::decode(src)\n    }\n}\n\npub(crate) enum PayloadLength {\n    Payload(PayloadType),\n    UpgradeWebSocket,\n    None,\n}\n\nimpl PayloadLength {\n    /// Returns true if variant is `None`.\n    fn is_none(&self) -> bool {\n        matches!(self, Self::None)\n    }\n\n    /// Returns true if variant is represents zero-length (not none) payload.\n    fn is_zero(&self) -> bool {\n        matches!(\n            self,\n            PayloadLength::Payload(PayloadType::Payload(PayloadDecoder {\n                kind: Kind::Length(0)\n            }))\n        )\n    }\n}\n\npub(crate) trait MessageType: Sized {\n    fn set_connection_type(&mut self, conn_type: Option<ConnectionType>);\n\n    fn set_expect(&mut self);\n\n    fn headers_mut(&mut self) -> &mut HeaderMap;\n\n    fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError>;\n\n    fn set_headers(\n        &mut self,\n        slice: &Bytes,\n        raw_headers: &[HeaderIndex],\n        version: Version,\n    ) -> Result<PayloadLength, ParseError> {\n        let mut ka = None;\n        let mut has_upgrade_websocket = false;\n        let mut expect = false;\n        let mut chunked = false;\n        let mut seen_te = false;\n        let mut content_length = None;\n\n        {\n            let headers = self.headers_mut();\n\n            for idx in raw_headers.iter() {\n                let name = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();\n\n                // SAFETY: httparse already checks header value is only visible ASCII bytes\n                // from_maybe_shared_unchecked contains debug assertions so they are omitted here\n                let value = unsafe {\n                    HeaderValue::from_maybe_shared_unchecked(slice.slice(idx.value.0..idx.value.1))\n                };\n\n                match name {\n                    header::CONTENT_LENGTH if content_length.is_some() => {\n                        debug!(\"multiple Content-Length\");\n                        return Err(ParseError::Header);\n                    }\n\n                    header::CONTENT_LENGTH => match value.to_str().map(str::trim) {\n                        Ok(val) if val.starts_with('+') => {\n                            debug!(\"illegal Content-Length: {:?}\", val);\n                            return Err(ParseError::Header);\n                        }\n\n                        Ok(val) => {\n                            if let Ok(len) = val.parse::<u64>() {\n                                // accept 0 lengths here and remove them in `decode` after all\n                                // headers have been processed to prevent request smuggling issues\n                                content_length = Some(len);\n                            } else {\n                                debug!(\"illegal Content-Length: {:?}\", val);\n                                return Err(ParseError::Header);\n                            }\n                        }\n\n                        Err(_) => {\n                            debug!(\"illegal Content-Length: {:?}\", value);\n                            return Err(ParseError::Header);\n                        }\n                    },\n\n                    // transfer-encoding\n                    header::TRANSFER_ENCODING if seen_te => {\n                        debug!(\"multiple Transfer-Encoding not allowed\");\n                        return Err(ParseError::Header);\n                    }\n\n                    header::TRANSFER_ENCODING if version == Version::HTTP_11 => {\n                        seen_te = true;\n\n                        if let Ok(val) = value.to_str().map(str::trim) {\n                            if val.eq_ignore_ascii_case(\"chunked\") {\n                                chunked = true;\n                            } else if val.eq_ignore_ascii_case(\"identity\") {\n                                // allow silently since multiple TE headers are already checked\n                            } else {\n                                debug!(\"illegal Transfer-Encoding: {:?}\", val);\n                                return Err(ParseError::Header);\n                            }\n                        } else {\n                            return Err(ParseError::Header);\n                        }\n                    }\n\n                    // connection keep-alive state\n                    header::CONNECTION => {\n                        ka = if let Ok(conn) = value.to_str().map(str::trim) {\n                            if conn.eq_ignore_ascii_case(\"keep-alive\") {\n                                Some(ConnectionType::KeepAlive)\n                            } else if conn.eq_ignore_ascii_case(\"close\") {\n                                Some(ConnectionType::Close)\n                            } else if conn.eq_ignore_ascii_case(\"upgrade\") {\n                                Some(ConnectionType::Upgrade)\n                            } else {\n                                None\n                            }\n                        } else {\n                            None\n                        };\n                    }\n\n                    header::UPGRADE => {\n                        if let Ok(val) = value.to_str().map(str::trim) {\n                            if val.eq_ignore_ascii_case(\"websocket\") {\n                                has_upgrade_websocket = true;\n                            }\n                        }\n                    }\n\n                    header::EXPECT => {\n                        let bytes = value.as_bytes();\n                        if bytes.len() >= 4 && &bytes[0..4] == b\"100-\" {\n                            expect = true;\n                        }\n                    }\n\n                    _ => {}\n                }\n\n                headers.append(name, value);\n            }\n        }\n\n        self.set_connection_type(ka);\n\n        if expect {\n            self.set_expect()\n        }\n\n        // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3\n        if chunked {\n            // Chunked encoding\n            Ok(PayloadLength::Payload(PayloadType::Payload(\n                PayloadDecoder::chunked(),\n            )))\n        } else if has_upgrade_websocket {\n            Ok(PayloadLength::UpgradeWebSocket)\n        } else if let Some(len) = content_length {\n            // Content-Length\n            Ok(PayloadLength::Payload(PayloadType::Payload(\n                PayloadDecoder::length(len),\n            )))\n        } else {\n            Ok(PayloadLength::None)\n        }\n    }\n}\n\nimpl MessageType for Request {\n    fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {\n        if let Some(ctype) = conn_type {\n            self.head_mut().set_connection_type(ctype);\n        }\n    }\n\n    fn set_expect(&mut self) {\n        self.head_mut().set_expect();\n    }\n\n    fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.head_mut().headers\n    }\n\n    fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {\n        let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;\n\n        let (len, method, uri, ver, h_len) = {\n            // SAFETY:\n            // Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe because the\n            // type we are claiming to have initialized here is a bunch of `MaybeUninit`s, which\n            // do not require initialization.\n            let mut parsed = unsafe {\n                MaybeUninit::<[MaybeUninit<httparse::Header<'_>>; MAX_HEADERS]>::uninit()\n                    .assume_init()\n            };\n\n            let mut req = httparse::Request::new(&mut []);\n\n            match req.parse_with_uninit_headers(src, &mut parsed)? {\n                httparse::Status::Complete(len) => {\n                    let method = Method::from_bytes(req.method.unwrap().as_bytes())\n                        .map_err(|_| ParseError::Method)?;\n                    let uri = Uri::try_from(req.path.unwrap())?;\n                    let version = if req.version.unwrap() == 1 {\n                        Version::HTTP_11\n                    } else {\n                        Version::HTTP_10\n                    };\n                    HeaderIndex::record(src, req.headers, &mut headers);\n\n                    (len, method, uri, version, req.headers.len())\n                }\n\n                httparse::Status::Partial => {\n                    return if src.len() >= MAX_BUFFER_SIZE {\n                        trace!(\"MAX_BUFFER_SIZE unprocessed data reached, closing\");\n                        Err(ParseError::TooLarge)\n                    } else {\n                        // Return None to notify more read are needed for parsing request\n                        Ok(None)\n                    };\n                }\n            }\n        };\n\n        let mut msg = Request::new();\n\n        // convert headers\n        let mut length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;\n\n        // disallow HTTP/1.0 POST requests that do not contain a Content-Length headers\n        // see https://datatracker.ietf.org/doc/html/rfc1945#section-7.2.2\n        if ver == Version::HTTP_10 && method == Method::POST && length.is_none() {\n            debug!(\"no Content-Length specified for HTTP/1.0 POST request\");\n            return Err(ParseError::Header);\n        }\n\n        // Remove CL value if 0 now that all headers and HTTP/1.0 special cases are processed.\n        // Protects against some request smuggling attacks.\n        // See https://github.com/actix/actix-web/issues/2767.\n        if length.is_zero() {\n            length = PayloadLength::None;\n        }\n\n        // payload decoder\n        let decoder = match length {\n            PayloadLength::Payload(pl) => pl,\n            PayloadLength::UpgradeWebSocket => {\n                // upgrade (WebSocket)\n                PayloadType::Stream(PayloadDecoder::eof())\n            }\n            PayloadLength::None => {\n                if method == Method::CONNECT {\n                    PayloadType::Stream(PayloadDecoder::eof())\n                } else {\n                    PayloadType::None\n                }\n            }\n        };\n\n        let head = msg.head_mut();\n        head.uri = uri;\n        head.method = method;\n        head.version = ver;\n\n        Ok(Some((msg, decoder)))\n    }\n}\n\nimpl MessageType for ResponseHead {\n    fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {\n        if let Some(ctype) = conn_type {\n            ResponseHead::set_connection_type(self, ctype);\n        }\n    }\n\n    fn set_expect(&mut self) {}\n\n    fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.headers\n    }\n\n    fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {\n        let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;\n\n        let (len, ver, status, h_len) = {\n            // SAFETY:\n            // Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe because the\n            // type we are claiming to have initialized here is a bunch of `MaybeUninit`s, which\n            // do not require initialization.\n            let mut parsed = unsafe {\n                MaybeUninit::<[MaybeUninit<httparse::Header<'_>>; MAX_HEADERS]>::uninit()\n                    .assume_init()\n            };\n\n            let mut res = httparse::Response::new(&mut []);\n\n            let mut config = httparse::ParserConfig::default();\n            config.allow_spaces_after_header_name_in_responses(true);\n\n            match config.parse_response_with_uninit_headers(&mut res, src, &mut parsed)? {\n                httparse::Status::Complete(len) => {\n                    let version = if res.version.unwrap() == 1 {\n                        Version::HTTP_11\n                    } else {\n                        Version::HTTP_10\n                    };\n\n                    let status =\n                        StatusCode::from_u16(res.code.unwrap()).map_err(|_| ParseError::Status)?;\n                    HeaderIndex::record(src, res.headers, &mut headers);\n\n                    (len, version, status, res.headers.len())\n                }\n\n                httparse::Status::Partial => {\n                    return if src.len() >= MAX_BUFFER_SIZE {\n                        error!(\"MAX_BUFFER_SIZE unprocessed data reached, closing\");\n                        Err(ParseError::TooLarge)\n                    } else {\n                        Ok(None)\n                    }\n                }\n            }\n        };\n\n        let mut msg = ResponseHead::new(status);\n        msg.version = ver;\n\n        // convert headers\n        let mut length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;\n\n        // Remove CL value if 0 now that all headers and HTTP/1.0 special cases are processed.\n        // Protects against some request smuggling attacks.\n        // See https://github.com/actix/actix-web/issues/2767.\n        if length.is_zero() {\n            length = PayloadLength::None;\n        }\n\n        // message payload\n        let decoder = if let PayloadLength::Payload(pl) = length {\n            pl\n        } else if status == StatusCode::SWITCHING_PROTOCOLS {\n            // switching protocol or connect\n            PayloadType::Stream(PayloadDecoder::eof())\n        } else {\n            // for HTTP/1.0 read to eof and close connection\n            if msg.version == Version::HTTP_10 {\n                msg.set_connection_type(ConnectionType::Close);\n                PayloadType::Payload(PayloadDecoder::eof())\n            } else {\n                PayloadType::None\n            }\n        };\n\n        Ok(Some((msg, decoder)))\n    }\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct HeaderIndex {\n    pub(crate) name: (usize, usize),\n    pub(crate) value: (usize, usize),\n}\n\npub(crate) const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex {\n    name: (0, 0),\n    value: (0, 0),\n};\n\npub(crate) const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] =\n    [EMPTY_HEADER_INDEX; MAX_HEADERS];\n\nimpl HeaderIndex {\n    pub(crate) fn record(\n        bytes: &[u8],\n        headers: &[httparse::Header<'_>],\n        indices: &mut [HeaderIndex],\n    ) {\n        let bytes_ptr = bytes.as_ptr() as usize;\n        for (header, indices) in headers.iter().zip(indices.iter_mut()) {\n            let name_start = header.name.as_ptr() as usize - bytes_ptr;\n            let name_end = name_start + header.name.len();\n            indices.name = (name_start, name_end);\n            let value_start = header.value.as_ptr() as usize - bytes_ptr;\n            let value_end = value_start + header.value.len();\n            indices.value = (value_start, value_end);\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n/// Chunk type yielded while decoding a payload.\npub enum PayloadItem {\n    Chunk(Bytes),\n    Eof,\n}\n\n/// Decoder that can handle different payload types.\n///\n/// If a message body does not use `Transfer-Encoding`, it should include a `Content-Length`.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct PayloadDecoder {\n    kind: Kind,\n}\n\nimpl PayloadDecoder {\n    /// Constructs a fixed-length payload decoder.\n    pub fn length(x: u64) -> PayloadDecoder {\n        PayloadDecoder {\n            kind: Kind::Length(x),\n        }\n    }\n\n    /// Constructs a chunked encoding decoder.\n    pub fn chunked() -> PayloadDecoder {\n        PayloadDecoder {\n            kind: Kind::Chunked(ChunkedState::Size, 0),\n        }\n    }\n\n    /// Creates an decoder that yields chunks until the stream returns EOF.\n    pub fn eof() -> PayloadDecoder {\n        PayloadDecoder { kind: Kind::Eof }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\nenum Kind {\n    /// A reader used when a `Content-Length` header is passed with a positive integer.\n    Length(u64),\n\n    /// A reader used when `Transfer-Encoding` is `chunked`.\n    Chunked(ChunkedState, u64),\n\n    /// A reader used for responses that don't indicate a length or chunked.\n    ///\n    /// Note: This should only used for `Response`s. It is illegal for a `Request` to be made\n    /// without either of `Content-Length` and `Transfer-Encoding: chunked` missing, as explained\n    /// in [RFC 7230 §3.3.3]:\n    ///\n    /// > If a Transfer-Encoding header field is present in a response and the chunked transfer\n    /// > coding is not the final encoding, the message body length is determined by reading the\n    /// > connection until it is closed by the server. If a Transfer-Encoding header field is\n    /// > present in a request and the chunked transfer coding is not the final encoding, the\n    /// > message body length cannot be determined reliably; the server MUST respond with the 400\n    /// > (Bad Request) status code and then close the connection.\n    ///\n    /// [RFC 7230 §3.3.3]: https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3\n    Eof,\n}\n\nimpl Decoder for PayloadDecoder {\n    type Item = PayloadItem;\n    type Error = io::Error;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        match self.kind {\n            Kind::Length(ref mut remaining) => {\n                if *remaining == 0 {\n                    Ok(Some(PayloadItem::Eof))\n                } else {\n                    if src.is_empty() {\n                        return Ok(None);\n                    }\n                    let len = src.len() as u64;\n                    let buf;\n                    if *remaining > len {\n                        buf = src.split().freeze();\n                        *remaining -= len;\n                    } else {\n                        buf = src.split_to(*remaining as usize).freeze();\n                        *remaining = 0;\n                    };\n                    trace!(\"Length read: {}\", buf.len());\n                    Ok(Some(PayloadItem::Chunk(buf)))\n                }\n            }\n\n            Kind::Chunked(ref mut state, ref mut size) => {\n                loop {\n                    let mut buf = None;\n\n                    // advances the chunked state\n                    *state = match state.step(src, size, &mut buf) {\n                        Poll::Pending => return Ok(None),\n                        Poll::Ready(Ok(state)) => state,\n                        Poll::Ready(Err(err)) => return Err(err),\n                    };\n\n                    if *state == ChunkedState::End {\n                        trace!(\"End of chunked stream\");\n                        return Ok(Some(PayloadItem::Eof));\n                    }\n\n                    if let Some(buf) = buf {\n                        return Ok(Some(PayloadItem::Chunk(buf)));\n                    }\n\n                    if src.is_empty() {\n                        return Ok(None);\n                    }\n                }\n            }\n\n            Kind::Eof => {\n                if src.is_empty() {\n                    Ok(None)\n                } else {\n                    Ok(Some(PayloadItem::Chunk(src.split().freeze())))\n                }\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{header::SET_COOKIE, HttpMessage as _};\n\n    impl PayloadType {\n        pub(crate) fn unwrap(self) -> PayloadDecoder {\n            match self {\n                PayloadType::Payload(pl) => pl,\n                _ => panic!(),\n            }\n        }\n\n        pub(crate) fn is_unhandled(&self) -> bool {\n            matches!(self, PayloadType::Stream(_))\n        }\n    }\n\n    impl PayloadItem {\n        pub(crate) fn chunk(self) -> Bytes {\n            match self {\n                PayloadItem::Chunk(chunk) => chunk,\n                _ => panic!(\"error\"),\n            }\n        }\n\n        pub(crate) fn eof(&self) -> bool {\n            matches!(*self, PayloadItem::Eof)\n        }\n    }\n\n    macro_rules! parse_ready {\n        ($e:expr) => {{\n            match MessageDecoder::<Request>::default().decode($e) {\n                Ok(Some((msg, _))) => msg,\n                Ok(_) => unreachable!(\"Eof during parsing http request\"),\n                Err(err) => unreachable!(\"Error during parsing http request: {:?}\", err),\n            }\n        }};\n    }\n\n    macro_rules! expect_parse_err {\n        ($e:expr) => {{\n            match MessageDecoder::<Request>::default().decode($e) {\n                Err(err) => match err {\n                    ParseError::Io(_) => unreachable!(\"Parse error expected\"),\n                    _ => {}\n                },\n                _ => unreachable!(\"Error expected\"),\n            }\n        }};\n    }\n\n    #[test]\n    fn test_parse() {\n        let mut buf = BytesMut::from(\"GET /test HTTP/1.1\\r\\n\\r\\n\");\n\n        let mut reader = MessageDecoder::<Request>::default();\n        match reader.decode(&mut buf) {\n            Ok(Some((req, _))) => {\n                assert_eq!(req.version(), Version::HTTP_11);\n                assert_eq!(*req.method(), Method::GET);\n                assert_eq!(req.path(), \"/test\");\n            }\n            Ok(_) | Err(_) => unreachable!(\"Error during parsing http request\"),\n        }\n    }\n\n    #[test]\n    fn test_parse_partial() {\n        let mut buf = BytesMut::from(\"PUT /test HTTP/1\");\n\n        let mut reader = MessageDecoder::<Request>::default();\n        assert!(reader.decode(&mut buf).unwrap().is_none());\n\n        buf.extend(b\".1\\r\\n\\r\\n\");\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_11);\n        assert_eq!(*req.method(), Method::PUT);\n        assert_eq!(req.path(), \"/test\");\n    }\n\n    #[test]\n    fn parse_h09_reject() {\n        let mut buf = BytesMut::from(\n            \"GET /test1 HTTP/0.9\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        reader.decode(&mut buf).unwrap_err();\n\n        let mut buf = BytesMut::from(\n            \"POST /test2 HTTP/0.9\\r\\n\\\n            Content-Length: 3\\r\\n\\\n            \\r\\n\n            abc\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        reader.decode(&mut buf).unwrap_err();\n    }\n\n    #[test]\n    fn parse_h10_get() {\n        let mut buf = BytesMut::from(\n            \"GET /test1 HTTP/1.0\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_10);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test1\");\n\n        let mut buf = BytesMut::from(\n            \"GET /test2 HTTP/1.0\\r\\n\\\n            Content-Length: 0\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_10);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test2\");\n\n        let mut buf = BytesMut::from(\n            \"GET /test3 HTTP/1.0\\r\\n\\\n            Content-Length: 3\\r\\n\\\n            \\r\\n\n            abc\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_10);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test3\");\n    }\n\n    #[test]\n    fn parse_h10_post() {\n        let mut buf = BytesMut::from(\n            \"POST /test1 HTTP/1.0\\r\\n\\\n            Content-Length: 3\\r\\n\\\n            \\r\\n\\\n            abc\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_10);\n        assert_eq!(*req.method(), Method::POST);\n        assert_eq!(req.path(), \"/test1\");\n\n        let mut buf = BytesMut::from(\n            \"POST /test2 HTTP/1.0\\r\\n\\\n            Content-Length: 0\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_10);\n        assert_eq!(*req.method(), Method::POST);\n        assert_eq!(req.path(), \"/test2\");\n\n        let mut buf = BytesMut::from(\n            \"POST /test3 HTTP/1.0\\r\\n\\\n            \\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let err = reader.decode(&mut buf).unwrap_err();\n        assert!(err.to_string().contains(\"Header\"))\n    }\n\n    #[test]\n    fn test_parse_body() {\n        let mut buf = BytesMut::from(\"GET /test HTTP/1.1\\r\\nContent-Length: 4\\r\\n\\r\\nbody\");\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert_eq!(req.version(), Version::HTTP_11);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test\");\n        assert_eq!(\n            pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),\n            b\"body\"\n        );\n    }\n\n    #[test]\n    fn test_parse_body_crlf() {\n        let mut buf = BytesMut::from(\"\\r\\nGET /test HTTP/1.1\\r\\nContent-Length: 4\\r\\n\\r\\nbody\");\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n        assert_eq!(req.version(), Version::HTTP_11);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test\");\n        assert_eq!(\n            pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),\n            b\"body\"\n        );\n    }\n\n    #[test]\n    fn test_parse_partial_eof() {\n        let mut buf = BytesMut::from(\"GET /test HTTP/1.1\\r\\n\");\n        let mut reader = MessageDecoder::<Request>::default();\n        assert!(reader.decode(&mut buf).unwrap().is_none());\n\n        buf.extend(b\"\\r\\n\");\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_11);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test\");\n    }\n\n    #[test]\n    fn test_headers_split_field() {\n        let mut buf = BytesMut::from(\"GET /test HTTP/1.1\\r\\n\");\n\n        let mut reader = MessageDecoder::<Request>::default();\n        assert! { reader.decode(&mut buf).unwrap().is_none() }\n\n        buf.extend(b\"t\");\n        assert! { reader.decode(&mut buf).unwrap().is_none() }\n\n        buf.extend(b\"es\");\n        assert! { reader.decode(&mut buf).unwrap().is_none() }\n\n        buf.extend(b\"t: value\\r\\n\\r\\n\");\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.version(), Version::HTTP_11);\n        assert_eq!(*req.method(), Method::GET);\n        assert_eq!(req.path(), \"/test\");\n        assert_eq!(\n            req.headers()\n                .get(HeaderName::try_from(\"test\").unwrap())\n                .unwrap()\n                .as_bytes(),\n            b\"value\"\n        );\n    }\n\n    #[test]\n    fn test_headers_multi_value() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             Set-Cookie: c1=cookie1\\r\\n\\\n             Set-Cookie: c2=cookie2\\r\\n\\r\\n\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, _) = reader.decode(&mut buf).unwrap().unwrap();\n\n        let val: Vec<_> = req\n            .headers()\n            .get_all(SET_COOKIE)\n            .map(|v| v.to_str().unwrap().to_owned())\n            .collect();\n        assert_eq!(val[0], \"c1=cookie1\");\n        assert_eq!(val[1], \"c2=cookie2\");\n    }\n\n    #[test]\n    fn test_conn_default_1_0() {\n        let req = parse_ready!(&mut BytesMut::from(\"GET /test HTTP/1.0\\r\\n\\r\\n\"));\n        assert_eq!(req.head().connection_type(), ConnectionType::Close);\n    }\n\n    #[test]\n    fn test_conn_default_1_1() {\n        let req = parse_ready!(&mut BytesMut::from(\"GET /test HTTP/1.1\\r\\n\\r\\n\"));\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n    }\n\n    #[test]\n    fn test_conn_close() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: close\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::Close);\n\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: Close\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::Close);\n    }\n\n    #[test]\n    fn test_conn_close_1_0() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.0\\r\\n\\\n             connection: close\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::Close);\n    }\n\n    #[test]\n    fn test_conn_keep_alive_1_0() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.0\\r\\n\\\n             connection: keep-alive\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.0\\r\\n\\\n             connection: Keep-Alive\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n    }\n\n    #[test]\n    fn test_conn_keep_alive_1_1() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: keep-alive\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n    }\n\n    #[test]\n    fn test_conn_other_1_0() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.0\\r\\n\\\n             connection: other\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::Close);\n    }\n\n    #[test]\n    fn test_conn_other_1_1() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: other\\r\\n\\r\\n\",\n        ));\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n    }\n\n    #[test]\n    fn test_conn_upgrade() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             upgrade: websockets\\r\\n\\\n             connection: upgrade\\r\\n\\r\\n\",\n        ));\n\n        assert!(req.upgrade());\n        assert_eq!(req.head().connection_type(), ConnectionType::Upgrade);\n\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             upgrade: Websockets\\r\\n\\\n             connection: Upgrade\\r\\n\\r\\n\",\n        ));\n\n        assert!(req.upgrade());\n        assert_eq!(req.head().connection_type(), ConnectionType::Upgrade);\n    }\n\n    #[test]\n    fn test_conn_upgrade_connect_method() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"CONNECT /test HTTP/1.1\\r\\n\\\n             content-type: text/plain\\r\\n\\r\\n\",\n        ));\n\n        assert!(req.upgrade());\n    }\n\n    #[test]\n    fn test_headers_bad_content_length() {\n        // string CL\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             content-length: line\\r\\n\\r\\n\",\n        ));\n\n        // negative CL\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             content-length: -1\\r\\n\\r\\n\",\n        ));\n    }\n\n    #[test]\n    fn octal_ish_cl_parsed_as_decimal() {\n        let mut buf = BytesMut::from(\n            \"POST /test HTTP/1.1\\r\\n\\\n             content-length: 011\\r\\n\\r\\n\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (_req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        assert!(matches!(\n            pl,\n            PayloadType::Payload(pl) if pl == PayloadDecoder::length(11)\n        ));\n    }\n\n    #[test]\n    fn test_invalid_header() {\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             test line\\r\\n\\r\\n\",\n        ));\n    }\n\n    #[test]\n    fn test_invalid_name() {\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             test[]: line\\r\\n\\r\\n\",\n        ));\n    }\n\n    #[test]\n    fn test_http_request_bad_status_line() {\n        expect_parse_err!(&mut BytesMut::from(\"getpath \\r\\n\\r\\n\"));\n    }\n\n    #[test]\n    fn test_http_request_upgrade_websocket() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: upgrade\\r\\n\\\n             upgrade: websocket\\r\\n\\r\\n\\\n             some raw data\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(req.head().connection_type(), ConnectionType::Upgrade);\n        assert!(req.upgrade());\n        assert!(pl.is_unhandled());\n    }\n\n    #[test]\n    fn test_http_request_upgrade_h2c() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             connection: upgrade, http2-settings\\r\\n\\\n             upgrade: h2c\\r\\n\\\n             http2-settings: dummy\\r\\n\\r\\n\",\n        );\n        let mut reader = MessageDecoder::<Request>::default();\n        let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        // `connection: upgrade, http2-settings` doesn't work properly..\n        // see MessageType::set_headers().\n        //\n        // The line below should be:\n        // assert_eq!(req.head().connection_type(), ConnectionType::Upgrade);\n        assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);\n        assert!(req.upgrade());\n        assert!(!pl.is_unhandled());\n    }\n\n    #[test]\n    fn test_http_request_parser_utf8() {\n        let req = parse_ready!(&mut BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n             x-test: тест\\r\\n\\r\\n\",\n        ));\n\n        assert_eq!(\n            req.headers().get(\"x-test\").unwrap().as_bytes(),\n            \"тест\".as_bytes()\n        );\n    }\n\n    #[test]\n    fn test_http_request_parser_two_slashes() {\n        let req = parse_ready!(&mut BytesMut::from(\"GET //path HTTP/1.1\\r\\n\\r\\n\"));\n        assert_eq!(req.path(), \"//path\");\n    }\n\n    #[test]\n    fn test_http_request_parser_bad_method() {\n        expect_parse_err!(&mut BytesMut::from(\"!12%()+=~$ /get HTTP/1.1\\r\\n\\r\\n\"));\n    }\n\n    #[test]\n    fn test_http_request_parser_bad_version() {\n        expect_parse_err!(&mut BytesMut::from(\"GET //get HT/11\\r\\n\\r\\n\"));\n    }\n\n    #[test]\n    fn test_response_http10_read_until_eof() {\n        let mut buf = BytesMut::from(\"HTTP/1.0 200 Ok\\r\\n\\r\\ntest data\");\n\n        let mut reader = MessageDecoder::<ResponseHead>::default();\n        let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n\n        let chunk = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(chunk, PayloadItem::Chunk(Bytes::from_static(b\"test data\")));\n    }\n\n    #[test]\n    fn hrs_multiple_content_length() {\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: 4\\r\\n\\\n            Content-Length: 2\\r\\n\\\n            \\r\\n\\\n            abcd\",\n        ));\n\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: 0\\r\\n\\\n            Content-Length: 2\\r\\n\\\n            \\r\\n\\\n            ab\",\n        ));\n    }\n\n    #[test]\n    fn hrs_content_length_plus() {\n        expect_parse_err!(&mut BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: +3\\r\\n\\\n            \\r\\n\\\n            000\",\n        ));\n    }\n\n    #[test]\n    fn hrs_te_http10() {\n        // in HTTP/1.0 transfer encoding is ignored and must therefore contain a CL header\n\n        expect_parse_err!(&mut BytesMut::from(\n            \"POST / HTTP/1.0\\r\\n\\\n            Host: example.com\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            3\\r\\n\\\n            aaa\\r\\n\\\n            0\\r\\n\\\n            \",\n        ));\n    }\n\n    #[test]\n    fn hrs_cl_and_te_http10() {\n        // in HTTP/1.0 transfer encoding is simply ignored so it's fine to have both\n\n        let mut buf = BytesMut::from(\n            \"GET / HTTP/1.0\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: 3\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            000\",\n        );\n\n        parse_ready!(&mut buf);\n    }\n\n    #[test]\n    fn hrs_unknown_transfer_encoding() {\n        let mut buf = BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Transfer-Encoding: JUNK\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            5\\r\\n\\\n            hello\\r\\n\\\n            0\",\n        );\n\n        expect_parse_err!(&mut buf);\n    }\n\n    #[test]\n    fn hrs_multiple_transfer_encoding() {\n        let mut buf = BytesMut::from(\n            \"GET / HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: 51\\r\\n\\\n            Transfer-Encoding: identity\\r\\n\\\n            Transfer-Encoding: chunked\\r\\n\\\n            \\r\\n\\\n            0\\r\\n\\\n            \\r\\n\\\n            GET /forbidden HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\r\\n\",\n        );\n\n        expect_parse_err!(&mut buf);\n    }\n\n    #[test]\n    fn transfer_encoding_agrees() {\n        let mut buf = BytesMut::from(\n            \"GET /test HTTP/1.1\\r\\n\\\n            Host: example.com\\r\\n\\\n            Content-Length: 3\\r\\n\\\n            Transfer-Encoding: identity\\r\\n\\\n            \\r\\n\\\n            0\\r\\n\",\n        );\n\n        let mut reader = MessageDecoder::<Request>::default();\n        let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();\n        let mut pl = pl.unwrap();\n\n        let chunk = pl.decode(&mut buf).unwrap().unwrap();\n        assert_eq!(chunk, PayloadItem::Chunk(Bytes::from_static(b\"0\\r\\n\")));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/dispatcher.rs",
    "content": "use std::{\n    collections::VecDeque,\n    fmt,\n    future::Future,\n    io, mem, net,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{Framed, FramedParts};\nuse actix_rt::time::sleep_until;\nuse actix_service::Service;\nuse bitflags::bitflags;\nuse bytes::{Buf, BytesMut};\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\nuse tokio::io::{AsyncRead, AsyncWrite};\nuse tokio_util::codec::{Decoder as _, Encoder as _};\nuse tracing::{error, trace};\n\nuse super::{\n    codec::Codec,\n    decoder::MAX_BUFFER_SIZE,\n    payload::{Payload, PayloadSender, PayloadStatus},\n    timer::TimerState,\n    Message, MessageType,\n};\nuse crate::{\n    body::{BodySize, BoxBody, MessageBody},\n    config::ServiceConfig,\n    error::{DispatchError, ParseError, PayloadError},\n    service::HttpFlow,\n    Error, Extensions, HttpMessage, OnConnectData, Request, Response, StatusCode,\n};\n\nconst LW_BUFFER_SIZE: usize = 1024;\nconst HW_BUFFER_SIZE: usize = 1024 * 8;\nconst MAX_PIPELINED_MESSAGES: usize = 16;\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    pub struct Flags: u8 {\n        /// Set when stream is read for first time.\n        const STARTED          = 0b0000_0001;\n\n        /// Set when full request-response cycle has occurred.\n        const FINISHED         = 0b0000_0010;\n\n        /// Set if connection is in keep-alive (inactive) state.\n        const KEEP_ALIVE       = 0b0000_0100;\n\n        /// Set if in shutdown procedure.\n        const SHUTDOWN         = 0b0000_1000;\n\n        /// Set if read-half is disconnected.\n        const READ_DISCONNECT  = 0b0001_0000;\n\n        /// Set if write-half is disconnected.\n        const WRITE_DISCONNECT = 0b0010_0000;\n    }\n}\n\n// there's 2 versions of Dispatcher state because of:\n// https://github.com/taiki-e/pin-project-lite/issues/3\n//\n// tl;dr: pin-project-lite doesn't play well with other attribute macros\n\n#[cfg(not(test))]\npin_project! {\n    /// Dispatcher for HTTP/1.1 protocol\n    pub struct Dispatcher<T, S, B, X, U>\n    where\n        S: Service<Request>,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        #[pin]\n        inner: DispatcherState<T, S, B, X, U>,\n    }\n}\n\n#[cfg(test)]\npin_project! {\n    /// Dispatcher for HTTP/1.1 protocol\n    pub struct Dispatcher<T, S, B, X, U>\n    where\n        S: Service<Request>,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        #[pin]\n        pub(super) inner: DispatcherState<T, S, B, X, U>,\n\n        // used in tests\n        pub(super) poll_count: u64,\n    }\n}\n\npin_project! {\n    #[project = DispatcherStateProj]\n    pub(super) enum DispatcherState<T, S, B, X, U>\n    where\n        S: Service<Request>,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        Normal { #[pin] inner: InnerDispatcher<T, S, B, X, U> },\n        Upgrade { #[pin] fut: U::Future },\n    }\n}\n\npin_project! {\n    #[project = InnerDispatcherProj]\n    pub(super) struct InnerDispatcher<T, S, B, X, U>\n    where\n        S: Service<Request>,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        flow: Rc<HttpFlow<S, X, U>>,\n        pub(super) flags: Flags,\n        peer_addr: Option<net::SocketAddr>,\n        conn_data: Option<Rc<Extensions>>,\n        config: ServiceConfig,\n        error: Option<DispatchError>,\n\n        #[pin]\n        pub(super) state: State<S, B, X>,\n        // when Some(_) dispatcher is in state of receiving request payload\n        payload: Option<PayloadSender>,\n        // true when current request uses chunked transfer encoding (drainable when payload is dropped)\n        payload_drainable: bool,\n        messages: VecDeque<DispatcherMessage>,\n\n        head_timer: TimerState,\n        ka_timer: TimerState,\n        shutdown_timer: TimerState,\n\n        pub(super) io: Option<T>,\n        read_buf: BytesMut,\n        write_buf: BytesMut,\n        codec: Codec,\n    }\n}\n\nenum DispatcherMessage {\n    Item(Request),\n    Upgrade(Request),\n    Error(Response<()>),\n}\n\npin_project! {\n    #[project = StateProj]\n    pub(super) enum State<S, B, X>\n    where\n        S: Service<Request>,\n        X: Service<Request, Response = Request>,\n        B: MessageBody,\n    {\n        None,\n        ExpectCall { #[pin] fut: X::Future },\n        ServiceCall { #[pin] fut: S::Future },\n        SendPayload { #[pin] body: B },\n        SendErrorPayload { #[pin] body: BoxBody },\n    }\n}\n\nimpl<S, B, X> State<S, B, X>\nwhere\n    S: Service<Request>,\n    X: Service<Request, Response = Request>,\n    B: MessageBody,\n{\n    pub(super) fn is_none(&self) -> bool {\n        matches!(self, State::None)\n    }\n}\n\nimpl<S, B, X> fmt::Debug for State<S, B, X>\nwhere\n    S: Service<Request>,\n    X: Service<Request, Response = Request>,\n    B: MessageBody,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::None => write!(f, \"State::None\"),\n            Self::ExpectCall { .. } => f.debug_struct(\"State::ExpectCall\").finish_non_exhaustive(),\n            Self::ServiceCall { .. } => {\n                f.debug_struct(\"State::ServiceCall\").finish_non_exhaustive()\n            }\n            Self::SendPayload { .. } => {\n                f.debug_struct(\"State::SendPayload\").finish_non_exhaustive()\n            }\n            Self::SendErrorPayload { .. } => f\n                .debug_struct(\"State::SendErrorPayload\")\n                .finish_non_exhaustive(),\n        }\n    }\n}\n\n#[derive(Debug)]\nenum PollResponse {\n    Upgrade(Request),\n    DoNothing,\n    DrainWriteBuf,\n}\n\nimpl<T, S, B, X, U> Dispatcher<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, Codec>), Response = ()>,\n    U::Error: fmt::Display,\n{\n    /// Create HTTP/1 dispatcher.\n    pub(crate) fn new(\n        io: T,\n        flow: Rc<HttpFlow<S, X, U>>,\n        config: ServiceConfig,\n        peer_addr: Option<net::SocketAddr>,\n        conn_data: OnConnectData,\n    ) -> Self {\n        Dispatcher {\n            inner: DispatcherState::Normal {\n                inner: InnerDispatcher {\n                    flow,\n                    flags: Flags::empty(),\n                    peer_addr,\n                    conn_data: conn_data.0.map(Rc::new),\n                    config: config.clone(),\n                    error: None,\n\n                    state: State::None,\n                    payload: None,\n                    payload_drainable: false,\n                    messages: VecDeque::new(),\n\n                    head_timer: TimerState::new(config.client_request_deadline().is_some()),\n                    ka_timer: TimerState::new(config.keep_alive().enabled()),\n                    shutdown_timer: TimerState::new(config.client_disconnect_deadline().is_some()),\n\n                    io: Some(io),\n                    read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),\n                    write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),\n                    codec: Codec::new(config),\n                },\n            },\n\n            #[cfg(test)]\n            poll_count: 0,\n        }\n    }\n}\n\nimpl<T, S, B, X, U> InnerDispatcher<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, Codec>), Response = ()>,\n    U::Error: fmt::Display,\n{\n    fn can_read(&self, cx: &mut Context<'_>) -> bool {\n        if self.flags.contains(Flags::READ_DISCONNECT) {\n            false\n        } else if let Some(ref info) = self.payload {\n            matches!(\n                info.need_read(cx),\n                PayloadStatus::Read | PayloadStatus::Dropped\n            )\n        } else {\n            true\n        }\n    }\n\n    fn client_disconnected(self: Pin<&mut Self>) {\n        let this = self.project();\n\n        this.flags\n            .insert(Flags::READ_DISCONNECT | Flags::WRITE_DISCONNECT);\n\n        if let Some(mut payload) = this.payload.take() {\n            payload.set_error(PayloadError::Incomplete(None));\n        }\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {\n        let InnerDispatcherProj { io, write_buf, .. } = self.project();\n        let mut io = Pin::new(io.as_mut().unwrap());\n\n        let len = write_buf.len();\n        let mut written = 0;\n\n        while written < len {\n            match io.as_mut().poll_write(cx, &write_buf[written..])? {\n                Poll::Ready(0) => {\n                    error!(\"write zero; closing\");\n                    return Poll::Ready(Err(io::Error::new(io::ErrorKind::WriteZero, \"\")));\n                }\n\n                Poll::Ready(n) => written += n,\n\n                Poll::Pending => {\n                    write_buf.advance(written);\n                    return Poll::Pending;\n                }\n            }\n        }\n\n        // everything has written to I/O; clear buffer\n        write_buf.clear();\n\n        // flush the I/O and check if get blocked\n        io.poll_flush(cx)\n    }\n\n    fn send_response_inner(\n        self: Pin<&mut Self>,\n        res: Response<()>,\n        body: &impl MessageBody,\n    ) -> Result<BodySize, DispatchError> {\n        let this = self.project();\n\n        let size = body.size();\n\n        this.codec\n            .encode(Message::Item((res, size)), this.write_buf)\n            .map_err(|err| {\n                if let Some(mut payload) = this.payload.take() {\n                    payload.set_error(PayloadError::Incomplete(None));\n                }\n\n                DispatchError::Io(err)\n            })?;\n\n        Ok(size)\n    }\n\n    fn send_response(\n        mut self: Pin<&mut Self>,\n        res: Response<()>,\n        body: B,\n    ) -> Result<(), DispatchError> {\n        let size = self.as_mut().send_response_inner(res, &body)?;\n        let mut this = self.project();\n        this.state.set(match size {\n            BodySize::None | BodySize::Sized(0) => {\n                let payload_unfinished = this.payload.is_some();\n                let drain_payload = this.payload.as_ref().is_some_and(|pl| pl.is_dropped())\n                    && *this.payload_drainable;\n\n                if payload_unfinished && !drain_payload {\n                    this.flags.insert(Flags::SHUTDOWN | Flags::FINISHED);\n                } else {\n                    this.flags.insert(Flags::FINISHED);\n                }\n\n                State::None\n            }\n            _ => State::SendPayload { body },\n        });\n\n        Ok(())\n    }\n\n    fn send_error_response(\n        mut self: Pin<&mut Self>,\n        res: Response<()>,\n        body: BoxBody,\n    ) -> Result<(), DispatchError> {\n        let size = self.as_mut().send_response_inner(res, &body)?;\n        let mut this = self.project();\n        this.state.set(match size {\n            BodySize::None | BodySize::Sized(0) => {\n                let payload_unfinished = this.payload.is_some();\n                let drain_payload = this.payload.as_ref().is_some_and(|pl| pl.is_dropped())\n                    && *this.payload_drainable;\n\n                if payload_unfinished && !drain_payload {\n                    this.flags.insert(Flags::SHUTDOWN | Flags::FINISHED);\n                } else {\n                    this.flags.insert(Flags::FINISHED);\n                }\n\n                State::None\n            }\n            _ => State::SendErrorPayload { body },\n        });\n\n        Ok(())\n    }\n\n    fn send_continue(self: Pin<&mut Self>) {\n        self.project()\n            .write_buf\n            .extend_from_slice(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\");\n    }\n\n    fn poll_response(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Result<PollResponse, DispatchError> {\n        'res: loop {\n            let mut this = self.as_mut().project();\n            match this.state.as_mut().project() {\n                // no future is in InnerDispatcher state; pop next message\n                StateProj::None => match this.messages.pop_front() {\n                    // handle request message\n                    Some(DispatcherMessage::Item(req)) => {\n                        // Handle `EXPECT: 100-Continue` header\n                        if req.head().expect() {\n                            // set InnerDispatcher state and continue loop to poll it\n                            let fut = this.flow.expect.call(req);\n                            this.state.set(State::ExpectCall { fut });\n                        } else {\n                            // set InnerDispatcher state and continue loop to poll it\n                            let fut = this.flow.service.call(req);\n                            this.state.set(State::ServiceCall { fut });\n                        };\n                    }\n\n                    // handle error message\n                    Some(DispatcherMessage::Error(res)) => {\n                        // send_response would update InnerDispatcher state to SendPayload or None\n                        // (If response body is empty)\n                        // continue loop to poll it\n                        self.as_mut().send_error_response(res, BoxBody::new(()))?;\n                    }\n\n                    // return with upgrade request and poll it exclusively\n                    Some(DispatcherMessage::Upgrade(req)) => return Ok(PollResponse::Upgrade(req)),\n\n                    // all messages are dealt with\n                    None => {\n                        // start keep-alive only if request payload is fully read/drained\n                        this.flags.set(\n                            Flags::KEEP_ALIVE,\n                            this.payload.is_none() && this.codec.keep_alive(),\n                        );\n\n                        return Ok(PollResponse::DoNothing);\n                    }\n                },\n\n                StateProj::ServiceCall { fut } => {\n                    match fut.poll(cx) {\n                        // service call resolved. send response.\n                        Poll::Ready(Ok(res)) => {\n                            let (res, body) = res.into().replace_body(());\n                            self.as_mut().send_response(res, body)?;\n                        }\n\n                        // send service call error as response\n                        Poll::Ready(Err(err)) => {\n                            let res: Response<BoxBody> = err.into();\n                            let (res, body) = res.replace_body(());\n                            self.as_mut().send_error_response(res, body)?;\n                        }\n\n                        // service call pending and could be waiting for more chunk messages\n                        // (pipeline message limit and/or payload can_read limit)\n                        Poll::Pending => {\n                            // no new message is decoded and no new payload is fed\n                            // nothing to do except waiting for new incoming data from client\n                            if !self.as_mut().poll_request(cx)? {\n                                return Ok(PollResponse::DoNothing);\n                            }\n                            // else loop\n                        }\n                    }\n                }\n\n                StateProj::SendPayload { mut body } => {\n                    // keep populate writer buffer until buffer size limit hit,\n                    // get blocked or finished.\n                    while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {\n                        match body.as_mut().poll_next(cx) {\n                            Poll::Ready(Some(Ok(item))) => {\n                                this.codec\n                                    .encode(Message::Chunk(Some(item)), this.write_buf)?;\n                            }\n\n                            Poll::Ready(None) => {\n                                this.codec.encode(Message::Chunk(None), this.write_buf)?;\n\n                                // if we have not yet pipelined to the next request, then\n                                // this.payload was the payload for the request we just finished\n                                // responding to. We can check to see if we finished reading it\n                                // yet, and if not, shutdown the connection.\n                                let payload_unfinished = this.payload.is_some();\n                                let drain_payload =\n                                    this.payload.as_ref().is_some_and(|pl| pl.is_dropped())\n                                        && *this.payload_drainable;\n                                let not_pipelined = this.messages.is_empty();\n\n                                // payload stream finished.\n                                // set state to None and handle next message\n                                this.state.set(State::None);\n\n                                if not_pipelined && payload_unfinished && !drain_payload {\n                                    this.flags.insert(Flags::SHUTDOWN | Flags::FINISHED);\n                                } else {\n                                    this.flags.insert(Flags::FINISHED);\n                                }\n\n                                continue 'res;\n                            }\n\n                            Poll::Ready(Some(Err(err))) => {\n                                let err = err.into();\n                                tracing::error!(\"Response payload stream error: {err:?}\");\n                                this.flags.insert(Flags::FINISHED);\n                                return Err(DispatchError::Body(err));\n                            }\n\n                            Poll::Pending => return Ok(PollResponse::DoNothing),\n                        }\n                    }\n\n                    // buffer is beyond max size\n                    // return and try to write the whole buffer to I/O stream.\n                    return Ok(PollResponse::DrainWriteBuf);\n                }\n\n                StateProj::SendErrorPayload { mut body } => {\n                    // TODO: de-dupe impl with SendPayload\n\n                    // keep populate writer buffer until buffer size limit hit,\n                    // get blocked or finished.\n                    while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {\n                        match body.as_mut().poll_next(cx) {\n                            Poll::Ready(Some(Ok(item))) => {\n                                this.codec\n                                    .encode(Message::Chunk(Some(item)), this.write_buf)?;\n                            }\n\n                            Poll::Ready(None) => {\n                                this.codec.encode(Message::Chunk(None), this.write_buf)?;\n\n                                // if we have not yet pipelined to the next request, then\n                                // this.payload was the payload for the request we just finished\n                                // responding to. We can check to see if we finished reading it\n                                // yet, and if not, shutdown the connection.\n                                let payload_unfinished = this.payload.is_some();\n                                let drain_payload =\n                                    this.payload.as_ref().is_some_and(|pl| pl.is_dropped())\n                                        && *this.payload_drainable;\n                                let not_pipelined = this.messages.is_empty();\n\n                                // payload stream finished.\n                                // set state to None and handle next message\n                                this.state.set(State::None);\n\n                                if not_pipelined && payload_unfinished && !drain_payload {\n                                    this.flags.insert(Flags::SHUTDOWN | Flags::FINISHED);\n                                } else {\n                                    this.flags.insert(Flags::FINISHED);\n                                }\n\n                                continue 'res;\n                            }\n\n                            Poll::Ready(Some(Err(err))) => {\n                                tracing::error!(\"Response payload stream error: {err:?}\");\n                                this.flags.insert(Flags::FINISHED);\n                                return Err(DispatchError::Body(\n                                    Error::new_body().with_cause(err).into(),\n                                ));\n                            }\n\n                            Poll::Pending => return Ok(PollResponse::DoNothing),\n                        }\n                    }\n\n                    // buffer is beyond max size\n                    // return and try to write the whole buffer to stream\n                    return Ok(PollResponse::DrainWriteBuf);\n                }\n\n                StateProj::ExpectCall { fut } => {\n                    trace!(\"  calling expect service\");\n\n                    match fut.poll(cx) {\n                        // expect resolved. write continue to buffer and set InnerDispatcher state\n                        // to service call.\n                        Poll::Ready(Ok(req)) => {\n                            this.write_buf\n                                .extend_from_slice(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\");\n                            let fut = this.flow.service.call(req);\n                            this.state.set(State::ServiceCall { fut });\n                        }\n\n                        // send expect error as response\n                        Poll::Ready(Err(err)) => {\n                            let res: Response<BoxBody> = err.into();\n                            let (res, body) = res.replace_body(());\n                            self.as_mut().send_error_response(res, body)?;\n                        }\n\n                        // expect must be solved before progress can be made.\n                        Poll::Pending => return Ok(PollResponse::DoNothing),\n                    }\n                }\n            }\n        }\n    }\n\n    fn handle_request(\n        mut self: Pin<&mut Self>,\n        req: Request,\n        cx: &mut Context<'_>,\n    ) -> Result<(), DispatchError> {\n        // initialize dispatcher state\n        {\n            let mut this = self.as_mut().project();\n\n            // Handle `EXPECT: 100-Continue` header\n            if req.head().expect() {\n                // set dispatcher state to call expect handler\n                let fut = this.flow.expect.call(req);\n                this.state.set(State::ExpectCall { fut });\n            } else {\n                // set dispatcher state to call service handler\n                let fut = this.flow.service.call(req);\n                this.state.set(State::ServiceCall { fut });\n            };\n        };\n\n        // eagerly poll the future once (or twice if expect is resolved immediately).\n        loop {\n            match self.as_mut().project().state.project() {\n                StateProj::ExpectCall { fut } => {\n                    match fut.poll(cx) {\n                        // expect is resolved; continue loop and poll the service call branch.\n                        Poll::Ready(Ok(req)) => {\n                            self.as_mut().send_continue();\n\n                            let mut this = self.as_mut().project();\n                            let fut = this.flow.service.call(req);\n                            this.state.set(State::ServiceCall { fut });\n\n                            continue;\n                        }\n\n                        // future is error; send response and return a result\n                        // on success to notify the dispatcher a new state is set and the outer loop\n                        // should be continued\n                        Poll::Ready(Err(err)) => {\n                            let res: Response<BoxBody> = err.into();\n                            let (res, body) = res.replace_body(());\n                            return self.send_error_response(res, body);\n                        }\n\n                        // future is pending; return Ok(()) to notify that a new state is\n                        // set and the outer loop should be continue.\n                        Poll::Pending => return Ok(()),\n                    }\n                }\n\n                StateProj::ServiceCall { fut } => {\n                    // return no matter the service call future's result.\n                    return match fut.poll(cx) {\n                        // Future is resolved. Send response and return a result. On success\n                        // to notify the dispatcher a new state is set and the outer loop\n                        // should be continue.\n                        Poll::Ready(Ok(res)) => {\n                            let (res, body) = res.into().replace_body(());\n                            self.as_mut().send_response(res, body)\n                        }\n\n                        // see the comment on ExpectCall state branch's Pending\n                        Poll::Pending => Ok(()),\n\n                        // see the comment on ExpectCall state branch's Ready(Err(_))\n                        Poll::Ready(Err(err)) => {\n                            let res: Response<BoxBody> = err.into();\n                            let (res, body) = res.replace_body(());\n                            self.as_mut().send_error_response(res, body)\n                        }\n                    };\n                }\n\n                _ => {\n                    unreachable!(\"State must be set to ServiceCall or ExceptCall in handle_request\")\n                }\n            }\n        }\n    }\n\n    /// Process one incoming request.\n    ///\n    /// Returns true if any meaningful work was done.\n    fn poll_request(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<bool, DispatchError> {\n        let pipeline_queue_full = self.messages.len() >= MAX_PIPELINED_MESSAGES;\n        let can_not_read = !self.can_read(cx);\n\n        // limit amount of non-processed requests\n        if pipeline_queue_full || can_not_read {\n            return Ok(false);\n        }\n\n        let mut this = self.as_mut().project();\n\n        let mut updated = false;\n\n        // decode from read buf as many full requests as possible\n        loop {\n            match this.codec.decode(this.read_buf) {\n                Ok(Some(msg)) => {\n                    updated = true;\n\n                    match msg {\n                        Message::Item(mut req) => {\n                            // head timer only applies to first request on connection\n                            this.head_timer.clear(line!());\n\n                            req.head_mut().peer_addr = *this.peer_addr;\n\n                            req.conn_data.clone_from(this.conn_data);\n\n                            match this.codec.message_type() {\n                                // request has no payload\n                                MessageType::None => *this.payload_drainable = false,\n\n                                // Request is upgradable. Add upgrade message and break.\n                                // Everything remaining in read buffer will be handed to\n                                // upgraded Request.\n                                MessageType::Stream if this.flow.upgrade.is_some() => {\n                                    *this.payload_drainable = false;\n                                    this.messages.push_back(DispatcherMessage::Upgrade(req));\n                                    break;\n                                }\n\n                                // request is not upgradable\n                                MessageType::Payload | MessageType::Stream => {\n                                    // PayloadSender and Payload are smart pointers share the\n                                    // same state. PayloadSender is attached to dispatcher and used\n                                    // to sink new chunked request data to state. Payload is\n                                    // attached to Request and passed to Service::call where the\n                                    // state can be collected and consumed.\n                                    let (sender, payload) = Payload::create(false);\n                                    *req.payload() = crate::Payload::H1 { payload };\n                                    *this.payload = Some(sender);\n                                    *this.payload_drainable = req.chunked().unwrap_or(false);\n                                }\n                            }\n\n                            // handle request early when no future in InnerDispatcher state.\n                            if this.state.is_none() {\n                                self.as_mut().handle_request(req, cx)?;\n                                this = self.as_mut().project();\n                            } else {\n                                this.messages.push_back(DispatcherMessage::Item(req));\n                            }\n                        }\n\n                        Message::Chunk(Some(chunk)) => {\n                            if let Some(ref mut payload) = this.payload {\n                                payload.feed_data(chunk);\n                            } else {\n                                error!(\"Internal server error: unexpected payload chunk\");\n                                this.flags.insert(Flags::READ_DISCONNECT);\n                                this.messages.push_back(DispatcherMessage::Error(\n                                    Response::internal_server_error().drop_body(),\n                                ));\n                                *this.error = Some(DispatchError::InternalError);\n                                break;\n                            }\n                        }\n\n                        Message::Chunk(None) => {\n                            if let Some(mut payload) = this.payload.take() {\n                                payload.feed_eof();\n                                *this.payload_drainable = false;\n                            } else {\n                                error!(\"Internal server error: unexpected eof\");\n                                this.flags.insert(Flags::READ_DISCONNECT);\n                                this.messages.push_back(DispatcherMessage::Error(\n                                    Response::internal_server_error().drop_body(),\n                                ));\n                                *this.error = Some(DispatchError::InternalError);\n                                break;\n                            }\n                        }\n                    }\n                }\n\n                // decode is partial and buffer is not full yet\n                // break and wait for more read\n                Ok(None) => break,\n\n                Err(ParseError::Io(err)) => {\n                    trace!(\"I/O error: {}\", &err);\n                    self.as_mut().client_disconnected();\n                    this = self.as_mut().project();\n                    *this.error = Some(DispatchError::Io(err));\n                    break;\n                }\n\n                Err(ParseError::TooLarge) => {\n                    trace!(\"request head was too big; returning 431 response\");\n\n                    if let Some(mut payload) = this.payload.take() {\n                        payload.set_error(PayloadError::Overflow);\n                    }\n\n                    // request heads that overflow buffer size return a 431 error\n                    this.messages\n                        .push_back(DispatcherMessage::Error(Response::with_body(\n                            StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,\n                            (),\n                        )));\n\n                    this.flags.insert(Flags::READ_DISCONNECT);\n                    *this.error = Some(ParseError::TooLarge.into());\n\n                    break;\n                }\n\n                Err(err) => {\n                    trace!(\"parse error {}\", &err);\n\n                    if let Some(mut payload) = this.payload.take() {\n                        payload.set_error(PayloadError::EncodingCorrupted);\n                    }\n\n                    // malformed requests should be responded with 400\n                    this.messages.push_back(DispatcherMessage::Error(\n                        Response::bad_request().drop_body(),\n                    ));\n\n                    this.flags.insert(Flags::READ_DISCONNECT);\n                    *this.error = Some(err.into());\n                    break;\n                }\n            }\n        }\n\n        Ok(updated)\n    }\n\n    fn poll_head_timer(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Result<(), DispatchError> {\n        let this = self.as_mut().project();\n\n        if let TimerState::Active { timer } = this.head_timer {\n            if timer.as_mut().poll(cx).is_ready() {\n                // timeout on first request (slow request) return 408\n\n                trace!(\"timed out on slow request; replying with 408 and closing connection\");\n\n                let _ = self.as_mut().send_error_response(\n                    Response::with_body(StatusCode::REQUEST_TIMEOUT, ()),\n                    BoxBody::new(()),\n                );\n\n                self.project().flags.insert(Flags::SHUTDOWN);\n            }\n        };\n\n        Ok(())\n    }\n\n    fn poll_ka_timer(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> {\n        let this = self.as_mut().project();\n        if let TimerState::Active { timer } = this.ka_timer {\n            debug_assert!(\n                this.flags.contains(Flags::KEEP_ALIVE),\n                \"keep-alive flag should be set when timer is active\",\n            );\n            debug_assert!(\n                this.state.is_none(),\n                \"dispatcher should not be in keep-alive phase if state is not none: {:?}\",\n                this.state,\n            );\n\n            // Assert removed by @robjtede on account of issue #2655. There are cases where an I/O\n            // flush can be pending after entering the keep-alive state causing the subsequent flush\n            // wake up to panic here. This appears to be a Linux-only problem. Leaving original code\n            // below for posterity because a simple and reliable test could not be found to trigger\n            // the behavior.\n            // debug_assert!(\n            //     this.write_buf.is_empty(),\n            //     \"dispatcher should not be in keep-alive phase if write_buf is not empty\",\n            // );\n\n            // keep-alive timer has timed out\n            if timer.as_mut().poll(cx).is_ready() {\n                // no tasks at hand\n                trace!(\"timer timed out; closing connection\");\n                this.flags.insert(Flags::SHUTDOWN);\n\n                if let Some(deadline) = this.config.client_disconnect_deadline() {\n                    // start shutdown timeout if enabled\n                    this.shutdown_timer\n                        .set_and_init(cx, sleep_until(deadline.into()), line!());\n                } else {\n                    // no shutdown timeout, drop socket\n                    this.flags.insert(Flags::WRITE_DISCONNECT);\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn poll_shutdown_timer(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Result<(), DispatchError> {\n        let this = self.as_mut().project();\n        if let TimerState::Active { timer } = this.shutdown_timer {\n            debug_assert!(\n                this.flags.contains(Flags::SHUTDOWN),\n                \"shutdown flag should be set when timer is active\",\n            );\n\n            // timed-out during shutdown; drop connection\n            if timer.as_mut().poll(cx).is_ready() {\n                trace!(\"timed-out during shutdown\");\n                return Err(DispatchError::DisconnectTimeout);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Poll head, keep-alive, and disconnect timer.\n    fn poll_timers(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> {\n        self.as_mut().poll_head_timer(cx)?;\n        self.as_mut().poll_ka_timer(cx)?;\n        self.as_mut().poll_shutdown_timer(cx)?;\n\n        Ok(())\n    }\n\n    /// Returns true when I/O stream can be disconnected after write to it.\n    ///\n    /// It covers these conditions:\n    /// - `std::io::ErrorKind::ConnectionReset` after partial read;\n    /// - all data read done.\n    #[inline(always)] // TODO: bench this inline\n    fn read_available(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<bool, DispatchError> {\n        let this = self.project();\n\n        if this.flags.contains(Flags::READ_DISCONNECT) {\n            return Ok(false);\n        };\n\n        let mut io = Pin::new(this.io.as_mut().unwrap());\n\n        let mut read_some = false;\n\n        loop {\n            // Return early when read buf exceed decoder's max buffer size.\n            if this.read_buf.len() >= MAX_BUFFER_SIZE {\n                // At this point it's not known IO stream is still scheduled to be waked up so\n                // force wake up dispatcher just in case.\n                //\n                // Reason:\n                // AsyncRead mostly would only have guarantee wake up when the poll_read\n                // return Poll::Pending.\n                //\n                // Case:\n                // When read_buf is beyond max buffer size the early return could be successfully\n                // be parsed as a new Request. This case would not generate ParseError::TooLarge and\n                // at this point IO stream is not fully read to Pending and would result in\n                // dispatcher stuck until timeout (keep-alive).\n                //\n                // Note:\n                // This is a perf choice to reduce branch on <Request as MessageType>::decode.\n                //\n                // A Request head too large to parse is only checked on `httparse::Status::Partial`.\n\n                match this.payload.as_ref().map(|p| p.need_read(cx)) {\n                    // Payload consumer is alive but applying backpressure. Wait for its waker.\n                    Some(PayloadStatus::Pause) => {}\n\n                    // Consumer dropped means drain/discard mode; keep polling to make progress.\n                    Some(PayloadStatus::Dropped) | Some(PayloadStatus::Read) | None => {\n                        cx.waker().wake_by_ref()\n                    }\n                }\n\n                return Ok(false);\n            }\n\n            // grow buffer if necessary.\n            let remaining = this.read_buf.capacity() - this.read_buf.len();\n            if remaining < LW_BUFFER_SIZE {\n                this.read_buf.reserve(HW_BUFFER_SIZE - remaining);\n            }\n\n            match tokio_util::io::poll_read_buf(io.as_mut(), cx, this.read_buf) {\n                Poll::Ready(Ok(n)) => {\n                    // When draining a dropped request payload, keep FINISHED set so the\n                    // disconnect/keep-alive decision can be made once the payload is fully drained.\n                    if !this.payload.as_ref().is_some_and(|pl| pl.is_dropped()) {\n                        this.flags.remove(Flags::FINISHED);\n                    }\n\n                    if n == 0 {\n                        return Ok(true);\n                    }\n\n                    read_some = true;\n                }\n\n                Poll::Pending => {\n                    return Ok(false);\n                }\n\n                Poll::Ready(Err(err)) => {\n                    return match err.kind() {\n                        // convert WouldBlock error to the same as Pending return\n                        io::ErrorKind::WouldBlock => Ok(false),\n\n                        // connection reset after partial read\n                        io::ErrorKind::ConnectionReset if read_some => Ok(true),\n\n                        _ => Err(DispatchError::Io(err)),\n                    };\n                }\n            }\n        }\n    }\n\n    /// call upgrade service with request.\n    fn upgrade(self: Pin<&mut Self>, req: Request) -> U::Future {\n        let this = self.project();\n        let mut parts = FramedParts::with_read_buf(\n            this.io.take().unwrap(),\n            mem::take(this.codec),\n            mem::take(this.read_buf),\n        );\n        parts.write_buf = mem::take(this.write_buf);\n        let framed = Framed::from_parts(parts);\n        this.flow.upgrade.as_ref().unwrap().call((req, framed))\n    }\n}\n\nimpl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, Codec>), Response = ()>,\n    U::Error: fmt::Display,\n{\n    type Output = Result<(), DispatchError>;\n\n    #[inline]\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.as_mut().project();\n\n        #[cfg(test)]\n        {\n            *this.poll_count += 1;\n        }\n\n        match this.inner.project() {\n            DispatcherStateProj::Upgrade { fut: upgrade } => upgrade.poll(cx).map_err(|err| {\n                error!(\"Upgrade handler error: {}\", err);\n                DispatchError::Upgrade\n            }),\n\n            DispatcherStateProj::Normal { mut inner } => {\n                trace!(\"start flags: {:?}\", &inner.flags);\n\n                trace_timer_states(\n                    \"start\",\n                    &inner.head_timer,\n                    &inner.ka_timer,\n                    &inner.shutdown_timer,\n                );\n\n                inner.as_mut().poll_timers(cx)?;\n\n                let poll = if inner.flags.contains(Flags::SHUTDOWN) {\n                    if inner.flags.contains(Flags::WRITE_DISCONNECT) {\n                        Poll::Ready(Ok(()))\n                    } else {\n                        // flush buffer and wait on blocked\n                        ready!(inner.as_mut().poll_flush(cx))?;\n                        Pin::new(inner.as_mut().project().io.as_mut().unwrap())\n                            .poll_shutdown(cx)\n                            .map_err(DispatchError::from)\n                    }\n                } else {\n                    // read from I/O stream and fill read buffer\n                    let should_disconnect = inner.as_mut().read_available(cx)?;\n\n                    // after reading something from stream, clear keep-alive timer\n                    if !inner.read_buf.is_empty() && inner.flags.contains(Flags::KEEP_ALIVE) {\n                        let inner = inner.as_mut().project();\n                        inner.flags.remove(Flags::KEEP_ALIVE);\n                        inner.ka_timer.clear(line!());\n                    }\n\n                    if !inner.flags.contains(Flags::STARTED) {\n                        inner.as_mut().project().flags.insert(Flags::STARTED);\n\n                        if let Some(deadline) = inner.config.client_request_deadline() {\n                            inner.as_mut().project().head_timer.set_and_init(\n                                cx,\n                                sleep_until(deadline.into()),\n                                line!(),\n                            );\n                        }\n                    }\n\n                    inner.as_mut().poll_request(cx)?;\n\n                    if should_disconnect {\n                        // I/O stream should to be closed\n                        let inner = inner.as_mut().project();\n                        inner.flags.insert(Flags::READ_DISCONNECT);\n                        if let Some(mut payload) = inner.payload.take() {\n                            payload.set_error(PayloadError::Incomplete(None));\n                            payload.feed_eof();\n                        }\n                    };\n\n                    loop {\n                        // poll response to populate write buffer\n                        // drain indicates whether write buffer should be emptied before next run\n                        let drain = match inner.as_mut().poll_response(cx)? {\n                            PollResponse::DrainWriteBuf => true,\n\n                            PollResponse::DoNothing => {\n                                // KEEP_ALIVE is set in send_response_inner if client allows it\n                                // FINISHED is set after writing last chunk of response\n                                if inner.flags.contains(Flags::KEEP_ALIVE | Flags::FINISHED) {\n                                    if let Some(timer) = inner.config.keep_alive_deadline() {\n                                        inner.as_mut().project().ka_timer.set_and_init(\n                                            cx,\n                                            sleep_until(timer.into()),\n                                            line!(),\n                                        );\n                                    }\n                                }\n\n                                false\n                            }\n\n                            // upgrade request and goes Upgrade variant of DispatcherState.\n                            PollResponse::Upgrade(req) => {\n                                let upgrade = inner.upgrade(req);\n                                self.as_mut()\n                                    .project()\n                                    .inner\n                                    .set(DispatcherState::Upgrade { fut: upgrade });\n                                return self.poll(cx);\n                            }\n                        };\n\n                        // we didn't get WouldBlock from write operation, so data get written to\n                        // kernel completely (macOS) and we have to write again otherwise response\n                        // can get stuck\n                        //\n                        // TODO: want to find a reference for this behavior\n                        // see introduced commit: 3872d3ba\n                        let flush_was_ready = inner.as_mut().poll_flush(cx)?.is_ready();\n\n                        // this assert seems to always be true but not willing to commit to it until\n                        // we understand what Nikolay meant when writing the above comment\n                        // debug_assert!(flush_was_ready);\n\n                        if !flush_was_ready || !drain {\n                            break;\n                        }\n                    }\n\n                    // client is gone\n                    if inner.flags.contains(Flags::WRITE_DISCONNECT) {\n                        trace!(\"client is gone; disconnecting\");\n                        return Poll::Ready(Ok(()));\n                    }\n\n                    let inner_p = inner.as_mut().project();\n                    let state_is_none = inner_p.state.is_none();\n\n                    // If the read-half is closed, we start the shutdown procedure if either is\n                    // true:\n                    //\n                    // - state is [`State::None`], which means that we're done with request\n                    //   processing, so if the client closed its writer-side it means that it won't\n                    //   send more requests.\n                    // - The user requested to not allow half-closures\n                    if inner_p.flags.contains(Flags::READ_DISCONNECT)\n                        && (!inner_p.config.h1_allow_half_closed() || state_is_none)\n                    {\n                        trace!(\"read half closed; start shutdown\");\n                        inner_p.flags.insert(Flags::SHUTDOWN);\n                    }\n\n                    // keep-alive and stream errors\n                    if state_is_none && inner_p.write_buf.is_empty() {\n                        if let Some(err) = inner_p.error.take() {\n                            error!(\"stream error: {}\", &err);\n                            return Poll::Ready(Err(err));\n                        }\n\n                        // disconnect if keep-alive is not enabled\n                        if inner_p.flags.contains(Flags::FINISHED)\n                            && !inner_p.flags.contains(Flags::KEEP_ALIVE)\n                            && inner_p.payload.is_none()\n                        {\n                            inner_p.flags.remove(Flags::FINISHED);\n                            inner_p.flags.insert(Flags::SHUTDOWN);\n                            return self.poll(cx);\n                        }\n\n                        // disconnect if shutdown\n                        if inner_p.flags.contains(Flags::SHUTDOWN) {\n                            return self.poll(cx);\n                        }\n                    }\n\n                    trace_timer_states(\n                        \"end\",\n                        inner_p.head_timer,\n                        inner_p.ka_timer,\n                        inner_p.shutdown_timer,\n                    );\n\n                    if inner_p.flags.contains(Flags::SHUTDOWN) {\n                        cx.waker().wake_by_ref();\n                    }\n                    Poll::Pending\n                };\n\n                trace!(\"end flags: {:?}\", &inner.flags);\n\n                poll\n            }\n        }\n    }\n}\n\n#[allow(dead_code)]\nfn trace_timer_states(\n    label: &str,\n    head_timer: &TimerState,\n    ka_timer: &TimerState,\n    shutdown_timer: &TimerState,\n) {\n    trace!(\"{} timers:\", label);\n\n    if head_timer.is_enabled() {\n        trace!(\"  head {}\", &head_timer);\n    }\n\n    if ka_timer.is_enabled() {\n        trace!(\"  keep-alive {}\", &ka_timer);\n    }\n\n    if shutdown_timer.is_enabled() {\n        trace!(\"  shutdown {}\", &shutdown_timer);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/dispatcher_tests.rs",
    "content": "use std::{\n    future::Future,\n    pin::Pin,\n    str,\n    task::{Context, Poll},\n    time::Duration,\n};\n\nuse actix_codec::Framed;\nuse actix_rt::{pin, time::sleep};\nuse actix_service::{fn_service, Service};\nuse actix_utils::future::{ready, Ready};\nuse bytes::{Buf, Bytes, BytesMut};\nuse futures_util::future::lazy;\n\nuse super::dispatcher::{Dispatcher, DispatcherState, DispatcherStateProj, Flags};\nuse crate::{\n    body::{BoxBody, MessageBody},\n    config::ServiceConfig,\n    h1::{Codec, ExpectHandler, UpgradeHandler},\n    service::HttpFlow,\n    test::{TestBuffer, TestSeqBuffer},\n    Error, HttpMessage, KeepAlive, Method, OnConnectData, Request, Response, StatusCode,\n};\n\nstruct YieldService;\n\nimpl Service<Request> for YieldService {\n    type Response = Response<BoxBody>;\n    type Error = Response<BoxBody>;\n    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, _: Request) -> Self::Future {\n        Box::pin(async {\n            // Yield twice because the dispatcher can poll the service twice per dispatcher's poll:\n            // once in `handle_request` and another in `poll_response`\n            actix_rt::task::yield_now().await;\n            actix_rt::task::yield_now().await;\n            Ok(Response::ok())\n        })\n    }\n}\n\nfn find_slice(haystack: &[u8], needle: &[u8], from: usize) -> Option<usize> {\n    memchr::memmem::find(&haystack[from..], needle)\n}\n\nfn stabilize_date_header(payload: &mut [u8]) {\n    let mut from = 0;\n    while let Some(pos) = find_slice(payload, b\"date\", from) {\n        payload[(from + pos)..(from + pos + 35)]\n            .copy_from_slice(b\"date: Thu, 01 Jan 1970 12:34:56 UTC\");\n        from += 35;\n    }\n}\n\nfn ok_service() -> impl Service<Request, Response = Response<impl MessageBody>, Error = Error> {\n    status_service(StatusCode::OK)\n}\n\nfn status_service(\n    status: StatusCode,\n) -> impl Service<Request, Response = Response<impl MessageBody>, Error = Error> {\n    fn_service(move |_req: Request| ready(Ok::<_, Error>(Response::new(status))))\n}\n\nfn echo_path_service() -> impl Service<Request, Response = Response<impl MessageBody>, Error = Error>\n{\n    fn_service(|req: Request| {\n        let path = req.path().as_bytes();\n        ready(Ok::<_, Error>(\n            Response::ok().set_body(Bytes::copy_from_slice(path)),\n        ))\n    })\n}\n\nfn drop_payload_service() -> impl Service<Request, Response = Response<&'static str>, Error = Error>\n{\n    fn_service(|mut req: Request| async move {\n        let _ = req.take_payload();\n        Ok::<_, Error>(Response::with_body(StatusCode::OK, \"payload dropped\"))\n    })\n}\n\nfn echo_payload_service() -> impl Service<Request, Response = Response<Bytes>, Error = Error> {\n    fn_service(|mut req: Request| {\n        Box::pin(async move {\n            use futures_util::StreamExt as _;\n\n            let mut pl = req.take_payload();\n            let mut body = BytesMut::new();\n            while let Some(chunk) = pl.next().await {\n                body.extend_from_slice(chunk.unwrap().chunk())\n            }\n\n            Ok::<_, Error>(Response::ok().set_body(body.freeze()))\n        })\n    })\n}\n\n#[actix_rt::test]\nasync fn late_request() {\n    let mut buf = TestBuffer::empty();\n\n    let cfg = ServiceConfig::new(\n        KeepAlive::Disabled,\n        Duration::from_millis(100),\n        Duration::ZERO,\n        false,\n        None,\n    );\n    let services = HttpFlow::new(ok_service(), ExpectHandler, None);\n\n    let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n        buf.clone(),\n        services,\n        cfg,\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        match h1.as_mut().poll(cx) {\n            Poll::Ready(_) => panic!(\"first poll should not be ready\"),\n            Poll::Pending => {}\n        }\n\n        // polls: initial\n        assert_eq!(h1.poll_count, 1);\n\n        buf.extend_read_buf(\"GET /abcd HTTP/1.1\\r\\nConnection: close\\r\\n\\r\\n\");\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(\"second poll should not be pending\"),\n            Poll::Ready(res) => assert!(res.is_ok()),\n        }\n\n        // polls: initial pending => handle req => shutdown\n        assert_eq!(h1.poll_count, 3);\n\n        let mut res = buf.take_write_buf().to_vec();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 0\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn oneshot_connection() {\n    let buf = TestBuffer::new(\"GET /abcd HTTP/1.1\\r\\n\\r\\n\");\n\n    let cfg = ServiceConfig::new(\n        KeepAlive::Disabled,\n        Duration::from_millis(100),\n        Duration::ZERO,\n        false,\n        None,\n    );\n    let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n    let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n        buf.clone(),\n        services,\n        cfg,\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(\"first poll should not be pending\"),\n            Poll::Ready(res) => assert!(res.is_ok()),\n        }\n\n        // polls: initial => shutdown\n        assert_eq!(h1.poll_count, 2);\n\n        let mut res = buf.take_write_buf().to_vec();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 200 OK\n            content-length: 5\n            connection: close\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            /abcd\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn keep_alive_timeout() {\n    let buf = TestBuffer::new(\"GET /abcd HTTP/1.1\\r\\n\\r\\n\");\n\n    let cfg = ServiceConfig::new(\n        KeepAlive::Timeout(Duration::from_millis(200)),\n        Duration::from_millis(100),\n        Duration::ZERO,\n        false,\n        None,\n    );\n    let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n    let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n        buf.clone(),\n        services,\n        cfg,\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        assert!(\n            h1.as_mut().poll(cx).is_pending(),\n            \"keep-alive should prevent poll from resolving\"\n        );\n\n        // polls: initial\n        assert_eq!(h1.poll_count, 1);\n\n        let mut res = buf.take_write_buf().to_vec();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 5\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /abcd\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n\n    // sleep slightly longer than keep-alive timeout\n    sleep(Duration::from_millis(250)).await;\n\n    lazy(|cx| {\n        assert!(\n            h1.as_mut().poll(cx).is_ready(),\n            \"keep-alive should have resolved\",\n        );\n\n        // polls: initial => keep-alive wake-up shutdown\n        assert_eq!(h1.poll_count, 2);\n\n        if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {\n            // connection closed\n            assert!(inner.flags.contains(Flags::SHUTDOWN));\n            assert!(inner.flags.contains(Flags::WRITE_DISCONNECT));\n            // and nothing added to write buffer\n            assert!(buf.write_buf_slice().is_empty());\n        }\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn keep_alive_follow_up_req() {\n    let mut buf = TestBuffer::new(\"GET /abcd HTTP/1.1\\r\\n\\r\\n\");\n\n    let cfg = ServiceConfig::new(\n        KeepAlive::Timeout(Duration::from_millis(500)),\n        Duration::from_millis(100),\n        Duration::ZERO,\n        false,\n        None,\n    );\n    let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n    let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n        buf.clone(),\n        services,\n        cfg,\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        assert!(\n            h1.as_mut().poll(cx).is_pending(),\n            \"keep-alive should prevent poll from resolving\"\n        );\n\n        // polls: initial\n        assert_eq!(h1.poll_count, 1);\n\n        let mut res = buf.take_write_buf().to_vec();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 5\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /abcd\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n\n    // sleep for less than KA timeout\n    sleep(Duration::from_millis(100)).await;\n\n    lazy(|cx| {\n        assert!(\n            h1.as_mut().poll(cx).is_pending(),\n            \"keep-alive should not have resolved dispatcher yet\",\n        );\n\n        // polls: initial => manual\n        assert_eq!(h1.poll_count, 2);\n\n        if let DispatcherStateProj::Normal { inner } = h1.as_mut().project().inner.project() {\n            // connection not closed\n            assert!(!inner.flags.contains(Flags::SHUTDOWN));\n            assert!(!inner.flags.contains(Flags::WRITE_DISCONNECT));\n            // and nothing added to write buffer\n            assert!(buf.write_buf_slice().is_empty());\n        }\n    })\n    .await;\n\n    lazy(|cx| {\n        buf.extend_read_buf(\n            \"\\\n            GET /efg HTTP/1.1\\r\\n\\\n            Connection: close\\r\\n\\\n            \\r\\n\\r\\n\",\n        );\n\n        assert!(\n            h1.as_mut().poll(cx).is_ready(),\n            \"connection close header should override keep-alive setting\",\n        );\n\n        // polls: initial => manual => follow-up req => shutdown\n        assert_eq!(h1.poll_count, 4);\n\n        if let DispatcherStateProj::Normal { inner } = h1.as_mut().project().inner.project() {\n            // connection closed\n            assert!(inner.flags.contains(Flags::SHUTDOWN));\n            assert!(!inner.flags.contains(Flags::WRITE_DISCONNECT));\n        }\n\n        let mut res = buf.take_write_buf().to_vec();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 4\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /efg\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn req_parse_err() {\n    lazy(|cx| {\n        let buf = TestBuffer::new(\"GET /test HTTP/1\\r\\n\\r\\n\");\n\n        let services = HttpFlow::new(ok_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            ServiceConfig::default(),\n            None,\n            OnConnectData::default(),\n        );\n\n        pin!(h1);\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(),\n            Poll::Ready(res) => assert!(res.is_err()),\n        }\n\n        if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {\n            assert!(inner.flags.contains(Flags::READ_DISCONNECT));\n            assert_eq!(\n                &buf.write_buf_slice()[..26],\n                b\"HTTP/1.1 400 Bad Request\\r\\n\"\n            );\n        }\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn pipelining_ok_then_ok() {\n    lazy(|cx| {\n        let buf = TestBuffer::new(\n            \"\\\n                GET /abcd HTTP/1.1\\r\\n\\r\\n\\\n                GET /def HTTP/1.1\\r\\n\\r\\n\\\n                \",\n        );\n\n        let cfg = ServiceConfig::new(\n            KeepAlive::Disabled,\n            Duration::from_millis(1),\n            Duration::from_millis(1),\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        pin!(h1);\n\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(\"first poll should not be pending\"),\n            Poll::Ready(res) => assert!(res.is_ok()),\n        }\n\n        // polls: initial => shutdown\n        assert_eq!(h1.poll_count, 2);\n\n        let mut res = buf.write_buf_slice_mut();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 5\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /abcd\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 4\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /def\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn early_response_with_payload_closes_connection() {\n    lazy(|cx| {\n        let buf = TestBuffer::new(\n            \"\\\n                GET /unfinished HTTP/1.1\\r\\n\\\n                Content-Length: 2\\r\\n\\\n                \\r\\n\\\n                \",\n        );\n\n        let cfg = ServiceConfig::new(\n            KeepAlive::Os,\n            Duration::from_millis(1),\n            Duration::from_millis(1),\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        pin!(h1);\n\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(\"Should have shut down\"),\n            Poll::Ready(res) => assert!(res.is_ok()),\n        }\n\n        // polls: initial => shutdown\n        assert_eq!(h1.poll_count, 2);\n\n        {\n            let mut res = buf.write_buf_slice_mut();\n            stabilize_date_header(&mut res);\n            let res = &res[..];\n\n            let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 11\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /unfinished\\\n                \";\n\n            assert_eq!(\n                res,\n                exp,\n                \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n                String::from_utf8_lossy(res),\n                String::from_utf8_lossy(exp)\n            );\n        }\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn pipelining_ok_then_bad() {\n    lazy(|cx| {\n        let buf = TestBuffer::new(\n            \"\\\n                GET /abcd HTTP/1.1\\r\\n\\r\\n\\\n                GET /def HTTP/1\\r\\n\\r\\n\\\n                \",\n        );\n\n        let cfg = ServiceConfig::new(\n            KeepAlive::Disabled,\n            Duration::from_millis(1),\n            Duration::from_millis(1),\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        pin!(h1);\n\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        match h1.as_mut().poll(cx) {\n            Poll::Pending => panic!(\"first poll should not be pending\"),\n            Poll::Ready(res) => assert!(res.is_err()),\n        }\n\n        // polls: initial => shutdown\n        assert_eq!(h1.poll_count, 1);\n\n        let mut res = buf.write_buf_slice_mut();\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = b\"\\\n                HTTP/1.1 200 OK\\r\\n\\\n                content-length: 5\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                /abcd\\\n                HTTP/1.1 400 Bad Request\\r\\n\\\n                content-length: 0\\r\\n\\\n                connection: close\\r\\n\\\n                date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\r\\n\\\n                \";\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn expect_handling() {\n    lazy(|cx| {\n        let mut buf = TestSeqBuffer::empty();\n        let cfg = ServiceConfig::new(\n            KeepAlive::Disabled,\n            Duration::ZERO,\n            Duration::ZERO,\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(echo_payload_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        buf.extend_read_buf(\n            \"\\\n                POST /upload HTTP/1.1\\r\\n\\\n                Content-Length: 5\\r\\n\\\n                Expect: 100-continue\\r\\n\\\n                \\r\\n\\\n                \",\n        );\n\n        pin!(h1);\n\n        assert!(h1.as_mut().poll(cx).is_pending());\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        // polls: manual\n        assert_eq!(h1.poll_count, 1);\n\n        if let DispatcherState::Normal { ref inner } = h1.inner {\n            let io = inner.io.as_ref().unwrap();\n            let res = &io.write_buf()[..];\n            assert_eq!(\n                str::from_utf8(res).unwrap(),\n                \"HTTP/1.1 100 Continue\\r\\n\\r\\n\"\n            );\n        }\n\n        buf.extend_read_buf(\"12345\");\n        assert!(h1.as_mut().poll(cx).is_ready());\n\n        // polls: manual manual shutdown\n        assert_eq!(h1.poll_count, 3);\n\n        if let DispatcherState::Normal { ref inner } = h1.inner {\n            let io = inner.io.as_ref().unwrap();\n            let mut res = io.write_buf()[..].to_owned();\n            stabilize_date_header(&mut res);\n\n            assert_eq!(\n                str::from_utf8(&res).unwrap(),\n                \"\\\n                    HTTP/1.1 100 Continue\\r\\n\\\n                    \\r\\n\\\n                    HTTP/1.1 200 OK\\r\\n\\\n                    content-length: 5\\r\\n\\\n                    connection: close\\r\\n\\\n                    date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\\n                    \\r\\n\\\n                    12345\\\n                    \"\n            );\n        }\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn expect_eager() {\n    lazy(|cx| {\n        let mut buf = TestSeqBuffer::empty();\n        let cfg = ServiceConfig::new(\n            KeepAlive::Disabled,\n            Duration::ZERO,\n            Duration::ZERO,\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(echo_path_service(), ExpectHandler, None);\n\n        let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        buf.extend_read_buf(\n            \"\\\n                POST /upload HTTP/1.1\\r\\n\\\n                Content-Length: 5\\r\\n\\\n                Expect: 100-continue\\r\\n\\\n                \\r\\n\\\n                \",\n        );\n\n        pin!(h1);\n\n        assert!(h1.as_mut().poll(cx).is_ready());\n        assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));\n\n        // polls: manual shutdown\n        assert_eq!(h1.poll_count, 2);\n\n        if let DispatcherState::Normal { ref inner } = h1.inner {\n            let io = inner.io.as_ref().unwrap();\n            let mut res = io.write_buf()[..].to_owned();\n            stabilize_date_header(&mut res);\n\n            // Despite the content-length header and even though the request payload has not\n            // been sent, this test expects a complete service response since the payload\n            // is not used at all. The service passed to dispatcher is path echo and doesn't\n            // consume payload bytes.\n            assert_eq!(\n                str::from_utf8(&res).unwrap(),\n                \"\\\n                    HTTP/1.1 100 Continue\\r\\n\\\n                    \\r\\n\\\n                    HTTP/1.1 200 OK\\r\\n\\\n                    content-length: 7\\r\\n\\\n                    connection: close\\r\\n\\\n                    date: Thu, 01 Jan 1970 12:34:56 UTC\\r\\n\\\n                    \\r\\n\\\n                    /upload\\\n                    \"\n            );\n        }\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn upgrade_handling() {\n    struct TestUpgrade;\n\n    impl<T> Service<(Request, Framed<T, Codec>)> for TestUpgrade {\n        type Response = ();\n        type Error = Error;\n        type Future = Ready<Result<Self::Response, Self::Error>>;\n\n        actix_service::always_ready!();\n\n        fn call(&self, (req, _framed): (Request, Framed<T, Codec>)) -> Self::Future {\n            assert_eq!(req.method(), Method::GET);\n            assert!(req.upgrade());\n            assert_eq!(req.headers().get(\"upgrade\").unwrap(), \"websocket\");\n            ready(Ok(()))\n        }\n    }\n\n    lazy(|cx| {\n        let mut buf = TestSeqBuffer::empty();\n        let cfg = ServiceConfig::new(\n            KeepAlive::Disabled,\n            Duration::ZERO,\n            Duration::ZERO,\n            false,\n            None,\n        );\n\n        let services = HttpFlow::new(ok_service(), ExpectHandler, Some(TestUpgrade));\n\n        let h1 = Dispatcher::<_, _, _, _, TestUpgrade>::new(\n            buf.clone(),\n            services,\n            cfg,\n            None,\n            OnConnectData::default(),\n        );\n\n        buf.extend_read_buf(\n            \"\\\n                GET /ws HTTP/1.1\\r\\n\\\n                Connection: Upgrade\\r\\n\\\n                Upgrade: websocket\\r\\n\\\n                \\r\\n\\\n                \",\n        );\n\n        pin!(h1);\n\n        assert!(h1.as_mut().poll(cx).is_ready());\n        assert!(matches!(&h1.inner, DispatcherState::Upgrade { .. }));\n\n        // polls: manual shutdown\n        assert_eq!(h1.poll_count, 2);\n    })\n    .await;\n}\n\n// fix in #2624 reverted temporarily\n// complete fix tracked in #2745\n#[ignore]\n#[actix_rt::test]\nasync fn handler_drop_payload() {\n    let _ = env_logger::try_init();\n\n    let mut buf = TestBuffer::new(http_msg(\n        r\"\n        POST /drop-payload HTTP/1.1\n        Content-Length: 3\n        \n        abc\n        \",\n    ));\n\n    let services = HttpFlow::new(\n        drop_payload_service(),\n        ExpectHandler,\n        None::<UpgradeHandler>,\n    );\n\n    let h1 = Dispatcher::new(\n        buf.clone(),\n        services,\n        ServiceConfig::default(),\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(h1.as_mut().poll(cx).is_pending());\n\n        // polls: manual\n        assert_eq!(h1.poll_count, 1);\n\n        let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 200 OK\n            content-length: 15\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            payload dropped\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n\n        if let DispatcherStateProj::Normal { inner } = h1.as_mut().project().inner.project() {\n            assert!(inner.state.is_none());\n        }\n    })\n    .await;\n\n    lazy(|cx| {\n        // add message that claims to have payload longer than provided\n        buf.extend_read_buf(http_msg(\n            r\"\n            POST /drop-payload HTTP/1.1\n            Content-Length: 200\n            \n            abc\n            \",\n        ));\n\n        assert!(h1.as_mut().poll(cx).is_pending());\n\n        // polls: manual => manual\n        assert_eq!(h1.poll_count, 2);\n\n        let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        // expect response immediately even though request side has not finished reading payload\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 200 OK\n            content-length: 15\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            payload dropped\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n    })\n    .await;\n\n    lazy(|cx| {\n        assert!(h1.as_mut().poll(cx).is_ready());\n\n        // polls: manual => manual => manual\n        assert_eq!(h1.poll_count, 3);\n\n        let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        // expect that unrequested error response is sent back since connection could not be cleaned\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 500 Internal Server Error\n            content-length: 0\n            connection: close\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n    })\n    .await;\n}\n\n// Handler drops request payload without reading it. Server should keep reading and discarding the\n// rest of the request body so clients that do not read the response until they've finished\n// writing the request (like `requests` in Python) do not deadlock.\n// ref. https://github.com/actix/actix-web/issues/2972\n#[actix_rt::test]\nasync fn handler_drop_payload_drains_body() {\n    let _ = env_logger::try_init();\n\n    let mut buf = TestSeqBuffer::new(http_msg(\n        r\"\n        POST /drop-payload HTTP/1.1\n        Transfer-Encoding: chunked\n        \n        \",\n    ));\n\n    let services = HttpFlow::new(\n        drop_payload_service(),\n        ExpectHandler,\n        None::<UpgradeHandler>,\n    );\n\n    let h1 = Dispatcher::new(\n        buf.clone(),\n        services,\n        ServiceConfig::default(),\n        None,\n        OnConnectData::default(),\n    );\n    pin!(h1);\n\n    lazy(|cx| {\n        assert!(h1.as_mut().poll(cx).is_pending());\n\n        let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 200 OK\n            content-length: 15\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            payload dropped\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n    })\n    .await;\n\n    // stream a body larger than the dispatcher read buffer limit; it should still be drained\n    // (read + decoded + discarded) without stalling.\n    for _ in 0..32 {\n        let data = vec![b'a'; 8192];\n        let mut chunk = BytesMut::new();\n        chunk.extend_from_slice(format!(\"{:x}\\r\\n\", data.len()).as_bytes());\n        chunk.extend_from_slice(&data);\n        chunk.extend_from_slice(b\"\\r\\n\");\n\n        buf.extend_read_buf(chunk);\n\n        lazy(|cx| {\n            assert!(h1.as_mut().poll(cx).is_pending());\n            assert!(buf.take_write_buf().is_empty());\n            assert!(buf.read_buf().is_empty());\n        })\n        .await;\n    }\n\n    // terminating chunk\n    buf.extend_read_buf(b\"0\\r\\n\\r\\n\");\n\n    lazy(|cx| {\n        assert!(h1.as_mut().poll(cx).is_pending());\n        assert!(buf.take_write_buf().is_empty());\n        assert!(buf.read_buf().is_empty());\n    })\n    .await;\n\n    // connection should be able to accept another request after draining the previous body\n    buf.extend_read_buf(http_msg(\"GET /drop-payload HTTP/1.1\"));\n\n    lazy(|cx| {\n        assert!(h1.as_mut().poll(cx).is_pending());\n\n        let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n        stabilize_date_header(&mut res);\n        let res = &res[..];\n\n        let exp = http_msg(\n            r\"\n            HTTP/1.1 200 OK\n            content-length: 15\n            date: Thu, 01 Jan 1970 12:34:56 UTC\n\n            payload dropped\n            \",\n        );\n\n        assert_eq!(\n            res,\n            exp,\n            \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n            String::from_utf8_lossy(res),\n            String::from_utf8_lossy(&exp)\n        );\n    })\n    .await;\n}\n\n#[actix_rt::test]\nasync fn allow_half_closed() {\n    let buf = TestSeqBuffer::new(http_msg(\"GET / HTTP/1.1\"));\n    buf.close_read();\n    let services = HttpFlow::new(YieldService, ExpectHandler, None::<UpgradeHandler>);\n\n    let mut cx = Context::from_waker(futures_util::task::noop_waker_ref());\n    let disptacher = Dispatcher::new(\n        buf.clone(),\n        services,\n        ServiceConfig::default(),\n        None,\n        OnConnectData::default(),\n    );\n    pin!(disptacher);\n\n    assert!(disptacher.as_mut().poll(&mut cx).is_pending());\n    assert_eq!(disptacher.poll_count, 1);\n\n    assert!(disptacher.as_mut().poll(&mut cx).is_ready());\n    assert_eq!(disptacher.poll_count, 3);\n\n    let mut res = BytesMut::from(buf.take_write_buf().as_ref());\n    stabilize_date_header(&mut res);\n    let exp = http_msg(\n        r\"\n        HTTP/1.1 200 OK\n        content-length: 0\n        date: Thu, 01 Jan 1970 12:34:56 UTC\n        \",\n    );\n    assert_eq!(\n        res,\n        exp,\n        \"\\nexpected response not in write buffer:\\n\\\n               response: {:?}\\n\\\n               expected: {:?}\",\n        String::from_utf8_lossy(&res),\n        String::from_utf8_lossy(&exp)\n    );\n\n    let DispatcherStateProj::Normal { inner } = disptacher.as_mut().project().inner.project()\n    else {\n        panic!(\"End dispatcher state should be Normal\");\n    };\n    assert!(inner.state.is_none());\n}\n\n#[actix_rt::test]\nasync fn disallow_half_closed() {\n    use crate::{config::ServiceConfigBuilder, h1::dispatcher::State};\n\n    let buf = TestSeqBuffer::new(http_msg(\"GET / HTTP/1.1\"));\n    buf.close_read();\n    let services = HttpFlow::new(YieldService, ExpectHandler, None::<UpgradeHandler>);\n    let config = ServiceConfigBuilder::new()\n        .h1_allow_half_closed(false)\n        .build();\n\n    let mut cx = Context::from_waker(futures_util::task::noop_waker_ref());\n    let disptacher = Dispatcher::new(\n        buf.clone(),\n        services,\n        config,\n        None,\n        OnConnectData::default(),\n    );\n    pin!(disptacher);\n\n    assert!(disptacher.as_mut().poll(&mut cx).is_pending());\n    assert_eq!(disptacher.poll_count, 1);\n\n    assert!(disptacher.as_mut().poll(&mut cx).is_ready());\n    assert_eq!(disptacher.poll_count, 2);\n\n    let res = BytesMut::from(buf.take_write_buf().as_ref());\n    assert!(res.is_empty());\n\n    let DispatcherStateProj::Normal { inner } = disptacher.as_mut().project().inner.project()\n    else {\n        panic!(\"End dispatcher state should be Normal\");\n    };\n    assert!(matches!(inner.state, State::ServiceCall { .. }))\n}\n\nfn http_msg(msg: impl AsRef<str>) -> BytesMut {\n    let mut msg = msg\n        .as_ref()\n        .trim()\n        .split('\\n')\n        .map(|line| [line.trim_start(), \"\\r\"].concat())\n        .collect::<Vec<_>>()\n        .join(\"\\n\");\n\n    // remove trailing \\r\n    msg.pop();\n\n    if !msg.is_empty() && !msg.contains(\"\\r\\n\\r\\n\") {\n        msg.push_str(\"\\r\\n\\r\\n\");\n    }\n\n    BytesMut::from(msg.as_bytes())\n}\n\n#[test]\nfn http_msg_creates_msg() {\n    assert_eq!(http_msg(r\"\"), \"\");\n\n    assert_eq!(\n        http_msg(\n            r\"\n            POST / HTTP/1.1\n            Content-Length: 3\n            \n            abc\n            \"\n        ),\n        \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\"\n    );\n\n    assert_eq!(\n        http_msg(\n            r\"\n            GET / HTTP/1.1\n            Content-Length: 3\n            \n            \"\n        ),\n        \"GET / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\n\"\n    );\n}\n"
  },
  {
    "path": "actix-http/src/h1/encoder.rs",
    "content": "use std::{\n    cmp,\n    io::{self, Write as _},\n    marker::PhantomData,\n    ptr::copy_nonoverlapping,\n    slice::from_raw_parts_mut,\n};\n\nuse bytes::{BufMut, BytesMut};\n\nuse crate::{\n    body::BodySize,\n    header::{\n        map::Value, HeaderMap, HeaderName, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,\n    },\n    helpers, ConnectionType, RequestHeadType, Response, ServiceConfig, StatusCode, Version,\n};\n\nconst AVERAGE_HEADER_SIZE: usize = 30;\n\n#[derive(Debug)]\npub(crate) struct MessageEncoder<T: MessageType> {\n    #[allow(dead_code)]\n    pub length: BodySize,\n    pub te: TransferEncoding,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: MessageType> Default for MessageEncoder<T> {\n    fn default() -> Self {\n        MessageEncoder {\n            length: BodySize::None,\n            te: TransferEncoding::empty(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\npub(crate) trait MessageType: Sized {\n    fn status(&self) -> Option<StatusCode>;\n\n    fn headers(&self) -> &HeaderMap;\n\n    fn extra_headers(&self) -> Option<&HeaderMap>;\n\n    fn camel_case(&self) -> bool {\n        false\n    }\n\n    fn chunked(&self) -> bool;\n\n    fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()>;\n\n    fn encode_headers(\n        &mut self,\n        dst: &mut BytesMut,\n        version: Version,\n        mut length: BodySize,\n        conn_type: ConnectionType,\n        config: &ServiceConfig,\n    ) -> io::Result<()> {\n        let chunked = self.chunked();\n        let mut skip_len = length != BodySize::Stream;\n        let camel_case = self.camel_case();\n\n        // Content length\n        if let Some(status) = self.status() {\n            match status {\n                StatusCode::CONTINUE\n                | StatusCode::SWITCHING_PROTOCOLS\n                | StatusCode::PROCESSING\n                | StatusCode::NO_CONTENT => {\n                    // skip content-length and transfer-encoding headers\n                    // see https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1\n                    // and https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2\n                    skip_len = true;\n                    length = BodySize::None\n                }\n\n                StatusCode::NOT_MODIFIED => {\n                    // 304 responses should never have a body but should retain a manually set\n                    // content-length header\n                    // see https://datatracker.ietf.org/doc/html/rfc7232#section-4.1\n                    skip_len = false;\n                    length = BodySize::None;\n                }\n\n                _ => {}\n            }\n        }\n\n        match length {\n            BodySize::Stream => {\n                if chunked {\n                    skip_len = true;\n                    if camel_case {\n                        dst.put_slice(b\"\\r\\nTransfer-Encoding: chunked\\r\\n\")\n                    } else {\n                        dst.put_slice(b\"\\r\\ntransfer-encoding: chunked\\r\\n\")\n                    }\n                } else {\n                    skip_len = false;\n                    dst.put_slice(b\"\\r\\n\");\n                }\n            }\n            BodySize::Sized(0) if camel_case => dst.put_slice(b\"\\r\\nContent-Length: 0\\r\\n\"),\n            BodySize::Sized(0) => dst.put_slice(b\"\\r\\ncontent-length: 0\\r\\n\"),\n            BodySize::Sized(len) => helpers::write_content_length(len, dst, camel_case),\n            BodySize::None => dst.put_slice(b\"\\r\\n\"),\n        }\n\n        // Connection\n        match conn_type {\n            ConnectionType::Upgrade => {\n                if camel_case {\n                    dst.put_slice(b\"Connection: Upgrade\\r\\n\")\n                } else {\n                    dst.put_slice(b\"connection: upgrade\\r\\n\")\n                }\n            }\n            ConnectionType::KeepAlive if version < Version::HTTP_11 => {\n                if camel_case {\n                    dst.put_slice(b\"Connection: keep-alive\\r\\n\")\n                } else {\n                    dst.put_slice(b\"connection: keep-alive\\r\\n\")\n                }\n            }\n            ConnectionType::Close if version >= Version::HTTP_11 => {\n                if camel_case {\n                    dst.put_slice(b\"Connection: close\\r\\n\")\n                } else {\n                    dst.put_slice(b\"connection: close\\r\\n\")\n                }\n            }\n            _ => {}\n        }\n\n        // write headers\n\n        let mut has_date = false;\n\n        let mut buf = dst.chunk_mut().as_mut_ptr();\n        let mut remaining = dst.capacity() - dst.len();\n\n        // tracks bytes written since last buffer resize\n        // since buf is a raw pointer to a bytes container storage but is written to without the\n        // container's knowledge, this is used to sync the containers cursor after data is written\n        let mut pos = 0;\n\n        self.write_headers(|key, value| {\n            match *key {\n                CONNECTION => return,\n                TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => return,\n                DATE => has_date = true,\n                _ => {}\n            }\n\n            let k = key.as_str().as_bytes();\n            let k_len = k.len();\n\n            for val in value.iter() {\n                let v = val.as_ref();\n                let v_len = v.len();\n\n                // key length + value length + colon + space + \\r\\n\n                let len = k_len + v_len + 4;\n\n                if len > remaining {\n                    // SAFETY: all the bytes written up to position \"pos\" are initialized\n                    // the written byte count and pointer advancement are kept in sync\n                    unsafe {\n                        dst.advance_mut(pos);\n                    }\n\n                    pos = 0;\n                    dst.reserve(len * 2);\n                    remaining = dst.capacity() - dst.len();\n\n                    // re-assign buf raw pointer since it's possible that the buffer was\n                    // reallocated and/or resized\n                    buf = dst.chunk_mut().as_mut_ptr();\n                }\n\n                // SAFETY: on each write, it is enough to ensure that the advancement of\n                // the cursor matches the number of bytes written\n                unsafe {\n                    if camel_case {\n                        // use Camel-Case headers\n                        write_camel_case(k, buf, k_len);\n                    } else {\n                        write_data(k, buf, k_len);\n                    }\n\n                    buf = buf.add(k_len);\n\n                    write_data(b\": \", buf, 2);\n                    buf = buf.add(2);\n\n                    write_data(v, buf, v_len);\n                    buf = buf.add(v_len);\n\n                    write_data(b\"\\r\\n\", buf, 2);\n                    buf = buf.add(2);\n                };\n\n                pos += len;\n                remaining -= len;\n            }\n        });\n\n        // final cursor synchronization with the bytes container\n        //\n        // SAFETY: all the bytes written up to position \"pos\" are initialized\n        // the written byte count and pointer advancement are kept in sync\n        unsafe {\n            dst.advance_mut(pos);\n        }\n\n        if !has_date {\n            // optimized date header, write_date_header writes its own \\r\\n\n            config.write_date_header(dst, camel_case);\n        }\n\n        // end-of-headers marker\n        dst.extend_from_slice(b\"\\r\\n\");\n\n        Ok(())\n    }\n\n    fn write_headers<F>(&mut self, mut f: F)\n    where\n        F: FnMut(&HeaderName, &Value),\n    {\n        match self.extra_headers() {\n            Some(headers) => {\n                // merging headers from head and extra headers.\n                self.headers()\n                    .inner\n                    .iter()\n                    .filter(|(name, _)| !headers.contains_key(*name))\n                    .chain(headers.inner.iter())\n                    .for_each(|(k, v)| f(k, v))\n            }\n            None => self.headers().inner.iter().for_each(|(k, v)| f(k, v)),\n        }\n    }\n}\n\nimpl MessageType for Response<()> {\n    fn status(&self) -> Option<StatusCode> {\n        Some(self.head().status)\n    }\n\n    fn chunked(&self) -> bool {\n        self.head().chunked()\n    }\n\n    fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    fn extra_headers(&self) -> Option<&HeaderMap> {\n        None\n    }\n\n    fn camel_case(&self) -> bool {\n        self.head()\n            .flags\n            .contains(crate::message::Flags::CAMEL_CASE)\n    }\n\n    fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> {\n        let head = self.head();\n        let reason = head.reason().as_bytes();\n        dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE + reason.len());\n\n        // status line\n        helpers::write_status_line(head.version, head.status.as_u16(), dst);\n        dst.put_slice(reason);\n        Ok(())\n    }\n}\n\nimpl MessageType for RequestHeadType {\n    fn status(&self) -> Option<StatusCode> {\n        None\n    }\n\n    fn chunked(&self) -> bool {\n        self.as_ref().chunked()\n    }\n\n    fn camel_case(&self) -> bool {\n        self.as_ref().camel_case_headers()\n    }\n\n    fn headers(&self) -> &HeaderMap {\n        self.as_ref().headers()\n    }\n\n    fn extra_headers(&self) -> Option<&HeaderMap> {\n        self.extra_headers()\n    }\n\n    fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> {\n        let head = self.as_ref();\n        dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);\n        write!(\n            helpers::MutWriter(dst),\n            \"{} {} {}\",\n            head.method,\n            head.uri.path_and_query().map(|u| u.as_str()).unwrap_or(\"/\"),\n            match head.version {\n                Version::HTTP_09 => \"HTTP/0.9\",\n                Version::HTTP_10 => \"HTTP/1.0\",\n                Version::HTTP_11 => \"HTTP/1.1\",\n                Version::HTTP_2 => \"HTTP/2.0\",\n                Version::HTTP_3 => \"HTTP/3.0\",\n                _ => return Err(io::Error::other(\"Unsupported version\")),\n            }\n        )\n        .map_err(io::Error::other)\n    }\n}\n\nimpl<T: MessageType> MessageEncoder<T> {\n    /// Encode chunk.\n    pub fn encode_chunk(&mut self, msg: &[u8], buf: &mut BytesMut) -> io::Result<bool> {\n        self.te.encode(msg, buf)\n    }\n\n    /// Encode EOF.\n    pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {\n        self.te.encode_eof(buf)\n    }\n\n    /// Encode message.\n    pub fn encode(\n        &mut self,\n        dst: &mut BytesMut,\n        message: &mut T,\n        head: bool,\n        stream: bool,\n        version: Version,\n        length: BodySize,\n        conn_type: ConnectionType,\n        config: &ServiceConfig,\n    ) -> io::Result<()> {\n        // transfer encoding\n        if !head {\n            self.te = match length {\n                BodySize::Sized(0) => TransferEncoding::empty(),\n                BodySize::Sized(len) => TransferEncoding::length(len),\n                BodySize::Stream => {\n                    if message.chunked() && !stream {\n                        TransferEncoding::chunked()\n                    } else {\n                        TransferEncoding::eof()\n                    }\n                }\n                BodySize::None => TransferEncoding::empty(),\n            };\n        } else {\n            self.te = TransferEncoding::empty();\n        }\n\n        message.encode_status(dst)?;\n        message.encode_headers(dst, version, length, conn_type, config)\n    }\n}\n\n/// Encoders to handle different Transfer-Encodings.\n#[derive(Debug)]\npub(crate) struct TransferEncoding {\n    kind: TransferEncodingKind,\n}\n\n#[derive(Debug, PartialEq, Clone)]\nenum TransferEncodingKind {\n    /// An Encoder for when Transfer-Encoding includes `chunked`.\n    Chunked(bool),\n\n    /// An Encoder for when Content-Length is set.\n    ///\n    /// Enforces that the body is not longer than the Content-Length header.\n    Length(u64),\n\n    /// An Encoder for when Content-Length is not known.\n    ///\n    /// Application decides when to stop writing.\n    Eof,\n}\n\nimpl TransferEncoding {\n    #[inline]\n    pub fn empty() -> TransferEncoding {\n        TransferEncoding {\n            kind: TransferEncodingKind::Length(0),\n        }\n    }\n\n    #[inline]\n    pub fn eof() -> TransferEncoding {\n        TransferEncoding {\n            kind: TransferEncodingKind::Eof,\n        }\n    }\n\n    #[inline]\n    pub fn chunked() -> TransferEncoding {\n        TransferEncoding {\n            kind: TransferEncodingKind::Chunked(false),\n        }\n    }\n\n    #[inline]\n    pub fn length(len: u64) -> TransferEncoding {\n        TransferEncoding {\n            kind: TransferEncodingKind::Length(len),\n        }\n    }\n\n    /// Encode message. Return `EOF` state of encoder\n    #[inline]\n    pub fn encode(&mut self, msg: &[u8], buf: &mut BytesMut) -> io::Result<bool> {\n        match self.kind {\n            TransferEncodingKind::Eof => {\n                let eof = msg.is_empty();\n                buf.extend_from_slice(msg);\n                Ok(eof)\n            }\n            TransferEncodingKind::Chunked(ref mut eof) => {\n                if *eof {\n                    return Ok(true);\n                }\n\n                if msg.is_empty() {\n                    *eof = true;\n                    buf.extend_from_slice(b\"0\\r\\n\\r\\n\");\n                } else {\n                    writeln!(helpers::MutWriter(buf), \"{:X}\\r\", msg.len())\n                        .map_err(io::Error::other)?;\n\n                    buf.reserve(msg.len() + 2);\n                    buf.extend_from_slice(msg);\n                    buf.extend_from_slice(b\"\\r\\n\");\n                }\n                Ok(*eof)\n            }\n            TransferEncodingKind::Length(ref mut remaining) => {\n                if *remaining > 0 {\n                    if msg.is_empty() {\n                        return Ok(*remaining == 0);\n                    }\n                    let len = cmp::min(*remaining, msg.len() as u64);\n\n                    buf.extend_from_slice(&msg[..len as usize]);\n\n                    *remaining -= len;\n                    Ok(*remaining == 0)\n                } else {\n                    Ok(true)\n                }\n            }\n        }\n    }\n\n    /// Encode eof. Return `EOF` state of encoder\n    #[inline]\n    pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {\n        match self.kind {\n            TransferEncodingKind::Eof => Ok(()),\n            TransferEncodingKind::Length(rem) => {\n                if rem != 0 {\n                    Err(io::Error::new(io::ErrorKind::UnexpectedEof, \"\"))\n                } else {\n                    Ok(())\n                }\n            }\n            TransferEncodingKind::Chunked(ref mut eof) => {\n                if !*eof {\n                    *eof = true;\n                    buf.extend_from_slice(b\"0\\r\\n\\r\\n\");\n                }\n                Ok(())\n            }\n        }\n    }\n}\n\n/// # Safety\n/// Callers must ensure that the given `len` matches the given `value` length and that `buf` is\n/// valid for writes of at least `len` bytes.\nunsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {\n    debug_assert_eq!(value.len(), len);\n    copy_nonoverlapping(value.as_ptr(), buf, len);\n}\n\n/// # Safety\n/// Callers must ensure that the given `len` matches the given `value` length and that `buf` is\n/// valid for writes of at least `len` bytes.\nunsafe fn write_camel_case(value: &[u8], buf: *mut u8, len: usize) {\n    // first copy entire (potentially wrong) slice to output\n    write_data(value, buf, len);\n\n    // SAFETY: We just initialized the buffer with `value`\n    let buffer = from_raw_parts_mut(buf, len);\n\n    let mut iter = value.iter();\n\n    // first character should be uppercase\n    if let Some(c @ b'a'..=b'z') = iter.next() {\n        buffer[0] = c & 0b1101_1111;\n    }\n\n    // track 1 ahead of the current position since that's the location being assigned to\n    let mut index = 2;\n\n    // remaining characters after hyphens should also be uppercase\n    while let Some(&c) = iter.next() {\n        if c == b'-' {\n            // advance iter by one and uppercase if needed\n            if let Some(c @ b'a'..=b'z') = iter.next() {\n                buffer[index] = c & 0b1101_1111;\n            }\n            index += 1;\n        }\n\n        index += 1;\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::rc::Rc;\n\n    use bytes::Bytes;\n    use http::header::{AUTHORIZATION, UPGRADE_INSECURE_REQUESTS};\n\n    use super::*;\n    use crate::{\n        header::{HeaderValue, CONTENT_TYPE},\n        RequestHead,\n    };\n\n    #[test]\n    fn test_chunked_te() {\n        let mut bytes = BytesMut::new();\n        let mut enc = TransferEncoding::chunked();\n        {\n            assert!(!enc.encode(b\"test\", &mut bytes).ok().unwrap());\n            assert!(enc.encode(b\"\", &mut bytes).ok().unwrap());\n        }\n        assert_eq!(\n            bytes.split().freeze(),\n            Bytes::from_static(b\"4\\r\\ntest\\r\\n0\\r\\n\\r\\n\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_camel_case() {\n        let mut bytes = BytesMut::with_capacity(2048);\n        let mut head = RequestHead::default();\n        head.set_camel_case_headers(true);\n        head.headers.insert(DATE, HeaderValue::from_static(\"date\"));\n        head.headers\n            .insert(CONTENT_TYPE, HeaderValue::from_static(\"plain/text\"));\n\n        head.headers\n            .insert(UPGRADE_INSECURE_REQUESTS, HeaderValue::from_static(\"1\"));\n\n        let mut head = RequestHeadType::Owned(head);\n\n        let _ = head.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::Sized(0),\n            ConnectionType::Close,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n\n        assert!(data.contains(\"Content-Length: 0\\r\\n\"));\n        assert!(data.contains(\"Connection: close\\r\\n\"));\n        assert!(data.contains(\"Content-Type: plain/text\\r\\n\"));\n        assert!(data.contains(\"Date: date\\r\\n\"));\n        assert!(data.contains(\"Upgrade-Insecure-Requests: 1\\r\\n\"));\n\n        let _ = head.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::None,\n            ConnectionType::Upgrade,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n        assert!(data.contains(\"Connection: Upgrade\\r\\n\"));\n\n        let _ = head.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::Stream,\n            ConnectionType::KeepAlive,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n        assert!(data.contains(\"Transfer-Encoding: chunked\\r\\n\"));\n        assert!(data.contains(\"Content-Type: plain/text\\r\\n\"));\n        assert!(data.contains(\"Date: date\\r\\n\"));\n\n        let mut head = RequestHead::default();\n        head.set_camel_case_headers(false);\n        head.headers.insert(DATE, HeaderValue::from_static(\"date\"));\n        head.headers\n            .insert(CONTENT_TYPE, HeaderValue::from_static(\"plain/text\"));\n        head.headers\n            .append(CONTENT_TYPE, HeaderValue::from_static(\"xml\"));\n\n        let mut head = RequestHeadType::Owned(head);\n        let _ = head.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::Stream,\n            ConnectionType::KeepAlive,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n        assert!(data.contains(\"transfer-encoding: chunked\\r\\n\"));\n        assert!(data.contains(\"content-type: xml\\r\\n\"));\n        assert!(data.contains(\"content-type: plain/text\\r\\n\"));\n        assert!(data.contains(\"date: date\\r\\n\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_extra_headers() {\n        let mut bytes = BytesMut::with_capacity(2048);\n\n        let mut head = RequestHead::default();\n        head.headers.insert(\n            AUTHORIZATION,\n            HeaderValue::from_static(\"some authorization\"),\n        );\n\n        let mut extra_headers = HeaderMap::new();\n        extra_headers.insert(\n            AUTHORIZATION,\n            HeaderValue::from_static(\"another authorization\"),\n        );\n        extra_headers.insert(DATE, HeaderValue::from_static(\"date\"));\n\n        let mut head = RequestHeadType::Rc(Rc::new(head), Some(extra_headers));\n\n        let _ = head.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::Sized(0),\n            ConnectionType::Close,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n        assert!(data.contains(\"content-length: 0\\r\\n\"));\n        assert!(data.contains(\"connection: close\\r\\n\"));\n        assert!(data.contains(\"authorization: another authorization\\r\\n\"));\n        assert!(data.contains(\"date: date\\r\\n\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_no_content_length() {\n        let mut bytes = BytesMut::with_capacity(2048);\n\n        let mut res = Response::with_body(StatusCode::SWITCHING_PROTOCOLS, ());\n        res.headers_mut().insert(DATE, HeaderValue::from_static(\"\"));\n        res.headers_mut()\n            .insert(CONTENT_LENGTH, HeaderValue::from_static(\"0\"));\n\n        let _ = res.encode_headers(\n            &mut bytes,\n            Version::HTTP_11,\n            BodySize::Stream,\n            ConnectionType::Upgrade,\n            &ServiceConfig::default(),\n        );\n        let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();\n        assert!(!data.contains(\"content-length: 0\\r\\n\"));\n        assert!(!data.contains(\"transfer-encoding: chunked\\r\\n\"));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/expect.rs",
    "content": "use actix_service::{Service, ServiceFactory};\nuse actix_utils::future::{ready, Ready};\n\nuse crate::{Error, Request};\n\npub struct ExpectHandler;\n\nimpl ServiceFactory<Request> for ExpectHandler {\n    type Response = Request;\n    type Error = Error;\n    type Config = ();\n    type Service = ExpectHandler;\n    type InitError = Error;\n    type Future = Ready<Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: Self::Config) -> Self::Future {\n        ready(Ok(ExpectHandler))\n    }\n}\n\nimpl Service<Request> for ExpectHandler {\n    type Response = Request;\n    type Error = Error;\n    type Future = Ready<Result<Self::Response, Self::Error>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, req: Request) -> Self::Future {\n        ready(Ok(req))\n        // TODO: add some way to trigger error\n        // Err(error::ErrorExpectationFailed(\"test\"))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/mod.rs",
    "content": "//! HTTP/1 protocol implementation.\n\nuse bytes::{Bytes, BytesMut};\n\nmod chunked;\nmod client;\nmod codec;\nmod decoder;\nmod dispatcher;\n#[cfg(test)]\nmod dispatcher_tests;\nmod encoder;\nmod expect;\nmod payload;\nmod service;\nmod timer;\nmod upgrade;\nmod utils;\n\npub use self::{\n    client::{ClientCodec, ClientPayloadCodec},\n    codec::Codec,\n    dispatcher::Dispatcher,\n    expect::ExpectHandler,\n    payload::Payload,\n    service::{H1Service, H1ServiceHandler},\n    upgrade::UpgradeHandler,\n    utils::SendResponse,\n};\n\n#[derive(Debug)]\n/// Codec message\npub enum Message<T> {\n    /// HTTP message.\n    Item(T),\n\n    /// Payload chunk.\n    Chunk(Option<Bytes>),\n}\n\nimpl<T> From<T> for Message<T> {\n    fn from(item: T) -> Self {\n        Message::Item(item)\n    }\n}\n\n/// Incoming request type\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum MessageType {\n    None,\n    Payload,\n    Stream,\n}\n\nconst LW: usize = 2 * 1024;\nconst HW: usize = 32 * 1024;\n\npub(crate) fn reserve_readbuf(src: &mut BytesMut) {\n    let cap = src.capacity();\n    if cap < LW {\n        src.reserve(HW - cap);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::Request;\n\n    impl Message<Request> {\n        pub fn message(self) -> Request {\n            match self {\n                Message::Item(req) => req,\n                _ => panic!(\"error\"),\n            }\n        }\n\n        pub fn chunk(self) -> Bytes {\n            match self {\n                Message::Chunk(Some(data)) => data,\n                _ => panic!(\"error\"),\n            }\n        }\n\n        pub fn eof(self) -> bool {\n            match self {\n                Message::Chunk(None) => true,\n                Message::Chunk(Some(_)) => false,\n                _ => panic!(\"error\"),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/payload.rs",
    "content": "//! Payload stream\n\nuse std::{\n    cell::RefCell,\n    collections::VecDeque,\n    pin::Pin,\n    rc::{Rc, Weak},\n    task::{Context, Poll, Waker},\n};\n\nuse bytes::Bytes;\nuse futures_core::Stream;\n\nuse crate::error::PayloadError;\n\n/// max buffer size 32k\npub(crate) const MAX_BUFFER_SIZE: usize = 32_768;\n\n#[derive(Debug, PartialEq, Eq)]\npub enum PayloadStatus {\n    Read,\n    Pause,\n    Dropped,\n}\n\n/// Buffered stream of bytes chunks\n///\n/// Payload stores chunks in a vector. First chunk can be received with `poll_next`. Payload does\n/// not notify current task when new data is available.\n///\n/// Payload can be used as `Response` body stream.\n#[derive(Debug)]\npub struct Payload {\n    inner: Rc<RefCell<Inner>>,\n}\n\nimpl Payload {\n    /// Creates a payload stream.\n    ///\n    /// This method construct two objects responsible for bytes stream generation:\n    /// - `PayloadSender` - *Sender* side of the stream\n    /// - `Payload` - *Receiver* side of the stream\n    pub fn create(eof: bool) -> (PayloadSender, Payload) {\n        let shared = Rc::new(RefCell::new(Inner::new(eof)));\n\n        (\n            PayloadSender::new(Rc::downgrade(&shared)),\n            Payload { inner: shared },\n        )\n    }\n\n    /// Creates an empty payload.\n    pub(crate) fn empty() -> Payload {\n        Payload {\n            inner: Rc::new(RefCell::new(Inner::new(true))),\n        }\n    }\n\n    /// Length of the data in this payload\n    #[cfg(test)]\n    pub fn len(&self) -> usize {\n        self.inner.borrow().len()\n    }\n\n    /// Is payload empty\n    #[cfg(test)]\n    pub fn is_empty(&self) -> bool {\n        self.inner.borrow().len() == 0\n    }\n\n    /// Put unused data back to payload\n    #[inline]\n    pub fn unread_data(&mut self, data: Bytes) {\n        self.inner.borrow_mut().unread_data(data);\n    }\n}\n\nimpl Stream for Payload {\n    type Item = Result<Bytes, PayloadError>;\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, PayloadError>>> {\n        Pin::new(&mut *self.inner.borrow_mut()).poll_next(cx)\n    }\n}\n\n/// Sender part of the payload stream\npub struct PayloadSender {\n    inner: Weak<RefCell<Inner>>,\n}\n\nimpl PayloadSender {\n    fn new(inner: Weak<RefCell<Inner>>) -> Self {\n        Self { inner }\n    }\n\n    #[inline]\n    pub fn set_error(&mut self, err: PayloadError) {\n        if let Some(shared) = self.inner.upgrade() {\n            shared.borrow_mut().set_error(err)\n        }\n    }\n\n    #[inline]\n    pub fn feed_eof(&mut self) {\n        if let Some(shared) = self.inner.upgrade() {\n            shared.borrow_mut().feed_eof()\n        }\n    }\n\n    #[inline]\n    pub fn feed_data(&mut self, data: Bytes) {\n        if let Some(shared) = self.inner.upgrade() {\n            shared.borrow_mut().feed_data(data)\n        }\n    }\n\n    #[allow(clippy::needless_pass_by_ref_mut)]\n    #[inline]\n    pub fn need_read(&self, cx: &mut Context<'_>) -> PayloadStatus {\n        // we check need_read only if Payload (other side) is alive,\n        // otherwise always return true (consume payload)\n        if let Some(shared) = self.inner.upgrade() {\n            if shared.borrow().need_read {\n                PayloadStatus::Read\n            } else {\n                shared.borrow_mut().register_io(cx);\n                PayloadStatus::Pause\n            }\n        } else {\n            PayloadStatus::Dropped\n        }\n    }\n\n    #[inline]\n    pub fn is_dropped(&self) -> bool {\n        self.inner.strong_count() == 0\n    }\n}\n\n#[derive(Debug)]\nstruct Inner {\n    len: usize,\n    eof: bool,\n    err: Option<PayloadError>,\n    need_read: bool,\n    items: VecDeque<Bytes>,\n    task: Option<Waker>,\n    io_task: Option<Waker>,\n}\n\nimpl Inner {\n    fn new(eof: bool) -> Self {\n        Inner {\n            eof,\n            len: 0,\n            err: None,\n            items: VecDeque::new(),\n            need_read: true,\n            task: None,\n            io_task: None,\n        }\n    }\n\n    /// Wake up future waiting for payload data to be available.\n    fn wake(&mut self) {\n        if let Some(waker) = self.task.take() {\n            waker.wake();\n        }\n    }\n\n    /// Wake up future feeding data to Payload.\n    fn wake_io(&mut self) {\n        if let Some(waker) = self.io_task.take() {\n            waker.wake();\n        }\n    }\n\n    /// Register future waiting data from payload.\n    /// Waker would be used in `Inner::wake`\n    fn register(&mut self, cx: &Context<'_>) {\n        if self.task.as_ref().is_none_or(|w| !cx.waker().will_wake(w)) {\n            self.task = Some(cx.waker().clone());\n        }\n    }\n\n    // Register future feeding data to payload.\n    /// Waker would be used in `Inner::wake_io`\n    fn register_io(&mut self, cx: &Context<'_>) {\n        if self\n            .io_task\n            .as_ref()\n            .is_none_or(|w| !cx.waker().will_wake(w))\n        {\n            self.io_task = Some(cx.waker().clone());\n        }\n    }\n\n    #[inline]\n    fn set_error(&mut self, err: PayloadError) {\n        self.err = Some(err);\n        self.wake();\n    }\n\n    #[inline]\n    fn feed_eof(&mut self) {\n        self.eof = true;\n        self.wake();\n    }\n\n    #[inline]\n    fn feed_data(&mut self, data: Bytes) {\n        self.len += data.len();\n        self.items.push_back(data);\n        self.need_read = self.len < MAX_BUFFER_SIZE;\n        self.wake();\n    }\n\n    #[cfg(test)]\n    fn len(&self) -> usize {\n        self.len\n    }\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &Context<'_>,\n    ) -> Poll<Option<Result<Bytes, PayloadError>>> {\n        if let Some(data) = self.items.pop_front() {\n            self.len -= data.len();\n            self.need_read = self.len < MAX_BUFFER_SIZE;\n\n            if self.need_read && !self.eof {\n                self.register(cx);\n            }\n            self.wake_io();\n            Poll::Ready(Some(Ok(data)))\n        } else if let Some(err) = self.err.take() {\n            Poll::Ready(Some(Err(err)))\n        } else if self.eof {\n            Poll::Ready(None)\n        } else {\n            self.need_read = true;\n            self.register(cx);\n            self.wake_io();\n            Poll::Pending\n        }\n    }\n\n    fn unread_data(&mut self, data: Bytes) {\n        self.len += data.len();\n        self.items.push_front(data);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{task::Poll, time::Duration};\n\n    use actix_rt::time::timeout;\n    use actix_utils::future::poll_fn;\n    use futures_util::{FutureExt, StreamExt};\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n    use tokio::sync::oneshot;\n\n    use super::*;\n\n    assert_impl_all!(Payload: Unpin);\n    assert_not_impl_any!(Payload: Send, Sync);\n\n    assert_impl_all!(Inner: Unpin, Send, Sync);\n\n    const WAKE_TIMEOUT: Duration = Duration::from_secs(2);\n\n    fn prepare_waking_test(\n        mut payload: Payload,\n        expected: Option<Result<(), ()>>,\n    ) -> (oneshot::Receiver<()>, actix_rt::task::JoinHandle<()>) {\n        let (tx, rx) = oneshot::channel();\n\n        let handle = actix_rt::spawn(async move {\n            // Make sure to poll once to set the waker\n            poll_fn(|cx| {\n                assert!(payload.poll_next_unpin(cx).is_pending());\n                Poll::Ready(())\n            })\n            .await;\n            tx.send(()).unwrap();\n\n            // actix-rt is single-threaded, so this won't race with `rx.await`\n            let mut pend_once = false;\n            poll_fn(|_| {\n                if pend_once {\n                    Poll::Ready(())\n                } else {\n                    // Return pending without storing wakers, we already did on the previous\n                    // `poll_fn`, now this task will only continue if the `sender` wakes us\n                    pend_once = true;\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            let got = payload.next().now_or_never().unwrap();\n            match expected {\n                Some(Ok(_)) => assert!(got.unwrap().is_ok()),\n                Some(Err(_)) => assert!(got.unwrap().is_err()),\n                None => assert!(got.is_none()),\n            }\n        });\n        (rx, handle)\n    }\n\n    #[actix_rt::test]\n    async fn wake_on_error() {\n        let (mut sender, payload) = Payload::create(false);\n        let (rx, handle) = prepare_waking_test(payload, Some(Err(())));\n\n        rx.await.unwrap();\n        sender.set_error(PayloadError::Incomplete(None));\n        timeout(WAKE_TIMEOUT, handle).await.unwrap().unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn wake_on_eof() {\n        let (mut sender, payload) = Payload::create(false);\n        let (rx, handle) = prepare_waking_test(payload, None);\n\n        rx.await.unwrap();\n        sender.feed_eof();\n        timeout(WAKE_TIMEOUT, handle).await.unwrap().unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn test_unread_data() {\n        let (_, mut payload) = Payload::create(false);\n\n        payload.unread_data(Bytes::from(\"data\"));\n        assert!(!payload.is_empty());\n        assert_eq!(payload.len(), 4);\n\n        assert_eq!(\n            Bytes::from(\"data\"),\n            poll_fn(|cx| Pin::new(&mut payload).poll_next(cx))\n                .await\n                .unwrap()\n                .unwrap()\n        );\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/service.rs",
    "content": "use std::{\n    fmt,\n    marker::PhantomData,\n    net,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse actix_rt::net::TcpStream;\nuse actix_service::{\n    fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,\n};\nuse actix_utils::future::ready;\nuse futures_core::future::LocalBoxFuture;\nuse tracing::error;\n\nuse super::{codec::Codec, dispatcher::Dispatcher, ExpectHandler, UpgradeHandler};\nuse crate::{\n    body::{BoxBody, MessageBody},\n    config::ServiceConfig,\n    error::DispatchError,\n    service::HttpServiceHandler,\n    ConnectCallback, OnConnectData, Request, Response,\n};\n\n/// `ServiceFactory` implementation for HTTP1 transport\npub struct H1Service<T, S, B, X = ExpectHandler, U = UpgradeHandler> {\n    srv: S,\n    cfg: ServiceConfig,\n    expect: X,\n    upgrade: Option<U>,\n    on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<B>,\n}\n\nimpl<T, S, B> H1Service<T, S, B>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>>,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>>,\n    B: MessageBody,\n{\n    /// Create new `HttpService` instance with config.\n    pub(crate) fn with_config<F: IntoServiceFactory<S, Request>>(\n        cfg: ServiceConfig,\n        service: F,\n    ) -> Self {\n        H1Service {\n            cfg,\n            srv: service.into_factory(),\n            expect: ExpectHandler,\n            upgrade: None,\n            on_connect_ext: None,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<S, B, X, U> H1Service<TcpStream, S, B, X, U>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>>,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n\n    X: ServiceFactory<Request, Config = (), Response = Request>,\n    X::Future: 'static,\n    X::Error: Into<Response<BoxBody>>,\n    X::InitError: fmt::Debug,\n\n    U: ServiceFactory<(Request, Framed<TcpStream, Codec>), Config = (), Response = ()>,\n    U::Future: 'static,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n    U::InitError: fmt::Debug,\n{\n    /// Create simple tcp stream service\n    pub fn tcp(\n        self,\n    ) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>\n    {\n        fn_service(|io: TcpStream| {\n            let peer_addr = io.peer_addr().ok();\n            ready(Ok((io, peer_addr)))\n        })\n        .and_then(self)\n    }\n}\n\n#[cfg(feature = \"openssl\")]\nmod openssl {\n    use actix_tls::accept::{\n        openssl::{\n            reexports::{Error as SslError, SslAcceptor},\n            Acceptor, TlsStream,\n        },\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n\n        B: MessageBody,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create OpenSSL based service.\n        pub fn openssl(\n            self,\n            acceptor: SslAcceptor,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<SslError, DispatchError>,\n            InitError = (),\n        > {\n            Acceptor::new(acceptor)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(|io: TlsStream<TcpStream>| {\n                    let peer_addr = io.get_ref().peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_20\")]\nmod rustls_0_20 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_20::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n\n        B: MessageBody,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.20 based service.\n        pub fn rustls(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(|io: TlsStream<TcpStream>| {\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_21\")]\nmod rustls_0_21 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n\n        B: MessageBody,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.21 based service.\n        pub fn rustls_021(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(|io: TlsStream<TcpStream>| {\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_22\")]\nmod rustls_0_22 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n\n        B: MessageBody,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.22 based service.\n        pub fn rustls_0_22(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(|io: TlsStream<TcpStream>| {\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_23\")]\nmod rustls_0_23 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>>,\n\n        B: MessageBody,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.23 based service.\n        pub fn rustls_0_23(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(|io: TlsStream<TcpStream>| {\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\nimpl<T, S, B, X, U> H1Service<T, S, B, X, U>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n    S::InitError: fmt::Debug,\n    B: MessageBody,\n{\n    pub fn expect<X1>(self, expect: X1) -> H1Service<T, S, B, X1, U>\n    where\n        X1: ServiceFactory<Request, Response = Request>,\n        X1::Error: Into<Response<BoxBody>>,\n        X1::InitError: fmt::Debug,\n    {\n        H1Service {\n            expect,\n            cfg: self.cfg,\n            srv: self.srv,\n            upgrade: self.upgrade,\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    pub fn upgrade<U1>(self, upgrade: Option<U1>) -> H1Service<T, S, B, X, U1>\n    where\n        U1: ServiceFactory<(Request, Framed<T, Codec>), Response = ()>,\n        U1::Error: fmt::Display,\n        U1::InitError: fmt::Debug,\n    {\n        H1Service {\n            upgrade,\n            cfg: self.cfg,\n            srv: self.srv,\n            expect: self.expect,\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Set on connect callback.\n    pub(crate) fn on_connect_ext(mut self, f: Option<Rc<ConnectCallback<T>>>) -> Self {\n        self.on_connect_ext = f;\n        self\n    }\n}\n\nimpl<T, S, B, X, U> ServiceFactory<(T, Option<net::SocketAddr>)> for H1Service<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin + 'static,\n\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n    S::InitError: fmt::Debug,\n\n    B: MessageBody,\n\n    X: ServiceFactory<Request, Config = (), Response = Request>,\n    X::Future: 'static,\n    X::Error: Into<Response<BoxBody>>,\n    X::InitError: fmt::Debug,\n\n    U: ServiceFactory<(Request, Framed<T, Codec>), Config = (), Response = ()>,\n    U::Future: 'static,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n    U::InitError: fmt::Debug,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Config = ();\n    type Service = H1ServiceHandler<T, S::Service, B, X::Service, U::Service>;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let service = self.srv.new_service(());\n        let expect = self.expect.new_service(());\n        let upgrade = self.upgrade.as_ref().map(|s| s.new_service(()));\n        let on_connect_ext = self.on_connect_ext.clone();\n        let cfg = self.cfg.clone();\n\n        Box::pin(async move {\n            let expect = expect.await.map_err(|err| {\n                tracing::error!(\"Initialization of HTTP expect service error: {err:?}\");\n            })?;\n\n            let upgrade = match upgrade {\n                Some(upgrade) => {\n                    let upgrade = upgrade.await.map_err(|err| {\n                        tracing::error!(\"Initialization of HTTP upgrade service error: {err:?}\");\n                    })?;\n                    Some(upgrade)\n                }\n                None => None,\n            };\n\n            let service = service\n                .await\n                .map_err(|err| error!(\"Initialization of HTTP service error: {err:?}\"))?;\n\n            Ok(H1ServiceHandler::new(\n                cfg,\n                service,\n                expect,\n                upgrade,\n                on_connect_ext,\n            ))\n        })\n    }\n}\n\n/// `Service` implementation for HTTP/1 transport\npub type H1ServiceHandler<T, S, B, X, U> = HttpServiceHandler<T, S, B, X, U>;\n\nimpl<T, S, B, X, U> Service<(T, Option<net::SocketAddr>)> for HttpServiceHandler<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, Codec>), Response = ()>,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Future = Dispatcher<T, S, B, X, U>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self._poll_ready(cx).map_err(|err| {\n            error!(\"HTTP/1 service readiness error: {:?}\", err);\n            DispatchError::Service(err)\n        })\n    }\n\n    fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {\n        let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());\n        Dispatcher::new(io, Rc::clone(&self.flow), self.cfg.clone(), addr, conn_data)\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/timer.rs",
    "content": "use std::{fmt, future::Future, pin::Pin, task::Context};\n\nuse actix_rt::time::{Instant, Sleep};\nuse tracing::trace;\n\n#[derive(Debug)]\npub(super) enum TimerState {\n    Disabled,\n    Inactive,\n    Active { timer: Pin<Box<Sleep>> },\n}\n\nimpl TimerState {\n    pub(super) fn new(enabled: bool) -> Self {\n        if enabled {\n            Self::Inactive\n        } else {\n            Self::Disabled\n        }\n    }\n\n    pub(super) fn is_enabled(&self) -> bool {\n        matches!(self, Self::Active { .. } | Self::Inactive)\n    }\n\n    pub(super) fn set(&mut self, timer: Sleep, line: u32) {\n        if matches!(self, Self::Disabled) {\n            trace!(\"setting disabled timer from line {}\", line);\n        }\n\n        *self = Self::Active {\n            timer: Box::pin(timer),\n        };\n    }\n\n    pub(super) fn set_and_init(&mut self, cx: &mut Context<'_>, timer: Sleep, line: u32) {\n        self.set(timer, line);\n        self.init(cx);\n    }\n\n    pub(super) fn clear(&mut self, line: u32) {\n        if matches!(self, Self::Disabled) {\n            trace!(\"trying to clear a disabled timer from line {}\", line);\n        }\n\n        if matches!(self, Self::Inactive) {\n            trace!(\"trying to clear an inactive timer from line {}\", line);\n        }\n\n        *self = Self::Inactive;\n    }\n\n    pub(super) fn init(&mut self, cx: &mut Context<'_>) {\n        if let TimerState::Active { timer } = self {\n            let _ = timer.as_mut().poll(cx);\n        }\n    }\n}\n\nimpl fmt::Display for TimerState {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            TimerState::Disabled => f.write_str(\"timer is disabled\"),\n            TimerState::Inactive => f.write_str(\"timer is inactive\"),\n            TimerState::Active { timer } => {\n                let deadline = timer.deadline();\n                let now = Instant::now();\n\n                if deadline < now {\n                    f.write_str(\"timer is active and has reached deadline\")\n                } else {\n                    write!(\n                        f,\n                        \"timer is active and due to expire in {} milliseconds\",\n                        ((deadline - now).as_secs_f32() * 1000.0)\n                    )\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/upgrade.rs",
    "content": "use actix_codec::Framed;\nuse actix_service::{Service, ServiceFactory};\nuse futures_core::future::LocalBoxFuture;\n\nuse crate::{h1::Codec, Error, Request};\n\npub struct UpgradeHandler;\n\nimpl<T> ServiceFactory<(Request, Framed<T, Codec>)> for UpgradeHandler {\n    type Response = ();\n    type Error = Error;\n    type Config = ();\n    type Service = UpgradeHandler;\n    type InitError = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        unimplemented!()\n    }\n}\n\nimpl<T> Service<(Request, Framed<T, Codec>)> for UpgradeHandler {\n    type Response = ();\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, _: (Request, Framed<T, Codec>)) -> Self::Future {\n        unimplemented!()\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h1/utils.rs",
    "content": "use std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::{BodySize, MessageBody},\n    h1::{Codec, Message},\n    Error, Response,\n};\n\npin_project! {\n    /// Send HTTP/1 response\n    pub struct SendResponse<T, B> {\n        res: Option<Message<(Response<()>, BodySize)>>,\n\n        #[pin]\n        body: Option<B>,\n\n        #[pin]\n        framed: Option<Framed<T, Codec>>,\n    }\n}\n\nimpl<T, B> SendResponse<T, B>\nwhere\n    B: MessageBody,\n    B::Error: Into<Error>,\n{\n    pub fn new(framed: Framed<T, Codec>, response: Response<B>) -> Self {\n        let (res, body) = response.into_parts();\n\n        SendResponse {\n            res: Some((res, body.size()).into()),\n            body: Some(body),\n            framed: Some(framed),\n        }\n    }\n}\n\nimpl<T, B> Future for SendResponse<T, B>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n    B: MessageBody,\n    B::Error: Into<Error>,\n{\n    type Output = Result<Framed<T, Codec>, Error>;\n\n    // TODO: rethink if we need loops in polls\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let mut this = self.as_mut().project();\n\n        let mut body_done = this.body.is_none();\n        loop {\n            let mut body_ready = !body_done;\n\n            // send body\n            if this.res.is_none() && body_ready {\n                while body_ready\n                    && !body_done\n                    && !this\n                        .framed\n                        .as_ref()\n                        .as_pin_ref()\n                        .unwrap()\n                        .is_write_buf_full()\n                {\n                    let next = match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx) {\n                        Poll::Ready(Some(Ok(item))) => Poll::Ready(Some(item)),\n                        Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),\n                        Poll::Ready(None) => Poll::Ready(None),\n                        Poll::Pending => Poll::Pending,\n                    };\n\n                    match next {\n                        Poll::Ready(item) => {\n                            // body is done when item is None\n                            body_done = item.is_none();\n                            if body_done {\n                                this.body.set(None);\n                            }\n                            let framed = this.framed.as_mut().as_pin_mut().unwrap();\n                            framed\n                                .write(Message::Chunk(item))\n                                .map_err(|err| Error::new_send_response().with_cause(err))?;\n                        }\n                        Poll::Pending => body_ready = false,\n                    }\n                }\n            }\n\n            let framed = this.framed.as_mut().as_pin_mut().unwrap();\n\n            // flush write buffer\n            if !framed.is_write_buf_empty() {\n                match framed\n                    .flush(cx)\n                    .map_err(|err| Error::new_send_response().with_cause(err))?\n                {\n                    Poll::Ready(_) => {\n                        if body_ready {\n                            continue;\n                        } else {\n                            return Poll::Pending;\n                        }\n                    }\n                    Poll::Pending => return Poll::Pending,\n                }\n            }\n\n            // send response\n            if let Some(res) = this.res.take() {\n                framed\n                    .write(res)\n                    .map_err(|err| Error::new_send_response().with_cause(err))?;\n                continue;\n            }\n\n            if !body_done {\n                if body_ready {\n                    continue;\n                } else {\n                    return Poll::Pending;\n                }\n            } else {\n                break;\n            }\n        }\n\n        let framed = this.framed.take().unwrap();\n\n        Poll::Ready(Ok(framed))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/h2/dispatcher.rs",
    "content": "use std::{\n    cmp,\n    error::Error as StdError,\n    future::Future,\n    marker::PhantomData,\n    net,\n    pin::{pin, Pin},\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite};\nuse actix_rt::time::{sleep, Sleep};\nuse actix_service::Service;\nuse actix_utils::future::poll_fn;\nuse bytes::{Bytes, BytesMut};\nuse futures_core::ready;\nuse h2::{\n    server::{Connection, SendResponse},\n    Ping, PingPong,\n};\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::{BodySize, BoxBody, MessageBody},\n    config::ServiceConfig,\n    header::{\n        HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,\n    },\n    service::HttpFlow,\n    Extensions, Method, OnConnectData, Payload, Request, Response, ResponseHead,\n};\n\nconst CHUNK_SIZE: usize = 16_384;\n\npin_project! {\n    /// Dispatcher for HTTP/2 protocol.\n    pub struct Dispatcher<T, S, B, X, U> {\n        flow: Rc<HttpFlow<S, X, U>>,\n        connection: Connection<T, Bytes>,\n        conn_data: Option<Rc<Extensions>>,\n        config: ServiceConfig,\n        peer_addr: Option<net::SocketAddr>,\n        ping_pong: Option<H2PingPong>,\n        _phantom: PhantomData<B>\n    }\n}\n\nimpl<T, S, B, X, U> Dispatcher<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n{\n    pub(crate) fn new(\n        mut conn: Connection<T, Bytes>,\n        flow: Rc<HttpFlow<S, X, U>>,\n        config: ServiceConfig,\n        peer_addr: Option<net::SocketAddr>,\n        conn_data: OnConnectData,\n        timer: Option<Pin<Box<Sleep>>>,\n    ) -> Self {\n        let ping_pong = config.keep_alive().duration().map(|dur| H2PingPong {\n            timer: timer\n                .map(|mut timer| {\n                    // reuse timer slot if it was initialized for handshake\n                    timer.as_mut().reset((config.now() + dur).into());\n                    timer\n                })\n                .unwrap_or_else(|| Box::pin(sleep(dur))),\n            in_flight: false,\n            ping_pong: conn.ping_pong().unwrap(),\n        });\n\n        Self {\n            flow,\n            config,\n            peer_addr,\n            connection: conn,\n            conn_data: conn_data.0.map(Rc::new),\n            ping_pong,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nstruct H2PingPong {\n    /// Handle to send ping frames from the peer.\n    ping_pong: PingPong,\n\n    /// True when a ping has been sent and is waiting for a reply.\n    in_flight: bool,\n\n    /// Timeout for pong response.\n    timer: Pin<Box<Sleep>>,\n}\n\nimpl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    S::Future: 'static,\n    S::Response: Into<Response<B>>,\n\n    B: MessageBody,\n{\n    type Output = Result<(), crate::error::DispatchError>;\n\n    #[inline]\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        loop {\n            match Pin::new(&mut this.connection).poll_accept(cx)? {\n                Poll::Ready(Some((req, tx))) => {\n                    let (parts, body) = req.into_parts();\n                    let payload = crate::h2::Payload::new(body);\n                    let pl = Payload::H2 { payload };\n                    let mut req = Request::with_payload(pl);\n                    let head_req = parts.method == Method::HEAD;\n\n                    let head = req.head_mut();\n                    head.uri = parts.uri;\n                    head.method = parts.method;\n                    head.version = parts.version;\n                    head.headers = parts.headers.into();\n                    head.peer_addr = this.peer_addr;\n\n                    req.conn_data.clone_from(&this.conn_data);\n\n                    let fut = this.flow.service.call(req);\n                    let config = this.config.clone();\n\n                    // multiplex request handling with spawn task\n                    actix_rt::spawn(async move {\n                        // resolve service call and send response.\n                        let res = match fut.await {\n                            Ok(res) => handle_response(res.into(), tx, config, head_req).await,\n                            Err(err) => {\n                                let res: Response<BoxBody> = err.into();\n                                handle_response(res, tx, config, head_req).await\n                            }\n                        };\n\n                        // log error.\n                        if let Err(err) = res {\n                            match err {\n                                DispatchError::SendResponse(err) => {\n                                    tracing::trace!(\"Error sending response: {err:?}\");\n                                }\n                                DispatchError::SendData(err) => {\n                                    tracing::warn!(\"Send data error: {err:?}\");\n                                }\n                                DispatchError::ResponseBody(err) => {\n                                    tracing::error!(\"Response payload stream error: {err:?}\");\n                                }\n                            }\n                        }\n                    });\n                }\n                Poll::Ready(None) => return Poll::Ready(Ok(())),\n\n                Poll::Pending => match this.ping_pong.as_mut() {\n                    Some(ping_pong) => loop {\n                        if ping_pong.in_flight {\n                            // When there is an in-flight ping-pong, poll pong and and keep-alive\n                            // timer. On successful pong received, update keep-alive timer to\n                            // determine the next timing of ping pong.\n                            match ping_pong.ping_pong.poll_pong(cx)? {\n                                Poll::Ready(_) => {\n                                    ping_pong.in_flight = false;\n\n                                    let dead_line = this.config.keep_alive_deadline().unwrap();\n                                    ping_pong.timer.as_mut().reset(dead_line.into());\n                                }\n                                Poll::Pending => {\n                                    return ping_pong.timer.as_mut().poll(cx).map(|_| Ok(()));\n                                }\n                            }\n                        } else {\n                            // When there is no in-flight ping-pong, keep-alive timer is used to\n                            // wait for next timing of ping-pong. Therefore, at this point it serves\n                            // as an interval instead.\n                            ready!(ping_pong.timer.as_mut().poll(cx));\n\n                            ping_pong.ping_pong.send_ping(Ping::opaque())?;\n\n                            let dead_line = this.config.keep_alive_deadline().unwrap();\n                            ping_pong.timer.as_mut().reset(dead_line.into());\n\n                            ping_pong.in_flight = true;\n                        }\n                    },\n                    None => return Poll::Pending,\n                },\n            }\n        }\n    }\n}\n\nenum DispatchError {\n    SendResponse(h2::Error),\n    SendData(h2::Error),\n    ResponseBody(Box<dyn StdError>),\n}\n\nasync fn handle_response<B>(\n    res: Response<B>,\n    mut tx: SendResponse<Bytes>,\n    config: ServiceConfig,\n    head_req: bool,\n) -> Result<(), DispatchError>\nwhere\n    B: MessageBody,\n{\n    let (res, body) = res.replace_body(());\n\n    // prepare response.\n    let mut size = body.size();\n    let res = prepare_response(config, res.head(), &mut size);\n    let eof_or_head = size.is_eof() || head_req;\n\n    // send response head and return on eof.\n    let mut stream = tx\n        .send_response(res, eof_or_head)\n        .map_err(DispatchError::SendResponse)?;\n\n    if eof_or_head {\n        return Ok(());\n    }\n\n    let mut body = pin!(body);\n\n    // poll response body and send chunks to client\n    while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {\n        let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?;\n\n        'send: loop {\n            let chunk_size = cmp::min(chunk.len(), CHUNK_SIZE);\n\n            // reserve enough space and wait for stream ready.\n            stream.reserve_capacity(chunk_size);\n\n            match poll_fn(|cx| stream.poll_capacity(cx)).await {\n                // No capacity left. drop body and return.\n                None => return Ok(()),\n\n                Some(Err(err)) => return Err(DispatchError::SendData(err)),\n\n                Some(Ok(cap)) => {\n                    // split chunk to writeable size and send to client\n                    let len = chunk.len();\n                    let bytes = chunk.split_to(cmp::min(len, cap));\n\n                    stream\n                        .send_data(bytes, false)\n                        .map_err(DispatchError::SendData)?;\n\n                    // Current chuck completely sent. break send loop and poll next one.\n                    if chunk.is_empty() {\n                        break 'send;\n                    }\n                }\n            }\n        }\n    }\n\n    // response body streaming finished. send end of stream and return.\n    stream\n        .send_data(Bytes::new(), true)\n        .map_err(DispatchError::SendData)?;\n\n    Ok(())\n}\n\nfn prepare_response(\n    config: ServiceConfig,\n    head: &ResponseHead,\n    size: &mut BodySize,\n) -> http::Response<()> {\n    let mut has_date = false;\n    let mut skip_len = size != &BodySize::Stream;\n\n    let mut res = http::Response::new(());\n    *res.status_mut() = head.status;\n    *res.version_mut() = http::Version::HTTP_2;\n\n    // Content length\n    match head.status {\n        http::StatusCode::NO_CONTENT\n        | http::StatusCode::CONTINUE\n        | http::StatusCode::PROCESSING => *size = BodySize::None,\n        http::StatusCode::SWITCHING_PROTOCOLS => {\n            skip_len = true;\n            *size = BodySize::Stream;\n        }\n        _ => {}\n    }\n\n    match size {\n        BodySize::None | BodySize::Stream => {}\n\n        BodySize::Sized(0) => {\n            #[allow(clippy::declare_interior_mutable_const)]\n            const HV_ZERO: HeaderValue = HeaderValue::from_static(\"0\");\n            res.headers_mut().insert(CONTENT_LENGTH, HV_ZERO);\n        }\n\n        BodySize::Sized(len) => {\n            let mut buf = itoa::Buffer::new();\n\n            res.headers_mut().insert(\n                CONTENT_LENGTH,\n                HeaderValue::from_str(buf.format(*len)).unwrap(),\n            );\n        }\n    };\n\n    // copy headers\n    for (key, value) in head.headers.iter() {\n        match key {\n            // omit HTTP/1.x only headers according to:\n            // https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2\n            &CONNECTION | &TRANSFER_ENCODING | &UPGRADE => continue,\n\n            &CONTENT_LENGTH if skip_len => continue,\n            &DATE => has_date = true,\n\n            // omit HTTP/1.x only headers according to:\n            // https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2\n            hdr if hdr == HeaderName::from_static(\"keep-alive\")\n                || hdr == HeaderName::from_static(\"proxy-connection\") =>\n            {\n                continue\n            }\n\n            _ => {}\n        }\n\n        res.headers_mut().append(key, value.clone());\n    }\n\n    // set date header\n    if !has_date {\n        let mut bytes = BytesMut::with_capacity(29);\n        config.write_date_header_value(&mut bytes);\n        res.headers_mut().insert(\n            DATE,\n            // SAFETY: serialized date-times are known ASCII strings\n            unsafe { HeaderValue::from_maybe_shared_unchecked(bytes.freeze()) },\n        );\n    }\n\n    res\n}\n"
  },
  {
    "path": "actix-http/src/h2/mod.rs",
    "content": "//! HTTP/2 protocol.\n\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite};\nuse actix_rt::time::{sleep_until, Sleep};\nuse bytes::Bytes;\nuse futures_core::{ready, Stream};\nuse h2::{\n    server::{Builder, Connection, Handshake},\n    RecvStream,\n};\n\nuse crate::{\n    config::ServiceConfig,\n    error::{DispatchError, PayloadError},\n};\n\nmod dispatcher;\nmod service;\n\npub use self::{dispatcher::Dispatcher, service::H2Service};\n\n/// HTTP/2 peer stream.\npub struct Payload {\n    stream: RecvStream,\n}\n\nimpl Payload {\n    pub(crate) fn new(stream: RecvStream) -> Self {\n        Self { stream }\n    }\n}\n\nimpl Stream for Payload {\n    type Item = Result<Bytes, PayloadError>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.get_mut();\n\n        match ready!(Pin::new(&mut this.stream).poll_data(cx)) {\n            Some(Ok(chunk)) => {\n                let len = chunk.len();\n\n                match this.stream.flow_control().release_capacity(len) {\n                    Ok(()) => Poll::Ready(Some(Ok(chunk))),\n                    Err(err) => Poll::Ready(Some(Err(err.into()))),\n                }\n            }\n            Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),\n            None => Poll::Ready(None),\n        }\n    }\n}\n\npub(crate) fn handshake_with_timeout<T>(io: T, config: &ServiceConfig) -> HandshakeWithTimeout<T>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n{\n    let mut builder = Builder::new();\n    builder\n        .initial_window_size(config.h2_initial_window_size())\n        .initial_connection_window_size(config.h2_initial_connection_window_size());\n\n    HandshakeWithTimeout {\n        handshake: builder.handshake(io),\n        timer: config\n            .client_request_deadline()\n            .map(|deadline| Box::pin(sleep_until(deadline.into()))),\n    }\n}\n\npub(crate) struct HandshakeWithTimeout<T: AsyncRead + AsyncWrite + Unpin> {\n    handshake: Handshake<T>,\n    timer: Option<Pin<Box<Sleep>>>,\n}\n\nimpl<T> Future for HandshakeWithTimeout<T>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n{\n    type Output = Result<(Connection<T, Bytes>, Option<Pin<Box<Sleep>>>), DispatchError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        match Pin::new(&mut this.handshake).poll(cx)? {\n            // return the timer on success handshake; its slot can be re-used for h2 ping-pong\n            Poll::Ready(conn) => Poll::Ready(Ok((conn, this.timer.take()))),\n            Poll::Pending => match this.timer.as_mut() {\n                Some(timer) => {\n                    ready!(timer.as_mut().poll(cx));\n                    Poll::Ready(Err(DispatchError::SlowRequestTimeout))\n                }\n                None => Poll::Pending,\n            },\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n\n    assert_impl_all!(Payload: Unpin, Send, Sync);\n}\n"
  },
  {
    "path": "actix-http/src/h2/service.rs",
    "content": "use std::{\n    future::Future,\n    marker::PhantomData,\n    mem, net,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite};\nuse actix_rt::net::TcpStream;\nuse actix_service::{\n    fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,\n};\nuse actix_utils::future::ready;\nuse futures_core::{future::LocalBoxFuture, ready};\nuse tracing::{error, trace};\n\nuse super::{dispatcher::Dispatcher, handshake_with_timeout, HandshakeWithTimeout};\nuse crate::{\n    body::{BoxBody, MessageBody},\n    config::ServiceConfig,\n    error::DispatchError,\n    service::HttpFlow,\n    ConnectCallback, OnConnectData, Request, Response,\n};\n\n#[inline]\nfn desired_nodelay(tcp_nodelay: Option<bool>) -> Option<bool> {\n    tcp_nodelay\n}\n\n#[inline]\nfn set_nodelay(stream: &TcpStream, nodelay: bool) {\n    let _ = stream.set_nodelay(nodelay);\n}\n\n/// `ServiceFactory` implementation for HTTP/2 transport\npub struct H2Service<T, S, B> {\n    srv: S,\n    cfg: ServiceConfig,\n    on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<(T, B)>,\n}\n\nimpl<T, S, B> H2Service<T, S, B>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n\n    B: MessageBody + 'static,\n{\n    /// Create new `H2Service` instance with config.\n    pub(crate) fn with_config<F: IntoServiceFactory<S, Request>>(\n        cfg: ServiceConfig,\n        service: F,\n    ) -> Self {\n        H2Service {\n            cfg,\n            on_connect_ext: None,\n            srv: service.into_factory(),\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Set on connect callback.\n    pub(crate) fn on_connect_ext(mut self, f: Option<Rc<ConnectCallback<T>>>) -> Self {\n        self.on_connect_ext = f;\n        self\n    }\n}\n\nimpl<S, B> H2Service<TcpStream, S, B>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n\n    B: MessageBody + 'static,\n{\n    /// Create plain TCP based service\n    pub fn tcp(\n        self,\n    ) -> impl ServiceFactory<\n        TcpStream,\n        Config = (),\n        Response = (),\n        Error = DispatchError,\n        InitError = S::InitError,\n    > {\n        let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n\n        fn_factory(move || {\n            ready(Ok::<_, S::InitError>(fn_service(move |io: TcpStream| {\n                if let Some(nodelay) = tcp_nodelay {\n                    set_nodelay(&io, nodelay);\n                }\n                let peer_addr = io.peer_addr().ok();\n                ready(Ok::<_, DispatchError>((io, peer_addr)))\n            })))\n        })\n        .and_then(self)\n    }\n}\n\n#[cfg(feature = \"openssl\")]\nmod openssl {\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        openssl::{\n            reexports::{Error as SslError, SslAcceptor},\n            Acceptor, TlsStream,\n        },\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n    {\n        /// Create OpenSSL based service.\n        pub fn openssl(\n            self,\n            acceptor: SslAcceptor,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<SslError, DispatchError>,\n            InitError = S::InitError,\n        > {\n            let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n\n            Acceptor::new(acceptor)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    if let Some(nodelay) = tcp_nodelay {\n                        set_nodelay(io.get_ref(), nodelay);\n                    }\n                    let peer_addr = io.get_ref().peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_20\")]\nmod rustls_0_20 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n    {\n        /// Create Rustls v0.20 based service.\n        pub fn rustls(\n            self,\n            mut config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = S::InitError,\n        > {\n            let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n            let mut protos = vec![b\"h2\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    if let Some(nodelay) = tcp_nodelay {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_21\")]\nmod rustls_0_21 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n    {\n        /// Create Rustls v0.21 based service.\n        pub fn rustls_021(\n            self,\n            mut config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = S::InitError,\n        > {\n            let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n            let mut protos = vec![b\"h2\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    if let Some(nodelay) = tcp_nodelay {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_22\")]\nmod rustls_0_22 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n    {\n        /// Create Rustls v0.22 based service.\n        pub fn rustls_0_22(\n            self,\n            mut config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = S::InitError,\n        > {\n            let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n            let mut protos = vec![b\"h2\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    if let Some(nodelay) = tcp_nodelay {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_23\")]\nmod rustls_0_23 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B> H2Service<TlsStream<TcpStream>, S, B>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n    {\n        /// Create Rustls v0.23 based service.\n        pub fn rustls_0_23(\n            self,\n            mut config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = S::InitError,\n        > {\n            let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());\n            let mut protos = vec![b\"h2\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            Acceptor::new(config)\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    if let Some(nodelay) = tcp_nodelay {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    (io, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\nimpl<T, S, B> ServiceFactory<(T, Option<net::SocketAddr>)> for H2Service<T, S, B>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin + 'static,\n\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n\n    B: MessageBody + 'static,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Config = ();\n    type Service = H2ServiceHandler<T, S::Service, B>;\n    type InitError = S::InitError;\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let service = self.srv.new_service(());\n        let cfg = self.cfg.clone();\n        let on_connect_ext = self.on_connect_ext.clone();\n\n        Box::pin(async move {\n            let service = service.await?;\n            Ok(H2ServiceHandler::new(cfg, on_connect_ext, service))\n        })\n    }\n}\n\n/// `Service` implementation for HTTP/2 transport\npub struct H2ServiceHandler<T, S, B>\nwhere\n    S: Service<Request>,\n{\n    flow: Rc<HttpFlow<S, (), ()>>,\n    cfg: ServiceConfig,\n    on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<B>,\n}\n\nimpl<T, S, B> H2ServiceHandler<T, S, B>\nwhere\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n    B: MessageBody + 'static,\n{\n    fn new(\n        cfg: ServiceConfig,\n        on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n        service: S,\n    ) -> H2ServiceHandler<T, S, B> {\n        H2ServiceHandler {\n            flow: HttpFlow::new(service, (), None),\n            cfg,\n            on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<T, S, B> Service<(T, Option<net::SocketAddr>)> for H2ServiceHandler<T, S, B>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n    B: MessageBody + 'static,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Future = H2ServiceHandlerResponse<T, S, B>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.flow.service.poll_ready(cx).map_err(|err| {\n            let err = err.into();\n            error!(\"Service readiness error: {:?}\", err);\n            DispatchError::Service(err)\n        })\n    }\n\n    fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {\n        let on_connect_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());\n\n        H2ServiceHandlerResponse {\n            state: State::Handshake(\n                Some(Rc::clone(&self.flow)),\n                Some(self.cfg.clone()),\n                addr,\n                on_connect_data,\n                handshake_with_timeout(io, &self.cfg),\n            ),\n        }\n    }\n}\n\nenum State<T, S: Service<Request>, B: MessageBody>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n    S::Future: 'static,\n{\n    Handshake(\n        Option<Rc<HttpFlow<S, (), ()>>>,\n        Option<ServiceConfig>,\n        Option<net::SocketAddr>,\n        OnConnectData,\n        HandshakeWithTimeout<T>,\n    ),\n    Established(Dispatcher<T, S, B, (), ()>),\n}\n\npub struct H2ServiceHandlerResponse<T, S, B>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n    B: MessageBody + 'static,\n{\n    state: State<T, S, B>,\n}\n\nimpl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n    B: MessageBody,\n{\n    type Output = Result<(), DispatchError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.state {\n            State::Handshake(\n                ref mut srv,\n                ref mut config,\n                ref peer_addr,\n                ref mut conn_data,\n                ref mut handshake,\n            ) => match ready!(Pin::new(handshake).poll(cx)) {\n                Ok((conn, timer)) => {\n                    let on_connect_data = mem::take(conn_data);\n\n                    self.state = State::Established(Dispatcher::new(\n                        conn,\n                        srv.take().unwrap(),\n                        config.take().unwrap(),\n                        *peer_addr,\n                        on_connect_data,\n                        timer,\n                    ));\n\n                    self.poll(cx)\n                }\n\n                Err(err) => {\n                    trace!(\"H2 handshake error: {}\", err);\n                    Poll::Ready(Err(err))\n                }\n            },\n\n            State::Established(ref mut disp) => Pin::new(disp).poll(cx),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/as_name.rs",
    "content": "//! Sealed [`AsHeaderName`] trait and implementations.\n\nuse std::{borrow::Cow, str::FromStr as _};\n\nuse http::header::{HeaderName, InvalidHeaderName};\n\n/// Sealed trait implemented for types that can be effectively borrowed as a [`HeaderValue`].\n///\n/// [`HeaderValue`]: super::HeaderValue\npub trait AsHeaderName: Sealed {}\n\npub struct Seal;\n\npub trait Sealed {\n    fn try_as_name(&self, seal: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName>;\n}\n\nimpl Sealed for HeaderName {\n    #[inline]\n    fn try_as_name(&self, _: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {\n        Ok(Cow::Borrowed(self))\n    }\n}\nimpl AsHeaderName for HeaderName {}\n\nimpl Sealed for &HeaderName {\n    #[inline]\n    fn try_as_name(&self, _: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {\n        Ok(Cow::Borrowed(*self))\n    }\n}\nimpl AsHeaderName for &HeaderName {}\n\nimpl Sealed for &str {\n    #[inline]\n    fn try_as_name(&self, _: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {\n        HeaderName::from_str(self).map(Cow::Owned)\n    }\n}\nimpl AsHeaderName for &str {}\n\nimpl Sealed for String {\n    #[inline]\n    fn try_as_name(&self, _: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {\n        HeaderName::from_str(self).map(Cow::Owned)\n    }\n}\nimpl AsHeaderName for String {}\n\nimpl Sealed for &String {\n    #[inline]\n    fn try_as_name(&self, _: Seal) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {\n        HeaderName::from_str(self).map(Cow::Owned)\n    }\n}\nimpl AsHeaderName for &String {}\n"
  },
  {
    "path": "actix-http/src/header/common.rs",
    "content": "//! Common header names not defined in [`http`].\n//!\n//! Any headers added to this file will need to be re-exported from the list at `crate::headers`.\n\nuse http::header::HeaderName;\n\n/// Response header field that indicates how caches have handled that response and its corresponding\n/// request.\n///\n/// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) for full semantics.\n// TODO(breaking): replace with http's version\npub const CACHE_STATUS: HeaderName = HeaderName::from_static(\"cache-status\");\n\n/// Response header field that allows origin servers to control the behavior of CDN caches\n/// interposed between them and clients separately from other caches that might handle the response.\n///\n/// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213) for full semantics.\n// TODO(breaking): replace with http's version\npub const CDN_CACHE_CONTROL: HeaderName = HeaderName::from_static(\"cdn-cache-control\");\n\n/// Response header field that sends a signal to the user agent that it ought to remove all data of\n/// a certain set of types.\n///\n/// See the [W3C Clear-Site-Data spec] for full semantics.\n///\n/// [W3C Clear-Site-Data spec]: https://www.w3.org/TR/clear-site-data/#header\npub const CLEAR_SITE_DATA: HeaderName = HeaderName::from_static(\"clear-site-data\");\n\n/// Response header that prevents a document from loading any cross-origin resources that don't\n/// explicitly grant the document permission (using [CORP] or [CORS]).\n///\n/// [CORP]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)\n/// [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\npub const CROSS_ORIGIN_EMBEDDER_POLICY: HeaderName =\n    HeaderName::from_static(\"cross-origin-embedder-policy\");\n\n/// Response header that allows you to ensure a top-level document does not share a browsing context\n/// group with cross-origin documents.\npub const CROSS_ORIGIN_OPENER_POLICY: HeaderName =\n    HeaderName::from_static(\"cross-origin-opener-policy\");\n\n/// Response header that conveys a desire that the browser blocks no-cors cross-origin/cross-site\n/// requests to the given resource.\npub const CROSS_ORIGIN_RESOURCE_POLICY: HeaderName =\n    HeaderName::from_static(\"cross-origin-resource-policy\");\n\n/// Response header that provides a mechanism to allow and deny the use of browser features in a\n/// document or within any `<iframe>` elements in the document.\npub const PERMISSIONS_POLICY: HeaderName = HeaderName::from_static(\"permissions-policy\");\n\n/// Request header (de-facto standard) for identifying the originating IP address of a client\n/// connecting to a web server through a proxy server.\npub const X_FORWARDED_FOR: HeaderName = HeaderName::from_static(\"x-forwarded-for\");\n\n/// Request header (de-facto standard) for identifying the original host requested by the client in\n/// the `Host` HTTP request header.\npub const X_FORWARDED_HOST: HeaderName = HeaderName::from_static(\"x-forwarded-host\");\n\n/// Request header (de-facto standard) for identifying the protocol that a client used to connect to\n/// your proxy or load balancer.\npub const X_FORWARDED_PROTO: HeaderName = HeaderName::from_static(\"x-forwarded-proto\");\n"
  },
  {
    "path": "actix-http/src/header/into_pair.rs",
    "content": "//! [`TryIntoHeaderPair`] trait and implementations.\n\nuse super::{\n    Header, HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue, TryIntoHeaderValue,\n};\nuse crate::error::HttpError;\n\n/// An interface for types that can be converted into a [`HeaderName`] + [`HeaderValue`] pair for\n/// insertion into a [`HeaderMap`].\n///\n/// [`HeaderMap`]: super::HeaderMap\npub trait TryIntoHeaderPair: Sized {\n    type Error: Into<HttpError>;\n\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error>;\n}\n\n#[derive(Debug)]\npub enum InvalidHeaderPart {\n    Name(InvalidHeaderName),\n    Value(InvalidHeaderValue),\n}\n\nimpl From<InvalidHeaderPart> for HttpError {\n    fn from(part_err: InvalidHeaderPart) -> Self {\n        match part_err {\n            InvalidHeaderPart::Name(err) => err.into(),\n            InvalidHeaderPart::Value(err) => err.into(),\n        }\n    }\n}\n\nimpl<V> TryIntoHeaderPair for (HeaderName, V)\nwhere\n    V: TryIntoHeaderValue,\n    V::Error: Into<InvalidHeaderValue>,\n{\n    type Error = InvalidHeaderPart;\n\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        let (name, value) = self;\n        let value = value\n            .try_into_value()\n            .map_err(|err| InvalidHeaderPart::Value(err.into()))?;\n        Ok((name, value))\n    }\n}\n\nimpl<V> TryIntoHeaderPair for (&HeaderName, V)\nwhere\n    V: TryIntoHeaderValue,\n    V::Error: Into<InvalidHeaderValue>,\n{\n    type Error = InvalidHeaderPart;\n\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        let (name, value) = self;\n        let value = value\n            .try_into_value()\n            .map_err(|err| InvalidHeaderPart::Value(err.into()))?;\n        Ok((name.clone(), value))\n    }\n}\n\nimpl<V> TryIntoHeaderPair for (&[u8], V)\nwhere\n    V: TryIntoHeaderValue,\n    V::Error: Into<InvalidHeaderValue>,\n{\n    type Error = InvalidHeaderPart;\n\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        let (name, value) = self;\n        let name = HeaderName::try_from(name).map_err(InvalidHeaderPart::Name)?;\n        let value = value\n            .try_into_value()\n            .map_err(|err| InvalidHeaderPart::Value(err.into()))?;\n        Ok((name, value))\n    }\n}\n\nimpl<V> TryIntoHeaderPair for (&str, V)\nwhere\n    V: TryIntoHeaderValue,\n    V::Error: Into<InvalidHeaderValue>,\n{\n    type Error = InvalidHeaderPart;\n\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        let (name, value) = self;\n        let name = HeaderName::try_from(name).map_err(InvalidHeaderPart::Name)?;\n        let value = value\n            .try_into_value()\n            .map_err(|err| InvalidHeaderPart::Value(err.into()))?;\n        Ok((name, value))\n    }\n}\n\nimpl<V> TryIntoHeaderPair for (String, V)\nwhere\n    V: TryIntoHeaderValue,\n    V::Error: Into<InvalidHeaderValue>,\n{\n    type Error = InvalidHeaderPart;\n\n    #[inline]\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        let (name, value) = self;\n        (name.as_str(), value).try_into_pair()\n    }\n}\n\nimpl<T: Header> TryIntoHeaderPair for T {\n    type Error = <T as TryIntoHeaderValue>::Error;\n\n    #[inline]\n    fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {\n        Ok((T::name(), self.try_into_value()?))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/into_value.rs",
    "content": "//! [`TryIntoHeaderValue`] trait and implementations.\n\nuse bytes::Bytes;\nuse http::{header::InvalidHeaderValue, Error as HttpError, HeaderValue};\nuse mime::Mime;\n\n/// An interface for types that can be converted into a [`HeaderValue`].\npub trait TryIntoHeaderValue: Sized {\n    /// The type returned in the event of a conversion error.\n    type Error: Into<HttpError>;\n\n    /// Try to convert value to a HeaderValue.\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error>;\n}\n\nimpl TryIntoHeaderValue for HeaderValue {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        Ok(self)\n    }\n}\n\nimpl TryIntoHeaderValue for &HeaderValue {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        Ok(self.clone())\n    }\n}\n\nimpl TryIntoHeaderValue for &str {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        self.parse()\n    }\n}\n\nimpl TryIntoHeaderValue for &[u8] {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::from_bytes(self)\n    }\n}\n\nimpl TryIntoHeaderValue for Bytes {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::from_maybe_shared(self)\n    }\n}\n\nimpl TryIntoHeaderValue for Vec<u8> {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self)\n    }\n}\n\nimpl TryIntoHeaderValue for String {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self)\n    }\n}\n\nimpl TryIntoHeaderValue for usize {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self.to_string())\n    }\n}\n\nimpl TryIntoHeaderValue for i64 {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self.to_string())\n    }\n}\n\nimpl TryIntoHeaderValue for u64 {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self.to_string())\n    }\n}\n\nimpl TryIntoHeaderValue for i32 {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self.to_string())\n    }\n}\n\nimpl TryIntoHeaderValue for u32 {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::try_from(self.to_string())\n    }\n}\n\nimpl TryIntoHeaderValue for Mime {\n    type Error = InvalidHeaderValue;\n\n    #[inline]\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        HeaderValue::from_str(self.as_ref())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/map.rs",
    "content": "//! A multi-value [`HeaderMap`] and its iterators.\n\nuse std::{borrow::Cow, collections::hash_map, iter, ops};\n\nuse foldhash::{HashMap as FoldHashMap, HashMapExt as _};\nuse http::header::{HeaderName, HeaderValue};\nuse smallvec::{smallvec, SmallVec};\n\nuse super::AsHeaderName;\n\n/// A multi-map of HTTP headers.\n///\n/// `HeaderMap` is a \"multi-map\" of [`HeaderName`] to one or more [`HeaderValue`]s.\n///\n/// # Examples\n///\n/// ```\n/// # use actix_http::header::{self, HeaderMap, HeaderValue};\n///\n/// let mut map = HeaderMap::new();\n///\n/// map.insert(header::CONTENT_TYPE, HeaderValue::from_static(\"text/plain\"));\n/// map.insert(header::ORIGIN, HeaderValue::from_static(\"example.com\"));\n///\n/// assert!(map.contains_key(header::CONTENT_TYPE));\n/// assert!(map.contains_key(header::ORIGIN));\n///\n/// let mut removed = map.remove(header::ORIGIN);\n/// assert_eq!(removed.next().unwrap(), \"example.com\");\n///\n/// assert!(!map.contains_key(header::ORIGIN));\n/// ```\n///\n/// Construct a header map using the [`FromIterator`] implementation. Note that it uses the append\n/// strategy, so duplicate header names are preserved.\n///\n/// ```\n/// use actix_http::header::{self, HeaderMap, HeaderValue};\n///\n/// let headers = HeaderMap::from_iter([\n///     (header::CONTENT_TYPE, HeaderValue::from_static(\"text/plain\")),\n///     (header::COOKIE, HeaderValue::from_static(\"foo=1\")),\n///     (header::COOKIE, HeaderValue::from_static(\"bar=1\")),\n/// ]);\n///\n/// assert_eq!(headers.len(), 3);\n/// ```\n#[derive(Debug, Clone, Default)]\npub struct HeaderMap {\n    pub(crate) inner: FoldHashMap<HeaderName, Value>,\n}\n\n/// A bespoke non-empty list for HeaderMap values.\n#[derive(Debug, Clone)]\npub(crate) struct Value {\n    inner: SmallVec<[HeaderValue; 4]>,\n}\n\nimpl Value {\n    fn one(val: HeaderValue) -> Self {\n        Self {\n            inner: smallvec![val],\n        }\n    }\n\n    fn first(&self) -> &HeaderValue {\n        &self.inner[0]\n    }\n\n    fn first_mut(&mut self) -> &mut HeaderValue {\n        &mut self.inner[0]\n    }\n\n    fn append(&mut self, new_val: HeaderValue) {\n        self.inner.push(new_val)\n    }\n}\n\nimpl ops::Deref for Value {\n    type Target = SmallVec<[HeaderValue; 4]>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl HeaderMap {\n    /// Create an empty `HeaderMap`.\n    ///\n    /// The map will be created without any capacity; this function will not allocate.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::HeaderMap;\n    /// let map = HeaderMap::new();\n    ///\n    /// assert!(map.is_empty());\n    /// assert_eq!(0, map.capacity());\n    /// ```\n    pub fn new() -> Self {\n        HeaderMap::default()\n    }\n\n    /// Create an empty `HeaderMap` with the specified capacity.\n    ///\n    /// The map will be able to hold at least `capacity` elements without needing to reallocate.\n    /// If `capacity` is 0, the map will be created without allocating.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::HeaderMap;\n    /// let map = HeaderMap::with_capacity(16);\n    ///\n    /// assert!(map.is_empty());\n    /// assert!(map.capacity() >= 16);\n    /// ```\n    pub fn with_capacity(capacity: usize) -> Self {\n        HeaderMap {\n            inner: FoldHashMap::with_capacity(capacity),\n        }\n    }\n\n    /// Create new `HeaderMap` from a `http::HeaderMap`-like drain.\n    pub(crate) fn from_drain<I>(mut drain: I) -> Self\n    where\n        I: Iterator<Item = (Option<HeaderName>, HeaderValue)>,\n    {\n        let (first_name, first_value) = match drain.next() {\n            None => return HeaderMap::new(),\n            Some((name, val)) => {\n                let name = name.expect(\"drained first item had no name\");\n                (name, val)\n            }\n        };\n\n        let (lb, ub) = drain.size_hint();\n        let capacity = ub.unwrap_or(lb);\n\n        let mut map = HeaderMap::with_capacity(capacity);\n        map.append(first_name.clone(), first_value);\n\n        let (map, _) = drain.fold((map, first_name), |(mut map, prev_name), (name, value)| {\n            let name = name.unwrap_or(prev_name);\n            map.append(name.clone(), value);\n            (map, name)\n        });\n\n        map\n    }\n\n    /// Returns the number of values stored in the map.\n    ///\n    /// See also: [`len_keys`](Self::len_keys).\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    /// assert_eq!(map.len(), 0);\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// assert_eq!(map.len(), 2);\n    ///\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    /// assert_eq!(map.len(), 3);\n    /// ```\n    pub fn len(&self) -> usize {\n        self.inner.values().map(|vals| vals.len()).sum()\n    }\n\n    /// Returns the number of _keys_ stored in the map.\n    ///\n    /// The number of values stored will be at least this number. See also: [`Self::len`].\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    /// assert_eq!(map.len_keys(), 0);\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// assert_eq!(map.len_keys(), 2);\n    ///\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    /// assert_eq!(map.len_keys(), 2);\n    /// ```\n    pub fn len_keys(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Returns true if the map contains no elements.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    /// assert!(map.is_empty());\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// assert!(!map.is_empty());\n    /// ```\n    pub fn is_empty(&self) -> bool {\n        self.inner.len() == 0\n    }\n\n    /// Clears the map, removing all name-value pairs.\n    ///\n    /// Keeps the allocated memory for reuse.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// assert_eq!(map.len(), 2);\n    ///\n    /// map.clear();\n    /// assert!(map.is_empty());\n    /// ```\n    pub fn clear(&mut self) {\n        self.inner.clear();\n    }\n\n    fn get_value(&self, key: impl AsHeaderName) -> Option<&Value> {\n        match key.try_as_name(super::as_name::Seal).ok()? {\n            Cow::Borrowed(name) => self.inner.get(name),\n            Cow::Owned(name) => self.inner.get(&name),\n        }\n    }\n\n    /// Returns a reference to the _first_ value associated with a header name.\n    ///\n    /// Returns `None` if there is no value associated with the key.\n    ///\n    /// Even when multiple values are associated with the key, the \"first\" one is returned but is\n    /// not guaranteed to be chosen with any particular order; though, the returned item will be\n    /// consistent for each call to `get` if the map has not changed.\n    ///\n    /// See also: [`get_all`](Self::get_all).\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    ///\n    /// let cookie = map.get(header::SET_COOKIE).unwrap();\n    /// assert_eq!(cookie, \"one=1\");\n    ///\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    /// assert_eq!(map.get(header::SET_COOKIE).unwrap(), \"one=1\");\n    ///\n    /// assert_eq!(map.get(header::SET_COOKIE), map.get(\"set-cookie\"));\n    /// assert_eq!(map.get(header::SET_COOKIE), map.get(\"Set-Cookie\"));\n    ///\n    /// assert!(map.get(header::HOST).is_none());\n    /// assert!(map.get(\"INVALID HEADER NAME\").is_none());\n    /// ```\n    pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {\n        self.get_value(key).map(Value::first)\n    }\n\n    /// Returns a mutable reference to the _first_ value associated a header name.\n    ///\n    /// Returns `None` if there is no value associated with the key.\n    ///\n    /// Even when multiple values are associated with the key, the \"first\" one is returned but is\n    /// not guaranteed to be chosen with any particular order; though, the returned item will be\n    /// consistent for each call to `get_mut` if the map has not changed.\n    ///\n    /// See also: [`get_all`](Self::get_all).\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    ///\n    /// let mut cookie = map.get_mut(header::SET_COOKIE).unwrap();\n    /// assert_eq!(cookie, \"one=1\");\n    ///\n    /// *cookie = HeaderValue::from_static(\"three=3\");\n    /// assert_eq!(map.get(header::SET_COOKIE).unwrap(), \"three=3\");\n    ///\n    /// assert!(map.get(header::HOST).is_none());\n    /// assert!(map.get(\"INVALID HEADER NAME\").is_none());\n    /// ```\n    pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {\n        match key.try_as_name(super::as_name::Seal).ok()? {\n            Cow::Borrowed(name) => self.inner.get_mut(name).map(Value::first_mut),\n            Cow::Owned(name) => self.inner.get_mut(&name).map(Value::first_mut),\n        }\n    }\n\n    /// Returns an iterator over all values associated with a header name.\n    ///\n    /// The returned iterator does not incur any allocations and will yield no items if there are no\n    /// values associated with the key. Iteration order is guaranteed to be the same as\n    /// insertion order.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let mut none_iter = map.get_all(header::ORIGIN);\n    /// assert!(none_iter.next().is_none());\n    ///\n    /// map.insert(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    ///\n    /// let mut set_cookies_iter = map.get_all(header::SET_COOKIE);\n    /// assert_eq!(set_cookies_iter.next().unwrap(), \"one=1\");\n    /// assert_eq!(set_cookies_iter.next().unwrap(), \"two=2\");\n    /// assert!(set_cookies_iter.next().is_none());\n    /// ```\n    pub fn get_all(&self, key: impl AsHeaderName) -> std::slice::Iter<'_, HeaderValue> {\n        match self.get_value(key) {\n            Some(value) => value.iter(),\n            None => [].iter(),\n        }\n    }\n\n    // TODO: get_all_mut ?\n\n    /// Returns `true` if the map contains a value for the specified key.\n    ///\n    /// Invalid header names will simply return false.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    /// assert!(!map.contains_key(header::ACCEPT));\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// assert!(map.contains_key(header::ACCEPT));\n    /// ```\n    pub fn contains_key(&self, key: impl AsHeaderName) -> bool {\n        match key.try_as_name(super::as_name::Seal) {\n            Ok(Cow::Borrowed(name)) => self.inner.contains_key(name),\n            Ok(Cow::Owned(name)) => self.inner.contains_key(&name),\n            Err(_) => false,\n        }\n    }\n\n    /// Inserts (overrides) a name-value pair in the map.\n    ///\n    /// If the map already contained this key, the new value is associated with the key and all\n    /// previous values are removed and returned as a `Removed` iterator. The key is not updated;\n    /// this matters for types that can be `==` without being identical.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// assert!(map.contains_key(header::ACCEPT));\n    /// assert_eq!(map.len(), 1);\n    ///\n    /// let mut removed = map.insert(header::ACCEPT, HeaderValue::from_static(\"text/csv\"));\n    /// assert_eq!(removed.next().unwrap(), \"text/plain\");\n    /// assert!(removed.next().is_none());\n    ///\n    /// assert_eq!(map.len(), 1);\n    /// ```\n    ///\n    /// A convenience method is provided on the returned iterator to check if the insertion replaced\n    /// any values.\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let removed = map.insert(header::ACCEPT, HeaderValue::from_static(\"text/plain\"));\n    /// assert!(removed.is_empty());\n    ///\n    /// let removed = map.insert(header::ACCEPT, HeaderValue::from_static(\"text/html\"));\n    /// assert!(!removed.is_empty());\n    /// ```\n    pub fn insert(&mut self, name: HeaderName, val: HeaderValue) -> Removed {\n        let value = self.inner.insert(name, Value::one(val));\n        Removed::new(value)\n    }\n\n    /// Appends a name-value pair to the map.\n    ///\n    /// If the map already contained this key, the new value is added to the list of values\n    /// currently associated with the key. The key is not updated; this matters for types that can\n    /// be `==` without being identical.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.append(header::HOST, HeaderValue::from_static(\"example.com\"));\n    /// assert_eq!(map.len(), 1);\n    ///\n    /// map.append(header::ACCEPT, HeaderValue::from_static(\"text/csv\"));\n    /// assert_eq!(map.len(), 2);\n    ///\n    /// map.append(header::ACCEPT, HeaderValue::from_static(\"text/html\"));\n    /// assert_eq!(map.len(), 3);\n    /// ```\n    pub fn append(&mut self, key: HeaderName, value: HeaderValue) {\n        match self.inner.entry(key) {\n            hash_map::Entry::Occupied(mut entry) => {\n                entry.get_mut().append(value);\n            }\n            hash_map::Entry::Vacant(entry) => {\n                entry.insert(Value::one(value));\n            }\n        };\n    }\n\n    /// Removes all headers for a particular header name from the map.\n    ///\n    /// Providing an invalid header names (as a string argument) will have no effect and return\n    /// without error.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=2\"));\n    ///\n    /// assert_eq!(map.len(), 2);\n    ///\n    /// let mut removed = map.remove(header::SET_COOKIE);\n    /// assert_eq!(removed.next().unwrap(), \"one=1\");\n    /// assert_eq!(removed.next().unwrap(), \"one=2\");\n    /// assert!(removed.next().is_none());\n    ///\n    /// assert!(map.is_empty());\n    /// ```\n    ///\n    /// A convenience method is provided on the returned iterator to check if the `remove` call\n    /// actually removed any values.\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let removed = map.remove(\"accept\");\n    /// assert!(removed.is_empty());\n    ///\n    /// map.insert(header::ACCEPT, HeaderValue::from_static(\"text/html\"));\n    /// let removed = map.remove(\"accept\");\n    /// assert!(!removed.is_empty());\n    /// ```\n    pub fn remove(&mut self, key: impl AsHeaderName) -> Removed {\n        let value = match key.try_as_name(super::as_name::Seal) {\n            Ok(Cow::Borrowed(name)) => self.inner.remove(name),\n            Ok(Cow::Owned(name)) => self.inner.remove(&name),\n            Err(_) => None,\n        };\n\n        Removed::new(value)\n    }\n\n    /// Returns the number of single-value headers the map can hold without needing to reallocate.\n    ///\n    /// Since this is a multi-value map, the actual capacity is much larger when considering\n    /// each header name can be associated with an arbitrary number of values. The effect is that\n    /// the size of `len` may be greater than `capacity` since it counts all the values.\n    /// Conversely, [`len_keys`](Self::len_keys) will never be larger than capacity.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::HeaderMap;\n    /// let map = HeaderMap::with_capacity(16);\n    ///\n    /// assert!(map.is_empty());\n    /// assert!(map.capacity() >= 16);\n    /// ```\n    pub fn capacity(&self) -> usize {\n        self.inner.capacity()\n    }\n\n    /// Reserves capacity for at least `additional` more headers to be inserted in the map.\n    ///\n    /// The header map may reserve more space to avoid frequent reallocations. Additional capacity\n    /// only considers single-value headers.\n    ///\n    /// # Panics\n    /// Panics if the new allocation size overflows usize.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::HeaderMap;\n    /// let mut map = HeaderMap::with_capacity(2);\n    /// assert!(map.capacity() >= 2);\n    ///\n    /// map.reserve(100);\n    /// assert!(map.capacity() >= 102);\n    ///\n    /// assert!(map.is_empty());\n    /// ```\n    pub fn reserve(&mut self, additional: usize) {\n        self.inner.reserve(additional)\n    }\n\n    /// An iterator over all name-value pairs.\n    ///\n    /// Names will be yielded for each associated value. So, if a key has 3 associated values, it\n    /// will be yielded 3 times. The iteration order should be considered arbitrary.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let mut iter = map.iter();\n    /// assert!(iter.next().is_none());\n    ///\n    /// map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    ///\n    /// let mut iter = map.iter();\n    /// assert!(iter.next().is_some());\n    /// assert!(iter.next().is_some());\n    /// assert!(iter.next().is_some());\n    /// assert!(iter.next().is_none());\n    ///\n    /// let pairs = map.iter().collect::<Vec<_>>();\n    /// assert!(pairs.contains(&(&header::HOST, &HeaderValue::from_static(\"duck.com\"))));\n    /// assert!(pairs.contains(&(&header::SET_COOKIE, &HeaderValue::from_static(\"one=1\"))));\n    /// assert!(pairs.contains(&(&header::SET_COOKIE, &HeaderValue::from_static(\"two=2\"))));\n    /// ```\n    pub fn iter(&self) -> Iter<'_> {\n        Iter::new(self.inner.iter(), self.len())\n    }\n\n    /// An iterator over all contained header names.\n    ///\n    /// Each name will only be yielded once even if it has multiple associated values. The iteration\n    /// order should be considered arbitrary.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let mut iter = map.keys();\n    /// assert!(iter.next().is_none());\n    ///\n    /// map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    ///\n    /// let keys = map.keys().cloned().collect::<Vec<_>>();\n    /// assert_eq!(keys.len(), 2);\n    /// assert!(keys.contains(&header::HOST));\n    /// assert!(keys.contains(&header::SET_COOKIE));\n    /// ```\n    pub fn keys(&self) -> Keys<'_> {\n        Keys(self.inner.keys())\n    }\n\n    /// Retains only the headers specified by the predicate.\n    ///\n    /// In other words, removes all headers `(name, val)` for which `retain_fn(&name, &mut val)`\n    /// returns false.\n    ///\n    /// The order in which headers are visited should be considered arbitrary.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    ///\n    /// map.retain(|name, val| val.as_bytes().starts_with(b\"one\"));\n    ///\n    /// assert_eq!(map.len(), 1);\n    /// assert!(map.contains_key(&header::SET_COOKIE));\n    /// ```\n    pub fn retain<F>(&mut self, mut retain_fn: F)\n    where\n        F: FnMut(&HeaderName, &mut HeaderValue) -> bool,\n    {\n        self.inner.retain(|name, vals| {\n            vals.inner.retain(|val| retain_fn(name, val));\n\n            // invariant: make sure newly empty value lists are removed\n            !vals.is_empty()\n        })\n    }\n\n    /// Clears the map, returning all name-value sets as an iterator.\n    ///\n    /// Header names will only be yielded for the first value in each set. All items that are\n    /// yielded without a name and after an item with a name are associated with that same name.\n    /// The first item will always contain a name.\n    ///\n    /// Keeps the allocated memory for reuse.\n    /// # Examples\n    /// ```\n    /// # use actix_http::header::{self, HeaderMap, HeaderValue};\n    /// let mut map = HeaderMap::new();\n    ///\n    /// let mut iter = map.drain();\n    /// assert!(iter.next().is_none());\n    /// drop(iter);\n    ///\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"one=1\"));\n    /// map.append(header::SET_COOKIE, HeaderValue::from_static(\"two=2\"));\n    ///\n    /// let mut iter = map.drain();\n    /// assert_eq!(iter.next().unwrap(), (Some(header::SET_COOKIE), HeaderValue::from_static(\"one=1\")));\n    /// assert_eq!(iter.next().unwrap(), (None, HeaderValue::from_static(\"two=2\")));\n    /// drop(iter);\n    ///\n    /// assert!(map.is_empty());\n    /// ```\n    pub fn drain(&mut self) -> Drain<'_> {\n        let len = self.len();\n        Drain::new(self.inner.drain(), len)\n    }\n}\n\n/// Note that this implementation will clone a [HeaderName] for each value. Consider using\n/// [`drain`](Self::drain) to control header name cloning.\nimpl IntoIterator for HeaderMap {\n    type Item = (HeaderName, HeaderValue);\n    type IntoIter = IntoIter;\n\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        let len = self.len();\n        IntoIter::new(self.inner.into_iter(), len)\n    }\n}\n\nimpl<'a> IntoIterator for &'a HeaderMap {\n    type Item = (&'a HeaderName, &'a HeaderValue);\n    type IntoIter = Iter<'a>;\n\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        Iter::new(self.inner.iter(), self.len())\n    }\n}\n\nimpl FromIterator<(HeaderName, HeaderValue)> for HeaderMap {\n    fn from_iter<T: IntoIterator<Item = (HeaderName, HeaderValue)>>(iter: T) -> Self {\n        iter.into_iter()\n            .fold(Self::new(), |mut map, (name, value)| {\n                map.append(name, value);\n                map\n            })\n    }\n}\n\n/// Convert a `http::HeaderMap` to our `HeaderMap`.\nimpl From<http::HeaderMap> for HeaderMap {\n    fn from(mut map: http::HeaderMap) -> Self {\n        Self::from_drain(map.drain())\n    }\n}\n\n/// Convert our `HeaderMap` to a `http::HeaderMap`.\nimpl From<HeaderMap> for http::HeaderMap {\n    fn from(map: HeaderMap) -> Self {\n        Self::from_iter(map)\n    }\n}\n\n/// Convert our `&HeaderMap` to a `http::HeaderMap`.\nimpl From<&HeaderMap> for http::HeaderMap {\n    fn from(map: &HeaderMap) -> Self {\n        map.to_owned().into()\n    }\n}\n\n/// Iterator over removed, owned values with the same associated name.\n///\n/// Returned from methods that remove or replace items. See [`HeaderMap::insert`]\n/// and [`HeaderMap::remove`].\n#[derive(Debug)]\npub struct Removed {\n    inner: Option<smallvec::IntoIter<[HeaderValue; 4]>>,\n}\n\nimpl Removed {\n    fn new(value: Option<Value>) -> Self {\n        let inner = value.map(|value| value.inner.into_iter());\n        Self { inner }\n    }\n\n    /// Returns true if iterator contains no elements, without consuming it.\n    ///\n    /// If called immediately after [`HeaderMap::insert`] or [`HeaderMap::remove`], it will indicate\n    /// whether any items were actually replaced or removed, respectively.\n    pub fn is_empty(&self) -> bool {\n        match self.inner {\n            // size hint lower bound of smallvec is the correct length\n            Some(ref iter) => iter.size_hint().0 == 0,\n            None => true,\n        }\n    }\n}\n\nimpl Iterator for Removed {\n    type Item = HeaderValue;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.as_mut()?.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match self.inner {\n            Some(ref iter) => iter.size_hint(),\n            None => (0, None),\n        }\n    }\n}\n\nimpl ExactSizeIterator for Removed {}\n\nimpl iter::FusedIterator for Removed {}\n\n/// Iterator over all names in the map.\n#[derive(Debug)]\npub struct Keys<'a>(hash_map::Keys<'a, HeaderName, Value>);\n\nimpl<'a> Iterator for Keys<'a> {\n    type Item = &'a HeaderName;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.0.size_hint()\n    }\n}\n\nimpl ExactSizeIterator for Keys<'_> {}\n\nimpl iter::FusedIterator for Keys<'_> {}\n\n/// Iterator over borrowed name-value pairs.\n#[derive(Debug)]\npub struct Iter<'a> {\n    inner: hash_map::Iter<'a, HeaderName, Value>,\n    multi_inner: Option<(&'a HeaderName, &'a SmallVec<[HeaderValue; 4]>)>,\n    multi_idx: usize,\n    remaining: usize,\n}\n\nimpl<'a> Iter<'a> {\n    fn new(iter: hash_map::Iter<'a, HeaderName, Value>, remaining: usize) -> Self {\n        Self {\n            inner: iter,\n            multi_idx: 0,\n            multi_inner: None,\n            remaining,\n        }\n    }\n}\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = (&'a HeaderName, &'a HeaderValue);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // handle in-progress multi value lists first\n        if let Some((name, ref mut vals)) = self.multi_inner {\n            match vals.get(self.multi_idx) {\n                Some(val) => {\n                    self.multi_idx += 1;\n                    self.remaining -= 1;\n                    return Some((name, val));\n                }\n                None => {\n                    // no more items in value list; reset state\n                    self.multi_idx = 0;\n                    self.multi_inner = None;\n                }\n            }\n        }\n\n        let (name, value) = self.inner.next()?;\n\n        // set up new inner iter and recurse into it\n        self.multi_inner = Some((name, &value.inner));\n        self.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (self.remaining, Some(self.remaining))\n    }\n}\n\nimpl ExactSizeIterator for Iter<'_> {}\n\nimpl iter::FusedIterator for Iter<'_> {}\n\n/// Iterator over drained name-value pairs.\n///\n/// Iterator items are `(Option<HeaderName>, HeaderValue)` to avoid cloning.\n#[derive(Debug)]\npub struct Drain<'a> {\n    inner: hash_map::Drain<'a, HeaderName, Value>,\n    multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>,\n    multi_idx: usize,\n    remaining: usize,\n}\n\nimpl<'a> Drain<'a> {\n    fn new(iter: hash_map::Drain<'a, HeaderName, Value>, remaining: usize) -> Self {\n        Self {\n            inner: iter,\n            multi_inner: None,\n            multi_idx: 0,\n            remaining,\n        }\n    }\n}\n\nimpl Iterator for Drain<'_> {\n    type Item = (Option<HeaderName>, HeaderValue);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // handle in-progress multi value iterators first\n        if let Some((ref mut name, ref mut vals)) = self.multi_inner {\n            if !vals.is_empty() {\n                // OPTIMIZE: array removals\n                self.remaining -= 1;\n                return Some((name.take(), vals.remove(0)));\n            } else {\n                // no more items in value iterator; reset state\n                self.multi_inner = None;\n                self.multi_idx = 0;\n            }\n        }\n\n        let (name, value) = self.inner.next()?;\n\n        // set up new inner iter and recurse into it\n        self.multi_inner = Some((Some(name), value.inner));\n        self.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (self.remaining, Some(self.remaining))\n    }\n}\n\nimpl ExactSizeIterator for Drain<'_> {}\n\nimpl iter::FusedIterator for Drain<'_> {}\n\n/// Iterator over owned name-value pairs.\n///\n/// Implementation necessarily clones header names for each value.\n#[derive(Debug)]\npub struct IntoIter {\n    inner: hash_map::IntoIter<HeaderName, Value>,\n    multi_inner: Option<(HeaderName, smallvec::IntoIter<[HeaderValue; 4]>)>,\n    remaining: usize,\n}\n\nimpl IntoIter {\n    fn new(inner: hash_map::IntoIter<HeaderName, Value>, remaining: usize) -> Self {\n        Self {\n            inner,\n            multi_inner: None,\n            remaining,\n        }\n    }\n}\n\nimpl Iterator for IntoIter {\n    type Item = (HeaderName, HeaderValue);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // handle in-progress multi value iterators first\n        if let Some((ref name, ref mut vals)) = self.multi_inner {\n            match vals.next() {\n                Some(val) => {\n                    self.remaining -= 1;\n                    return Some((name.clone(), val));\n                }\n                None => {\n                    // no more items in value iterator; reset state\n                    self.multi_inner = None;\n                }\n            }\n        }\n\n        let (name, value) = self.inner.next()?;\n\n        // set up new inner iter and recurse into it\n        self.multi_inner = Some((name, value.inner.into_iter()));\n        self.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (self.remaining, Some(self.remaining))\n    }\n}\n\nimpl ExactSizeIterator for IntoIter {}\n\nimpl iter::FusedIterator for IntoIter {}\n\n#[cfg(test)]\nmod tests {\n    use std::iter::FusedIterator;\n\n    use http::header;\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n\n    assert_impl_all!(HeaderMap: IntoIterator);\n    assert_impl_all!(Keys<'_>: Iterator, ExactSizeIterator, FusedIterator);\n    assert_impl_all!(std::slice::Iter<'_, HeaderValue>: Iterator, ExactSizeIterator, FusedIterator);\n    assert_impl_all!(Removed: Iterator, ExactSizeIterator, FusedIterator);\n    assert_impl_all!(Iter<'_>: Iterator, ExactSizeIterator, FusedIterator);\n    assert_impl_all!(IntoIter: Iterator, ExactSizeIterator, FusedIterator);\n    assert_impl_all!(Drain<'_>: Iterator, ExactSizeIterator, FusedIterator);\n\n    #[test]\n    fn create() {\n        let map = HeaderMap::new();\n        assert_eq!(map.len(), 0);\n        assert_eq!(map.capacity(), 0);\n\n        let map = HeaderMap::with_capacity(16);\n        assert_eq!(map.len(), 0);\n        assert!(map.capacity() >= 16);\n    }\n\n    #[test]\n    fn insert() {\n        let mut map = HeaderMap::new();\n\n        map.insert(header::LOCATION, HeaderValue::from_static(\"/test\"));\n        assert_eq!(map.len(), 1);\n    }\n\n    #[test]\n    fn contains() {\n        let mut map = HeaderMap::new();\n        assert!(!map.contains_key(header::LOCATION));\n\n        map.insert(header::LOCATION, HeaderValue::from_static(\"/test\"));\n        assert!(map.contains_key(header::LOCATION));\n        assert!(map.contains_key(\"Location\"));\n        assert!(map.contains_key(\"Location\".to_owned()));\n        assert!(map.contains_key(\"location\"));\n    }\n\n    #[test]\n    fn entries_iter() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        let mut iter = map.iter();\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_none());\n\n        let pairs = map.iter().collect::<Vec<_>>();\n        assert!(pairs.contains(&(&header::HOST, &HeaderValue::from_static(\"duck.com\"))));\n        assert!(pairs.contains(&(&header::COOKIE, &HeaderValue::from_static(\"one=1\"))));\n        assert!(pairs.contains(&(&header::COOKIE, &HeaderValue::from_static(\"two=2\"))));\n    }\n\n    #[test]\n    fn drain_iter() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        let mut vals = vec![];\n        let mut iter = map.drain();\n\n        let (name, val) = iter.next().unwrap();\n        assert_eq!(name, Some(header::COOKIE));\n        vals.push(val);\n\n        let (name, val) = iter.next().unwrap();\n        assert!(name.is_none());\n        vals.push(val);\n\n        assert!(vals.contains(&HeaderValue::from_static(\"one=1\")));\n        assert!(vals.contains(&HeaderValue::from_static(\"two=2\")));\n\n        assert!(iter.next().is_none());\n        drop(iter);\n\n        assert!(map.is_empty());\n    }\n\n    #[test]\n    fn retain() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::LOCATION, HeaderValue::from_static(\"/test\"));\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        assert_eq!(map.len(), 4);\n\n        // by value\n        map.retain(|_, val| !val.as_bytes().contains(&b'/'));\n        assert_eq!(map.len(), 3);\n\n        // by name\n        map.retain(|name, _| name.as_str() != \"cookie\");\n        assert_eq!(map.len(), 1);\n\n        // keep but mutate value\n        map.retain(|_, val| {\n            *val = HeaderValue::from_static(\"replaced\");\n            true\n        });\n        assert_eq!(map.len(), 1);\n        assert_eq!(map.get(\"host\").unwrap(), \"replaced\");\n    }\n\n    #[test]\n    fn retain_removes_empty_value_lists() {\n        let mut map = HeaderMap::with_capacity(3);\n\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n\n        assert_eq!(map.len(), 2);\n        assert_eq!(map.len_keys(), 1);\n        assert_eq!(map.inner.len(), 1);\n        assert_eq!(map.capacity(), 3);\n\n        // remove everything\n        map.retain(|_n, _v| false);\n\n        assert_eq!(map.len(), 0);\n        assert_eq!(map.len_keys(), 0);\n        assert_eq!(map.inner.len(), 0);\n        assert_eq!(map.capacity(), 3);\n    }\n\n    #[test]\n    fn entries_into_iter() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        let mut iter = map.into_iter();\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_some());\n        assert!(iter.next().is_none());\n    }\n\n    #[test]\n    fn iter_and_into_iter_same_order() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::HOST, HeaderValue::from_static(\"duck.com\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        let mut iter = map.iter();\n        let mut into_iter = map.clone().into_iter();\n\n        assert_eq!(iter.next().map(owned_pair), into_iter.next());\n        assert_eq!(iter.next().map(owned_pair), into_iter.next());\n        assert_eq!(iter.next().map(owned_pair), into_iter.next());\n        assert_eq!(iter.next().map(owned_pair), into_iter.next());\n    }\n\n    #[test]\n    fn get_all_and_remove_same_order() {\n        let mut map = HeaderMap::new();\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"one=1\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"two=2\"));\n\n        let mut vals = map.get_all(header::COOKIE);\n        let mut removed = map.clone().remove(header::COOKIE);\n\n        assert_eq!(vals.next(), removed.next().as_ref());\n        assert_eq!(vals.next(), removed.next().as_ref());\n        assert_eq!(vals.next(), removed.next().as_ref());\n    }\n\n    #[test]\n    fn get_all_iteration_order_matches_insertion_order() {\n        let mut map = HeaderMap::new();\n\n        let mut vals = map.get_all(header::COOKIE);\n        assert!(vals.next().is_none());\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"1\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"1\");\n        assert!(vals.next().is_none());\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"2\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"1\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"2\");\n        assert!(vals.next().is_none());\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"3\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"4\"));\n        map.append(header::COOKIE, HeaderValue::from_static(\"5\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"1\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"2\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"3\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"4\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"5\");\n        assert!(vals.next().is_none());\n\n        let _ = map.insert(header::COOKIE, HeaderValue::from_static(\"6\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"6\");\n        assert!(vals.next().is_none());\n\n        let _ = map.insert(header::COOKIE, HeaderValue::from_static(\"7\"));\n        let _ = map.insert(header::COOKIE, HeaderValue::from_static(\"8\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"8\");\n        assert!(vals.next().is_none());\n\n        map.append(header::COOKIE, HeaderValue::from_static(\"9\"));\n        let mut vals = map.get_all(header::COOKIE);\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"8\");\n        assert_eq!(vals.next().unwrap().as_bytes(), b\"9\");\n        assert!(vals.next().is_none());\n\n        // check for fused-ness\n        assert!(vals.next().is_none());\n    }\n\n    #[test]\n    fn iter_len_counts_values() {\n        let mut map = HeaderMap::new();\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"a=1\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"b=2\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"c=3\"));\n\n        assert_eq!(map.iter().count(), 3);\n        assert_eq!(map.iter().len(), 3);\n    }\n\n    #[test]\n    fn into_iter_len_counts_values() {\n        let mut map = HeaderMap::new();\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"a=1\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"b=2\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"c=3\"));\n\n        assert_eq!(map.clone().into_iter().count(), 3);\n        assert_eq!(map.into_iter().len(), 3);\n    }\n\n    #[test]\n    fn drain_len_counts_values() {\n        let mut map = HeaderMap::new();\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"a=1\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"b=2\"));\n        map.append(header::SET_COOKIE, HeaderValue::from_static(\"c=3\"));\n\n        let mut drained = map.clone();\n        assert_eq!(map.drain().count(), 3);\n        assert_eq!(drained.drain().len(), 3);\n    }\n\n    fn owned_pair<'a>((name, val): (&'a HeaderName, &'a HeaderValue)) -> (HeaderName, HeaderValue) {\n        (name.clone(), val.clone())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/mod.rs",
    "content": "//! Pre-defined `HeaderName`s, traits for parsing and conversion, and other header utility methods.\n\n// declaring new header consts will yield this error\n#![allow(clippy::declare_interior_mutable_const)]\n\n// re-export from http except header map related items\npub use ::http::header::{\n    HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue, ToStrError,\n};\n// re-export const header names, list is explicit so that any updates to `common` module do not\n// conflict with this set\npub use ::http::header::{\n    ACCEPT, ACCEPT_CHARSET, ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES,\n    ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,\n    ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE,\n    ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE, ALLOW, ALT_SVC,\n    AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING,\n    CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE, CONTENT_SECURITY_POLICY,\n    CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, DATE, DNT, ETAG, EXPECT, EXPIRES,\n    FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE,\n    IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, LOCATION, MAX_FORWARDS, ORIGIN, PRAGMA,\n    PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, PUBLIC_KEY_PINS, PUBLIC_KEY_PINS_REPORT_ONLY, RANGE,\n    REFERER, REFERRER_POLICY, REFRESH, RETRY_AFTER, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_EXTENSIONS,\n    SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL, SEC_WEBSOCKET_VERSION, SERVER, SET_COOKIE,\n    STRICT_TRANSPORT_SECURITY, TE, TRAILER, TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS,\n    USER_AGENT, VARY, VIA, WARNING, WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS,\n    X_DNS_PREFETCH_CONTROL, X_FRAME_OPTIONS, X_XSS_PROTECTION,\n};\nuse percent_encoding::{AsciiSet, CONTROLS};\n\nuse crate::{error::ParseError, HttpMessage};\n\nmod as_name;\nmod common;\nmod into_pair;\nmod into_value;\npub mod map;\nmod shared;\nmod utils;\n\npub use self::{\n    as_name::AsHeaderName,\n    // re-export list is explicit so that any updates to `http` do not conflict with this set\n    common::{\n        CACHE_STATUS, CDN_CACHE_CONTROL, CLEAR_SITE_DATA, CROSS_ORIGIN_EMBEDDER_POLICY,\n        CROSS_ORIGIN_OPENER_POLICY, CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY,\n        X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO,\n    },\n    into_pair::TryIntoHeaderPair,\n    into_value::TryIntoHeaderValue,\n    map::HeaderMap,\n    shared::{\n        parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate, LanguageTag,\n        Quality, QualityItem,\n    },\n    utils::{fmt_comma_delimited, from_comma_delimited, from_one_raw_str, http_percent_encode},\n};\n\n/// An interface for types that already represent a valid header.\npub trait Header: TryIntoHeaderValue {\n    /// Returns the name of the header field.\n    fn name() -> HeaderName;\n\n    /// Parse the header from a HTTP message.\n    fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError>;\n}\n\n/// This encode set is used for HTTP header values and is defined at\n/// <https://datatracker.ietf.org/doc/html/rfc5987#section-3.2>.\npub(crate) const HTTP_VALUE: &AsciiSet = &CONTROLS\n    .add(b' ')\n    .add(b'\"')\n    .add(b'%')\n    .add(b'\\'')\n    .add(b'(')\n    .add(b')')\n    .add(b'*')\n    .add(b',')\n    .add(b'/')\n    .add(b':')\n    .add(b';')\n    .add(b'<')\n    .add(b'-')\n    .add(b'>')\n    .add(b'?')\n    .add(b'[')\n    .add(b'\\\\')\n    .add(b']')\n    .add(b'{')\n    .add(b'}');\n"
  },
  {
    "path": "actix-http/src/header/shared/charset.rs",
    "content": "use std::{fmt, str};\n\nuse self::Charset::*;\n\n/// A MIME character set.\n///\n/// The string representation is normalized to upper case.\n///\n/// See <http://www.iana.org/assignments/character-sets/character-sets.xhtml>.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[allow(non_camel_case_types)]\npub enum Charset {\n    /// US ASCII\n    Us_Ascii,\n    /// ISO-8859-1\n    Iso_8859_1,\n    /// ISO-8859-2\n    Iso_8859_2,\n    /// ISO-8859-3\n    Iso_8859_3,\n    /// ISO-8859-4\n    Iso_8859_4,\n    /// ISO-8859-5\n    Iso_8859_5,\n    /// ISO-8859-6\n    Iso_8859_6,\n    /// ISO-8859-7\n    Iso_8859_7,\n    /// ISO-8859-8\n    Iso_8859_8,\n    /// ISO-8859-9\n    Iso_8859_9,\n    /// ISO-8859-10\n    Iso_8859_10,\n    /// Shift_JIS\n    Shift_Jis,\n    /// EUC-JP\n    Euc_Jp,\n    /// ISO-2022-KR\n    Iso_2022_Kr,\n    /// EUC-KR\n    Euc_Kr,\n    /// ISO-2022-JP\n    Iso_2022_Jp,\n    /// ISO-2022-JP-2\n    Iso_2022_Jp_2,\n    /// ISO-8859-6-E\n    Iso_8859_6_E,\n    /// ISO-8859-6-I\n    Iso_8859_6_I,\n    /// ISO-8859-8-E\n    Iso_8859_8_E,\n    /// ISO-8859-8-I\n    Iso_8859_8_I,\n    /// GB2312\n    Gb2312,\n    /// Big5\n    Big5,\n    /// KOI8-R\n    Koi8_R,\n    /// An arbitrary charset specified as a string\n    Ext(String),\n}\n\nimpl Charset {\n    fn label(&self) -> &str {\n        match *self {\n            Us_Ascii => \"US-ASCII\",\n            Iso_8859_1 => \"ISO-8859-1\",\n            Iso_8859_2 => \"ISO-8859-2\",\n            Iso_8859_3 => \"ISO-8859-3\",\n            Iso_8859_4 => \"ISO-8859-4\",\n            Iso_8859_5 => \"ISO-8859-5\",\n            Iso_8859_6 => \"ISO-8859-6\",\n            Iso_8859_7 => \"ISO-8859-7\",\n            Iso_8859_8 => \"ISO-8859-8\",\n            Iso_8859_9 => \"ISO-8859-9\",\n            Iso_8859_10 => \"ISO-8859-10\",\n            Shift_Jis => \"Shift-JIS\",\n            Euc_Jp => \"EUC-JP\",\n            Iso_2022_Kr => \"ISO-2022-KR\",\n            Euc_Kr => \"EUC-KR\",\n            Iso_2022_Jp => \"ISO-2022-JP\",\n            Iso_2022_Jp_2 => \"ISO-2022-JP-2\",\n            Iso_8859_6_E => \"ISO-8859-6-E\",\n            Iso_8859_6_I => \"ISO-8859-6-I\",\n            Iso_8859_8_E => \"ISO-8859-8-E\",\n            Iso_8859_8_I => \"ISO-8859-8-I\",\n            Gb2312 => \"GB2312\",\n            Big5 => \"Big5\",\n            Koi8_R => \"KOI8-R\",\n            Ext(ref s) => s,\n        }\n    }\n}\n\nimpl fmt::Display for Charset {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(self.label())\n    }\n}\n\nimpl str::FromStr for Charset {\n    type Err = crate::Error;\n\n    fn from_str(s: &str) -> Result<Charset, crate::Error> {\n        Ok(match s.to_ascii_uppercase().as_ref() {\n            \"US-ASCII\" => Us_Ascii,\n            \"ISO-8859-1\" => Iso_8859_1,\n            \"ISO-8859-2\" => Iso_8859_2,\n            \"ISO-8859-3\" => Iso_8859_3,\n            \"ISO-8859-4\" => Iso_8859_4,\n            \"ISO-8859-5\" => Iso_8859_5,\n            \"ISO-8859-6\" => Iso_8859_6,\n            \"ISO-8859-7\" => Iso_8859_7,\n            \"ISO-8859-8\" => Iso_8859_8,\n            \"ISO-8859-9\" => Iso_8859_9,\n            \"ISO-8859-10\" => Iso_8859_10,\n            \"SHIFT-JIS\" => Shift_Jis,\n            \"EUC-JP\" => Euc_Jp,\n            \"ISO-2022-KR\" => Iso_2022_Kr,\n            \"EUC-KR\" => Euc_Kr,\n            \"ISO-2022-JP\" => Iso_2022_Jp,\n            \"ISO-2022-JP-2\" => Iso_2022_Jp_2,\n            \"ISO-8859-6-E\" => Iso_8859_6_E,\n            \"ISO-8859-6-I\" => Iso_8859_6_I,\n            \"ISO-8859-8-E\" => Iso_8859_8_E,\n            \"ISO-8859-8-I\" => Iso_8859_8_I,\n            \"GB2312\" => Gb2312,\n            \"BIG5\" => Big5,\n            \"KOI8-R\" => Koi8_R,\n            s => Ext(s.to_owned()),\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_parse() {\n        assert_eq!(Us_Ascii, \"us-ascii\".parse().unwrap());\n        assert_eq!(Us_Ascii, \"US-Ascii\".parse().unwrap());\n        assert_eq!(Us_Ascii, \"US-ASCII\".parse().unwrap());\n        assert_eq!(Shift_Jis, \"Shift-JIS\".parse().unwrap());\n        assert_eq!(Ext(\"ABCD\".to_owned()), \"abcd\".parse().unwrap());\n    }\n\n    #[test]\n    fn test_display() {\n        assert_eq!(\"US-ASCII\", format!(\"{}\", Us_Ascii));\n        assert_eq!(\"ABCD\", format!(\"{}\", Ext(\"ABCD\".to_owned())));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/shared/content_encoding.rs",
    "content": "use std::str::FromStr;\n\nuse derive_more::{Display, Error};\nuse http::header::InvalidHeaderValue;\n\nuse crate::{\n    error::ParseError,\n    header::{self, from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue},\n    HttpMessage,\n};\n\n/// Error returned when a content encoding is unknown.\n#[derive(Debug, Display, Error)]\n#[display(\"unsupported content encoding\")]\npub struct ContentEncodingParseError;\n\n/// Represents a supported content encoding.\n///\n/// Includes a commonly-used subset of media types appropriate for use as HTTP content encodings.\n/// See [IANA HTTP Content Coding Registry].\n///\n/// [IANA HTTP Content Coding Registry]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[non_exhaustive]\npub enum ContentEncoding {\n    /// Indicates the no-op identity encoding.\n    ///\n    /// I.e., no compression or modification.\n    Identity,\n\n    /// A format using the Brotli algorithm.\n    Brotli,\n\n    /// A format using the zlib structure with deflate algorithm.\n    Deflate,\n\n    /// Gzip algorithm.\n    Gzip,\n\n    /// Zstd algorithm.\n    Zstd,\n}\n\nimpl ContentEncoding {\n    /// Convert content encoding to string.\n    #[inline]\n    pub const fn as_str(self) -> &'static str {\n        match self {\n            ContentEncoding::Brotli => \"br\",\n            ContentEncoding::Gzip => \"gzip\",\n            ContentEncoding::Deflate => \"deflate\",\n            ContentEncoding::Zstd => \"zstd\",\n            ContentEncoding::Identity => \"identity\",\n        }\n    }\n\n    /// Convert content encoding to header value.\n    #[inline]\n    pub const fn to_header_value(self) -> HeaderValue {\n        match self {\n            ContentEncoding::Brotli => HeaderValue::from_static(\"br\"),\n            ContentEncoding::Gzip => HeaderValue::from_static(\"gzip\"),\n            ContentEncoding::Deflate => HeaderValue::from_static(\"deflate\"),\n            ContentEncoding::Zstd => HeaderValue::from_static(\"zstd\"),\n            ContentEncoding::Identity => HeaderValue::from_static(\"identity\"),\n        }\n    }\n}\n\nimpl Default for ContentEncoding {\n    #[inline]\n    fn default() -> Self {\n        Self::Identity\n    }\n}\n\nimpl FromStr for ContentEncoding {\n    type Err = ContentEncodingParseError;\n\n    fn from_str(enc: &str) -> Result<Self, Self::Err> {\n        let enc = enc.trim();\n\n        if enc.eq_ignore_ascii_case(\"br\") {\n            Ok(ContentEncoding::Brotli)\n        } else if enc.eq_ignore_ascii_case(\"gzip\") {\n            Ok(ContentEncoding::Gzip)\n        } else if enc.eq_ignore_ascii_case(\"deflate\") {\n            Ok(ContentEncoding::Deflate)\n        } else if enc.eq_ignore_ascii_case(\"identity\") {\n            Ok(ContentEncoding::Identity)\n        } else if enc.eq_ignore_ascii_case(\"zstd\") {\n            Ok(ContentEncoding::Zstd)\n        } else {\n            Err(ContentEncodingParseError)\n        }\n    }\n}\n\nimpl TryFrom<&str> for ContentEncoding {\n    type Error = ContentEncodingParseError;\n\n    fn try_from(val: &str) -> Result<Self, Self::Error> {\n        val.parse()\n    }\n}\n\nimpl TryIntoHeaderValue for ContentEncoding {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<http::HeaderValue, Self::Error> {\n        Ok(HeaderValue::from_static(self.as_str()))\n    }\n}\n\nimpl Header for ContentEncoding {\n    fn name() -> HeaderName {\n        header::CONTENT_ENCODING\n    }\n\n    fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {\n        from_one_raw_str(msg.headers().get(Self::name()))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/shared/extended.rs",
    "content": "//! Originally taken from `hyper::header::parsing`.\n\nuse std::{fmt, str::FromStr};\n\nuse language_tags::LanguageTag;\n\nuse crate::header::{Charset, HTTP_VALUE};\n\n/// The value part of an extended parameter consisting of three parts:\n/// - The REQUIRED character set name (`charset`).\n/// - The OPTIONAL language information (`language_tag`).\n/// - A character sequence representing the actual value (`value`), separated by single quotes.\n///\n/// It is defined in [RFC 5987 §3.2](https://datatracker.ietf.org/doc/html/rfc5987#section-3.2).\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ExtendedValue {\n    /// The character set that is used to encode the `value` to a string.\n    pub charset: Charset,\n\n    /// The human language details of the `value`, if available.\n    pub language_tag: Option<LanguageTag>,\n\n    /// The parameter value, as expressed in octets.\n    pub value: Vec<u8>,\n}\n\n/// Parses extended header parameter values (`ext-value`), as defined\n/// in [RFC 5987 §3.2](https://datatracker.ietf.org/doc/html/rfc5987#section-3.2).\n///\n/// Extended values are denoted by parameter names that end with `*`.\n///\n/// ## ABNF\n///\n/// ```plain\n/// ext-value     = charset  \"'\" [ language ] \"'\" value-chars\n///               ; like RFC 2231's <extended-initial-value>\n///               ; (see [RFC 2231 §7])\n///\n/// charset       = \"UTF-8\" / \"ISO-8859-1\" / mime-charset\n///\n/// mime-charset  = 1*mime-charsetc\n/// mime-charsetc = ALPHA / DIGIT\n///               / \"!\" / \"#\" / \"$\" / \"%\" / \"&\"\n///               / \"+\" / \"-\" / \"^\" / \"_\" / \"`\"\n///               / \"{\" / \"}\" / \"~\"\n///               ; as <mime-charset> in [RFC 2978 §2.3]\n///               ; except that the single quote is not included\n///               ; SHOULD be registered in the IANA charset registry\n///\n/// language      = <Language-Tag, defined in [RFC 5646 §2.1]>\n///\n/// value-chars   = *( pct-encoded / attr-char )\n///\n/// pct-encoded   = \"%\" HEXDIG HEXDIG\n///               ; see [RFC 3986 §2.1]\n///\n/// attr-char     = ALPHA / DIGIT\n///               / \"!\" / \"#\" / \"$\" / \"&\" / \"+\" / \"-\" / \".\"\n///               / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n///               ; token except ( \"*\" / \"'\" / \"%\" )\n/// ```\n///\n/// [RFC 2231 §7]: https://datatracker.ietf.org/doc/html/rfc2231#section-7\n/// [RFC 2978 §2.3]: https://datatracker.ietf.org/doc/html/rfc2978#section-2.3\n/// [RFC 3986 §2.1]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1\npub fn parse_extended_value(val: &str) -> Result<ExtendedValue, crate::error::ParseError> {\n    // Break into three pieces separated by the single-quote character\n    let mut parts = val.splitn(3, '\\'');\n\n    // Interpret the first piece as a Charset\n    let charset: Charset = match parts.next() {\n        None => return Err(crate::error::ParseError::Header),\n        Some(n) => FromStr::from_str(n).map_err(|_| crate::error::ParseError::Header)?,\n    };\n\n    // Interpret the second piece as a language tag\n    let language_tag: Option<LanguageTag> = match parts.next() {\n        None => return Err(crate::error::ParseError::Header),\n        Some(\"\") => None,\n        Some(s) => match s.parse() {\n            Ok(lt) => Some(lt),\n            Err(_) => return Err(crate::error::ParseError::Header),\n        },\n    };\n\n    // Interpret the third piece as a sequence of value characters\n    let value: Vec<u8> = match parts.next() {\n        None => return Err(crate::error::ParseError::Header),\n        Some(v) => percent_encoding::percent_decode(v.as_bytes()).collect(),\n    };\n\n    Ok(ExtendedValue {\n        charset,\n        language_tag,\n        value,\n    })\n}\n\nimpl fmt::Display for ExtendedValue {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let encoded_value = percent_encoding::percent_encode(&self.value[..], HTTP_VALUE);\n        if let Some(ref lang) = self.language_tag {\n            write!(f, \"{}'{}'{}\", self.charset, lang, encoded_value)\n        } else {\n            write!(f, \"{}''{}\", self.charset, encoded_value)\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_parse_extended_value_with_encoding_and_language_tag() {\n        let expected_language_tag = \"en\".parse::<LanguageTag>().unwrap();\n        // RFC 5987, Section 3.2.2\n        // Extended notation, using the Unicode character U+00A3 (POUND SIGN)\n        let result = parse_extended_value(\"iso-8859-1'en'%A3%20rates\");\n        assert!(result.is_ok());\n        let extended_value = result.unwrap();\n        assert_eq!(Charset::Iso_8859_1, extended_value.charset);\n        assert!(extended_value.language_tag.is_some());\n        assert_eq!(expected_language_tag, extended_value.language_tag.unwrap());\n        assert_eq!(\n            vec![163, b' ', b'r', b'a', b't', b'e', b's'],\n            extended_value.value\n        );\n    }\n\n    #[test]\n    fn test_parse_extended_value_with_encoding() {\n        // RFC 5987, Section 3.2.2\n        // Extended notation, using the Unicode characters U+00A3 (POUND SIGN)\n        // and U+20AC (EURO SIGN)\n        let result = parse_extended_value(\"UTF-8''%c2%a3%20and%20%e2%82%ac%20rates\");\n        assert!(result.is_ok());\n        let extended_value = result.unwrap();\n        assert_eq!(Charset::Ext(\"UTF-8\".to_string()), extended_value.charset);\n        assert!(extended_value.language_tag.is_none());\n        assert_eq!(\n            vec![\n                194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't',\n                b'e', b's',\n            ],\n            extended_value.value\n        );\n    }\n\n    #[test]\n    fn test_parse_extended_value_missing_language_tag_and_encoding() {\n        // From: https://greenbytes.de/tech/tc2231/#attwithfn2231quot2\n        let result = parse_extended_value(\"foo%20bar.html\");\n        assert!(result.is_err());\n    }\n\n    #[test]\n    fn test_parse_extended_value_partially_formatted() {\n        let result = parse_extended_value(\"UTF-8'missing third part\");\n        assert!(result.is_err());\n    }\n\n    #[test]\n    fn test_parse_extended_value_partially_formatted_blank() {\n        let result = parse_extended_value(\"blank second part'\");\n        assert!(result.is_err());\n    }\n\n    #[test]\n    fn test_fmt_extended_value_with_encoding_and_language_tag() {\n        let extended_value = ExtendedValue {\n            charset: Charset::Iso_8859_1,\n            language_tag: Some(\"en\".parse().expect(\"Could not parse language tag\")),\n            value: vec![163, b' ', b'r', b'a', b't', b'e', b's'],\n        };\n        assert_eq!(\"ISO-8859-1'en'%A3%20rates\", format!(\"{}\", extended_value));\n    }\n\n    #[test]\n    fn test_fmt_extended_value_with_encoding() {\n        let extended_value = ExtendedValue {\n            charset: Charset::Ext(\"UTF-8\".to_string()),\n            language_tag: None,\n            value: vec![\n                194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't',\n                b'e', b's',\n            ],\n        };\n        assert_eq!(\n            \"UTF-8''%C2%A3%20and%20%E2%82%AC%20rates\",\n            format!(\"{}\", extended_value)\n        );\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/shared/http_date.rs",
    "content": "use std::{fmt, io::Write, str::FromStr, time::SystemTime};\n\nuse bytes::BytesMut;\nuse http::header::{HeaderValue, InvalidHeaderValue};\n\nuse crate::{\n    date::DATE_VALUE_LENGTH, error::ParseError, header::TryIntoHeaderValue, helpers::MutWriter,\n};\n\n/// A timestamp with HTTP-style formatting and parsing.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub struct HttpDate(SystemTime);\n\nimpl FromStr for HttpDate {\n    type Err = ParseError;\n\n    fn from_str(s: &str) -> Result<HttpDate, ParseError> {\n        match httpdate::parse_http_date(s) {\n            Ok(sys_time) => Ok(HttpDate(sys_time)),\n            Err(_) => Err(ParseError::Header),\n        }\n    }\n}\n\nimpl fmt::Display for HttpDate {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        httpdate::HttpDate::from(self.0).fmt(f)\n    }\n}\n\nimpl TryIntoHeaderValue for HttpDate {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH);\n        let mut wrt = MutWriter(&mut buf);\n\n        // unwrap: date output is known to be well formed and of known length\n        write!(wrt, \"{}\", self).unwrap();\n\n        HeaderValue::from_maybe_shared(buf.split().freeze())\n    }\n}\n\nimpl From<SystemTime> for HttpDate {\n    fn from(sys_time: SystemTime) -> HttpDate {\n        HttpDate(sys_time)\n    }\n}\n\nimpl From<HttpDate> for SystemTime {\n    fn from(HttpDate(sys_time): HttpDate) -> SystemTime {\n        sys_time\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use super::*;\n\n    #[test]\n    fn date_header() {\n        macro_rules! assert_parsed_date {\n            ($case:expr, $exp:expr) => {\n                assert_eq!($case.parse::<HttpDate>().unwrap(), $exp);\n            };\n        }\n\n        // 784198117 = SystemTime::from(datetime!(1994-11-07 08:48:37).assume_utc()).duration_since(SystemTime::UNIX_EPOCH));\n        let nov_07 = HttpDate(SystemTime::UNIX_EPOCH + Duration::from_secs(784198117));\n\n        assert_parsed_date!(\"Mon, 07 Nov 1994 08:48:37 GMT\", nov_07);\n        assert_parsed_date!(\"Monday, 07-Nov-94 08:48:37 GMT\", nov_07);\n        assert_parsed_date!(\"Mon Nov  7 08:48:37 1994\", nov_07);\n\n        assert!(\"this-is-no-date\".parse::<HttpDate>().is_err());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/shared/mod.rs",
    "content": "//! Originally taken from `hyper::header::shared`.\n\npub use language_tags::LanguageTag;\n\nmod charset;\nmod content_encoding;\nmod extended;\nmod http_date;\nmod quality;\nmod quality_item;\n\npub use self::{\n    charset::Charset,\n    content_encoding::ContentEncoding,\n    extended::{parse_extended_value, ExtendedValue},\n    http_date::HttpDate,\n    quality::{q, Quality},\n    quality_item::QualityItem,\n};\n"
  },
  {
    "path": "actix-http/src/header/shared/quality.rs",
    "content": "use std::fmt;\n\nuse derive_more::{Display, Error};\n\nconst MAX_QUALITY_INT: u16 = 1000;\nconst MAX_QUALITY_FLOAT: f32 = 1.0;\n\n/// Represents a quality used in q-factor values.\n///\n/// The default value is equivalent to `q=1.0` (the [max](Self::MAX) value).\n///\n/// # Implementation notes\n/// The quality value is defined as a number between 0.0 and 1.0 with three decimal places.\n/// This means there are 1001 possible values. Since floating point numbers are not exact and the\n/// smallest floating point data type (`f32`) consumes four bytes, we use an `u16` value to store\n/// the quality internally.\n///\n/// [RFC 7231 §5.3.1] gives more information on quality values in HTTP header fields.\n///\n/// # Examples\n/// ```\n/// use actix_http::header::{Quality, q};\n/// assert_eq!(q(1.0), Quality::MAX);\n///\n/// assert_eq!(q(0.42).to_string(), \"0.42\");\n/// assert_eq!(q(1.0).to_string(), \"1\");\n/// assert_eq!(Quality::MIN.to_string(), \"0.001\");\n/// assert_eq!(Quality::ZERO.to_string(), \"0\");\n/// ```\n///\n/// [RFC 7231 §5.3.1]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub struct Quality(pub(super) u16);\n\nimpl Quality {\n    /// The maximum quality value, equivalent to `q=1.0`.\n    pub const MAX: Quality = Quality(MAX_QUALITY_INT);\n\n    /// The minimum, non-zero quality value, equivalent to `q=0.001`.\n    pub const MIN: Quality = Quality(1);\n\n    /// The zero quality value, equivalent to `q=0.0`.\n    pub const ZERO: Quality = Quality(0);\n\n    /// Converts a float in the range 0.0–1.0 to a `Quality`.\n    ///\n    /// Intentionally private. External uses should rely on the `TryFrom` impl.\n    ///\n    /// # Panics\n    /// Panics in debug mode when value is not in the range 0.0 <= n <= 1.0.\n    fn from_f32(value: f32) -> Self {\n        // Check that `value` is within range should be done before calling this method.\n        // Just in case, this debug_assert should catch if we were forgetful.\n        debug_assert!(\n            (0.0..=MAX_QUALITY_FLOAT).contains(&value),\n            \"q value must be between 0.0 and 1.0\"\n        );\n\n        Quality((value * MAX_QUALITY_INT as f32) as u16)\n    }\n}\n\n/// The default value is [`Quality::MAX`].\nimpl Default for Quality {\n    fn default() -> Quality {\n        Quality::MAX\n    }\n}\n\nimpl fmt::Display for Quality {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.0 {\n            0 => f.write_str(\"0\"),\n            MAX_QUALITY_INT => f.write_str(\"1\"),\n\n            // some number in the range 1–999\n            x => {\n                f.write_str(\"0.\")?;\n\n                // This implementation avoids string allocation for removing trailing zeroes.\n                // In benchmarks it is twice as fast as approach using something like\n                // `format!(\"{}\").trim_end_matches('0')` for non-fast-path quality values.\n\n                if x < 10 {\n                    // x in is range 1–9\n\n                    f.write_str(\"00\")?;\n\n                    // 0 is already handled so it's not possible to have a trailing 0 in this range\n                    // we can just write the integer\n                    itoa_fmt(f, x)\n                } else if x < 100 {\n                    // x in is range 10–99\n\n                    f.write_str(\"0\")?;\n\n                    if x % 10 == 0 {\n                        // trailing 0, divide by 10 and write\n                        itoa_fmt(f, x / 10)\n                    } else {\n                        itoa_fmt(f, x)\n                    }\n                } else {\n                    // x is in range 100–999\n\n                    if x % 100 == 0 {\n                        // two trailing 0s, divide by 100 and write\n                        itoa_fmt(f, x / 100)\n                    } else if x % 10 == 0 {\n                        // one trailing 0, divide by 10 and write\n                        itoa_fmt(f, x / 10)\n                    } else {\n                        itoa_fmt(f, x)\n                    }\n                }\n            }\n        }\n    }\n}\n\n/// Write integer to a `fmt::Write`.\npub fn itoa_fmt<W: fmt::Write, V: itoa::Integer>(mut wr: W, value: V) -> fmt::Result {\n    let mut buf = itoa::Buffer::new();\n    wr.write_str(buf.format(value))\n}\n\n#[derive(Debug, Clone, Display, Error)]\n#[display(\"quality out of bounds\")]\n#[non_exhaustive]\npub struct QualityOutOfBounds;\n\nimpl TryFrom<f32> for Quality {\n    type Error = QualityOutOfBounds;\n\n    #[inline]\n    fn try_from(value: f32) -> Result<Self, Self::Error> {\n        if (0.0..=MAX_QUALITY_FLOAT).contains(&value) {\n            Ok(Quality::from_f32(value))\n        } else {\n            Err(QualityOutOfBounds)\n        }\n    }\n}\n\n/// Convenience function to create a [`Quality`] from an `f32` (0.0–1.0).\n///\n/// Not recommended for use with user input. Rely on the `TryFrom` impls where possible.\n///\n/// # Panics\n/// Panics if value is out of range.\n///\n/// # Examples\n/// ```\n/// # use actix_http::header::{q, Quality};\n/// let q1 = q(1.0);\n/// assert_eq!(q1, Quality::MAX);\n///\n/// let q2 = q(0.001);\n/// assert_eq!(q2, Quality::MIN);\n///\n/// let q3 = q(0.0);\n/// assert_eq!(q3, Quality::ZERO);\n///\n/// let q4 = q(0.42);\n/// ```\n///\n/// An out-of-range `f32` quality will panic.\n/// ```should_panic\n/// # use actix_http::header::q;\n/// let _q2 = q(1.42);\n/// ```\n#[inline]\npub fn q<T>(quality: T) -> Quality\nwhere\n    T: TryInto<Quality>,\n    T::Error: fmt::Debug,\n{\n    quality.try_into().expect(\"quality value was out of bounds\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn q_helper() {\n        assert_eq!(q(0.5), Quality(500));\n    }\n\n    #[test]\n    fn display_output() {\n        assert_eq!(Quality::ZERO.to_string(), \"0\");\n        assert_eq!(Quality::MIN.to_string(), \"0.001\");\n        assert_eq!(Quality::MAX.to_string(), \"1\");\n\n        assert_eq!(q(0.0).to_string(), \"0\");\n        assert_eq!(q(1.0).to_string(), \"1\");\n        assert_eq!(q(0.001).to_string(), \"0.001\");\n        assert_eq!(q(0.5).to_string(), \"0.5\");\n        assert_eq!(q(0.22).to_string(), \"0.22\");\n        assert_eq!(q(0.123).to_string(), \"0.123\");\n        assert_eq!(q(0.999).to_string(), \"0.999\");\n\n        for x in 0..=1000 {\n            // if trailing zeroes are handled correctly, we would not expect the serialized length\n            // to ever exceed \"0.\" + 3 decimal places = 5 in length\n            assert!(q(x as f32 / 1000.0).to_string().len() <= 5);\n        }\n    }\n\n    #[test]\n    #[should_panic]\n    fn negative_quality() {\n        q(-1.0);\n    }\n\n    #[test]\n    #[should_panic]\n    fn quality_out_of_bounds() {\n        q(2.0);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/shared/quality_item.rs",
    "content": "use std::{cmp, fmt, str};\n\nuse super::Quality;\nuse crate::error::ParseError;\n\n/// Represents an item with a quality value as defined\n/// in [RFC 7231 §5.3.1](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1).\n///\n/// # Parsing and Formatting\n/// This wrapper be used to parse header value items that have a q-factor annotation as well as\n/// serialize items with a their q-factor.\n///\n/// # Ordering\n/// Since this context of use for this type is header value items, ordering is defined for\n/// `QualityItem`s but _only_ considers the item's quality. Order of appearance should be used as\n/// the secondary sorting parameter; i.e., a stable sort over the quality values will produce a\n/// correctly sorted sequence.\n///\n/// # Examples\n/// ```\n/// # use actix_http::header::{QualityItem, q};\n/// let q_item: QualityItem<String> = \"hello;q=0.3\".parse().unwrap();\n/// assert_eq!(&q_item.item, \"hello\");\n/// assert_eq!(q_item.quality, q(0.3));\n///\n/// // note that format is normalized compared to parsed item\n/// assert_eq!(q_item.to_string(), \"hello; q=0.3\");\n///\n/// // item with q=0.3 is greater than item with q=0.1\n/// let q_item_fallback: QualityItem<String> = \"abc;q=0.1\".parse().unwrap();\n/// assert!(q_item > q_item_fallback);\n/// ```\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct QualityItem<T> {\n    /// The wrapped contents of the field.\n    pub item: T,\n\n    /// The quality (client or server preference) for the value.\n    pub quality: Quality,\n}\n\nimpl<T> QualityItem<T> {\n    /// Constructs a new `QualityItem` from an item and a quality value.\n    ///\n    /// The item can be of any type. The quality should be a value in the range [0, 1].\n    pub fn new(item: T, quality: Quality) -> Self {\n        QualityItem { item, quality }\n    }\n\n    /// Constructs a new `QualityItem` from an item, using the maximum q-value.\n    pub fn max(item: T) -> Self {\n        Self::new(item, Quality::MAX)\n    }\n\n    /// Constructs a new `QualityItem` from an item, using the minimum, non-zero q-value.\n    pub fn min(item: T) -> Self {\n        Self::new(item, Quality::MIN)\n    }\n\n    /// Constructs a new `QualityItem` from an item, using zero q-value of zero.\n    pub fn zero(item: T) -> Self {\n        Self::new(item, Quality::ZERO)\n    }\n}\n\nimpl<T: PartialEq> PartialOrd for QualityItem<T> {\n    fn partial_cmp(&self, other: &QualityItem<T>) -> Option<cmp::Ordering> {\n        self.quality.partial_cmp(&other.quality)\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for QualityItem<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.item, f)?;\n\n        match self.quality {\n            // q-factor value is implied for max value\n            Quality::MAX => Ok(()),\n\n            // fast path for zero\n            Quality::ZERO => f.write_str(\"; q=0\"),\n\n            // quality formatting is already using itoa\n            q => write!(f, \"; q={}\", q),\n        }\n    }\n}\n\nimpl<T: str::FromStr> str::FromStr for QualityItem<T> {\n    type Err = ParseError;\n\n    fn from_str(q_item_str: &str) -> Result<Self, Self::Err> {\n        if !q_item_str.is_ascii() {\n            return Err(ParseError::Header);\n        }\n\n        // set defaults used if quality-item parsing fails, i.e., item has no q attribute\n        let mut raw_item = q_item_str;\n        let mut quality = Quality::MAX;\n\n        let parts = q_item_str\n            .rsplit_once(';')\n            .map(|(item, q_attr)| (item.trim(), q_attr.trim()));\n\n        if let Some((val, q_attr)) = parts {\n            // example for item with q-factor:\n            //\n            // gzip;q=0.65\n            // ^^^^         val\n            //      ^^^^^^  q_attr\n            //      ^^      q\n            //        ^^^^  q_val\n\n            if q_attr.len() < 2 {\n                // Can't possibly be an attribute since an attribute needs at least a name followed\n                // by an equals sign. And bare identifiers are forbidden.\n                return Err(ParseError::Header);\n            }\n\n            let q = &q_attr[0..2];\n\n            if q == \"q=\" || q == \"Q=\" {\n                let q_val = &q_attr[2..];\n                if q_val.len() > 5 {\n                    // longer than 5 indicates an over-precise q-factor\n                    return Err(ParseError::Header);\n                }\n\n                let q_value = q_val.parse::<f32>().map_err(|_| ParseError::Header)?;\n                let q_value = Quality::try_from(q_value).map_err(|_| ParseError::Header)?;\n\n                quality = q_value;\n                raw_item = val;\n            }\n        }\n\n        let item = raw_item.parse::<T>().map_err(|_| ParseError::Header)?;\n\n        Ok(QualityItem::new(item, quality))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    // copy of encoding from actix-web headers\n    #[allow(clippy::enum_variant_names)] // allow Encoding prefix on EncodingExt\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    pub enum Encoding {\n        Chunked,\n        Brotli,\n        Gzip,\n        Deflate,\n        Compress,\n        Identity,\n        Trailers,\n        EncodingExt(String),\n    }\n\n    impl fmt::Display for Encoding {\n        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n            use Encoding::*;\n            f.write_str(match *self {\n                Chunked => \"chunked\",\n                Brotli => \"br\",\n                Gzip => \"gzip\",\n                Deflate => \"deflate\",\n                Compress => \"compress\",\n                Identity => \"identity\",\n                Trailers => \"trailers\",\n                EncodingExt(ref s) => s.as_ref(),\n            })\n        }\n    }\n\n    impl str::FromStr for Encoding {\n        type Err = crate::error::ParseError;\n        fn from_str(s: &str) -> Result<Encoding, crate::error::ParseError> {\n            use Encoding::*;\n            match s {\n                \"chunked\" => Ok(Chunked),\n                \"br\" => Ok(Brotli),\n                \"deflate\" => Ok(Deflate),\n                \"gzip\" => Ok(Gzip),\n                \"compress\" => Ok(Compress),\n                \"identity\" => Ok(Identity),\n                \"trailers\" => Ok(Trailers),\n                _ => Ok(EncodingExt(s.to_owned())),\n            }\n        }\n    }\n\n    #[test]\n    fn test_quality_item_fmt_q_1() {\n        use Encoding::*;\n        let x = QualityItem::max(Chunked);\n        assert_eq!(format!(\"{}\", x), \"chunked\");\n    }\n    #[test]\n    fn test_quality_item_fmt_q_0001() {\n        use Encoding::*;\n        let x = QualityItem::new(Chunked, Quality(1));\n        assert_eq!(format!(\"{}\", x), \"chunked; q=0.001\");\n    }\n    #[test]\n    fn test_quality_item_fmt_q_05() {\n        use Encoding::*;\n        // Custom value\n        let x = QualityItem {\n            item: EncodingExt(\"identity\".to_owned()),\n            quality: Quality(500),\n        };\n        assert_eq!(format!(\"{}\", x), \"identity; q=0.5\");\n    }\n\n    #[test]\n    fn test_quality_item_fmt_q_0() {\n        use Encoding::*;\n        // Custom value\n        let x = QualityItem {\n            item: EncodingExt(\"identity\".to_owned()),\n            quality: Quality(0),\n        };\n        assert_eq!(x.to_string(), \"identity; q=0\");\n    }\n\n    #[test]\n    fn test_quality_item_from_str1() {\n        use Encoding::*;\n        let x: Result<QualityItem<Encoding>, _> = \"chunked\".parse();\n        assert_eq!(\n            x.unwrap(),\n            QualityItem {\n                item: Chunked,\n                quality: Quality(1000),\n            }\n        );\n    }\n\n    #[test]\n    fn test_quality_item_from_str2() {\n        use Encoding::*;\n        let x: Result<QualityItem<Encoding>, _> = \"chunked; q=1\".parse();\n        assert_eq!(\n            x.unwrap(),\n            QualityItem {\n                item: Chunked,\n                quality: Quality(1000),\n            }\n        );\n    }\n\n    #[test]\n    fn test_quality_item_from_str3() {\n        use Encoding::*;\n        let x: Result<QualityItem<Encoding>, _> = \"gzip; q=0.5\".parse();\n        assert_eq!(\n            x.unwrap(),\n            QualityItem {\n                item: Gzip,\n                quality: Quality(500),\n            }\n        );\n    }\n\n    #[test]\n    fn test_quality_item_from_str4() {\n        use Encoding::*;\n        let x: Result<QualityItem<Encoding>, _> = \"gzip; q=0.273\".parse();\n        assert_eq!(\n            x.unwrap(),\n            QualityItem {\n                item: Gzip,\n                quality: Quality(273),\n            }\n        );\n    }\n\n    #[test]\n    fn test_quality_item_from_str5() {\n        let x: Result<QualityItem<Encoding>, _> = \"gzip; q=0.2739999\".parse();\n        assert!(x.is_err());\n    }\n\n    #[test]\n    fn test_quality_item_from_str6() {\n        let x: Result<QualityItem<Encoding>, _> = \"gzip; q=2\".parse();\n        assert!(x.is_err());\n    }\n\n    #[test]\n    fn test_quality_item_ordering() {\n        let x: QualityItem<Encoding> = \"gzip; q=0.5\".parse().ok().unwrap();\n        let y: QualityItem<Encoding> = \"gzip; q=0.273\".parse().ok().unwrap();\n        let comparison_result: bool = x.gt(&y);\n        assert!(comparison_result)\n    }\n\n    #[test]\n    fn test_fuzzing_bugs() {\n        assert!(\"99999;\".parse::<QualityItem<String>>().is_err());\n        assert!(\"\\x0d;;;=\\u{d6aa}==\".parse::<QualityItem<String>>().is_err())\n    }\n}\n"
  },
  {
    "path": "actix-http/src/header/utils.rs",
    "content": "//! Header parsing utilities.\n\nuse std::{fmt, str::FromStr};\n\nuse super::HeaderValue;\nuse crate::{error::ParseError, header::HTTP_VALUE};\n\n/// Reads a comma-delimited raw header into a Vec.\n#[inline]\npub fn from_comma_delimited<'a, I, T>(all: I) -> Result<Vec<T>, ParseError>\nwhere\n    I: Iterator<Item = &'a HeaderValue> + 'a,\n    T: FromStr,\n{\n    let size_guess = all.size_hint().1.unwrap_or(2);\n    let mut result = Vec::with_capacity(size_guess);\n\n    for h in all {\n        let s = h.to_str().map_err(|_| ParseError::Header)?;\n\n        result.extend(\n            s.split(',')\n                .filter_map(|x| match x.trim() {\n                    \"\" => None,\n                    y => Some(y),\n                })\n                .filter_map(|x| x.trim().parse().ok()),\n        )\n    }\n\n    Ok(result)\n}\n\n/// Reads a single string when parsing a header.\n#[inline]\npub fn from_one_raw_str<T: FromStr>(val: Option<&HeaderValue>) -> Result<T, ParseError> {\n    if let Some(line) = val {\n        let line = line.to_str().map_err(|_| ParseError::Header)?;\n\n        if !line.is_empty() {\n            return T::from_str(line).or(Err(ParseError::Header));\n        }\n    }\n\n    Err(ParseError::Header)\n}\n\n/// Format an array into a comma-delimited string.\n#[inline]\npub fn fmt_comma_delimited<T>(f: &mut fmt::Formatter<'_>, parts: &[T]) -> fmt::Result\nwhere\n    T: fmt::Display,\n{\n    let mut iter = parts.iter();\n\n    if let Some(part) = iter.next() {\n        fmt::Display::fmt(part, f)?;\n    }\n\n    for part in iter {\n        f.write_str(\", \")?;\n        fmt::Display::fmt(part, f)?;\n    }\n\n    Ok(())\n}\n\n/// Percent encode a sequence of bytes with a character set defined in [RFC 5987 §3.2].\n///\n/// [RFC 5987 §3.2]: https://datatracker.ietf.org/doc/html/rfc5987#section-3.2\n#[inline]\npub fn http_percent_encode(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {\n    let encoded = percent_encoding::percent_encode(bytes, HTTP_VALUE);\n    fmt::Display::fmt(&encoded, f)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn comma_delimited_parsing() {\n        let headers = [];\n        let res: Vec<usize> = from_comma_delimited(headers.iter()).unwrap();\n        assert_eq!(res, vec![0; 0]);\n\n        let headers = [\n            HeaderValue::from_static(\"1, 2\"),\n            HeaderValue::from_static(\"3,4\"),\n        ];\n        let res: Vec<usize> = from_comma_delimited(headers.iter()).unwrap();\n        assert_eq!(res, vec![1, 2, 3, 4]);\n\n        let headers = [\n            HeaderValue::from_static(\"\"),\n            HeaderValue::from_static(\",\"),\n            HeaderValue::from_static(\"  \"),\n            HeaderValue::from_static(\"1    ,\"),\n            HeaderValue::from_static(\"\"),\n        ];\n        let res: Vec<usize> = from_comma_delimited(headers.iter()).unwrap();\n        assert_eq!(res, vec![1]);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/helpers.rs",
    "content": "use std::io;\n\nuse bytes::BufMut;\nuse http::Version;\n\nconst DIGITS_START: u8 = b'0';\n\npub(crate) fn write_status_line<B: BufMut>(version: Version, n: u16, buf: &mut B) {\n    match version {\n        Version::HTTP_11 => buf.put_slice(b\"HTTP/1.1 \"),\n        Version::HTTP_10 => buf.put_slice(b\"HTTP/1.0 \"),\n        Version::HTTP_09 => buf.put_slice(b\"HTTP/0.9 \"),\n        _ => {\n            // other HTTP version handlers do not use this method\n        }\n    }\n\n    let d100 = (n / 100) as u8;\n    let d10 = ((n / 10) % 10) as u8;\n    let d1 = (n % 10) as u8;\n\n    buf.put_u8(DIGITS_START + d100);\n    buf.put_u8(DIGITS_START + d10);\n    buf.put_u8(DIGITS_START + d1);\n\n    // trailing space before reason\n    buf.put_u8(b' ');\n}\n\n/// Write out content length header.\n///\n/// Buffer must to contain enough space or be implicitly extendable.\npub fn write_content_length<B: BufMut>(n: u64, buf: &mut B, camel_case: bool) {\n    if n == 0 {\n        if camel_case {\n            buf.put_slice(b\"\\r\\nContent-Length: 0\\r\\n\");\n        } else {\n            buf.put_slice(b\"\\r\\ncontent-length: 0\\r\\n\");\n        }\n\n        return;\n    }\n\n    let mut buffer = itoa::Buffer::new();\n\n    if camel_case {\n        buf.put_slice(b\"\\r\\nContent-Length: \");\n    } else {\n        buf.put_slice(b\"\\r\\ncontent-length: \");\n    }\n\n    buf.put_slice(buffer.format(n).as_bytes());\n    buf.put_slice(b\"\\r\\n\");\n}\n\n/// An `io::Write`r that only requires mutable reference and assumes that there is space available\n/// in the buffer for every write operation or that it can be extended implicitly (like\n/// `bytes::BytesMut`, for example).\n///\n/// This is slightly faster (~10%) than `bytes::buf::Writer` in such cases because it does not\n/// perform a remaining length check before writing.\npub(crate) struct MutWriter<'a, B>(pub(crate) &'a mut B);\n\nimpl<B> io::Write for MutWriter<'_, B>\nwhere\n    B: BufMut,\n{\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.0.put_slice(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::str::from_utf8;\n\n    use bytes::BytesMut;\n\n    use super::*;\n\n    #[test]\n    fn test_status_line() {\n        let mut bytes = BytesMut::new();\n        bytes.reserve(50);\n        write_status_line(Version::HTTP_11, 200, &mut bytes);\n        assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), \"HTTP/1.1 200 \");\n\n        let mut bytes = BytesMut::new();\n        bytes.reserve(50);\n        write_status_line(Version::HTTP_09, 404, &mut bytes);\n        assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), \"HTTP/0.9 404 \");\n\n        let mut bytes = BytesMut::new();\n        bytes.reserve(50);\n        write_status_line(Version::HTTP_09, 515, &mut bytes);\n        assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), \"HTTP/0.9 515 \");\n    }\n\n    #[test]\n    fn test_write_content_length() {\n        let mut bytes = BytesMut::new();\n        bytes.reserve(50);\n        write_content_length(0, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 0\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(9, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 9\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(10, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 10\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(99, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 99\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(100, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 100\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(101, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 101\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(998, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 998\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(1000, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 1000\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(1001, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 1001\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(5909, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 5909\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(9999, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 9999\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(10001, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 10001\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(59094, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 59094\\r\\n\"[..]);\n        bytes.reserve(50);\n        write_content_length(99999, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 99999\\r\\n\"[..]);\n\n        bytes.reserve(50);\n        write_content_length(590947, &mut bytes, false);\n        assert_eq!(\n            bytes.split().freeze(),\n            b\"\\r\\ncontent-length: 590947\\r\\n\"[..]\n        );\n        bytes.reserve(50);\n        write_content_length(999999, &mut bytes, false);\n        assert_eq!(\n            bytes.split().freeze(),\n            b\"\\r\\ncontent-length: 999999\\r\\n\"[..]\n        );\n        bytes.reserve(50);\n        write_content_length(5909471, &mut bytes, false);\n        assert_eq!(\n            bytes.split().freeze(),\n            b\"\\r\\ncontent-length: 5909471\\r\\n\"[..]\n        );\n        bytes.reserve(50);\n        write_content_length(59094718, &mut bytes, false);\n        assert_eq!(\n            bytes.split().freeze(),\n            b\"\\r\\ncontent-length: 59094718\\r\\n\"[..]\n        );\n        bytes.reserve(50);\n        write_content_length(4294973728, &mut bytes, false);\n        assert_eq!(\n            bytes.split().freeze(),\n            b\"\\r\\ncontent-length: 4294973728\\r\\n\"[..]\n        );\n    }\n\n    #[test]\n    fn write_content_length_camel_case() {\n        let mut bytes = BytesMut::new();\n        write_content_length(0, &mut bytes, false);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\ncontent-length: 0\\r\\n\"[..]);\n\n        let mut bytes = BytesMut::new();\n        write_content_length(0, &mut bytes, true);\n        assert_eq!(bytes.split().freeze(), b\"\\r\\nContent-Length: 0\\r\\n\"[..]);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/http_message.rs",
    "content": "use std::{\n    cell::{Ref, RefMut},\n    str,\n};\n\nuse encoding_rs::{Encoding, UTF_8};\nuse http::header;\nuse mime::Mime;\n\nuse crate::{\n    error::{ContentTypeError, ParseError},\n    header::{Header, HeaderMap},\n    payload::Payload,\n    Extensions,\n};\n\n/// Trait that implements general purpose operations on HTTP messages.\npub trait HttpMessage: Sized {\n    /// Type of message payload stream\n    type Stream;\n\n    /// Read the message headers.\n    fn headers(&self) -> &HeaderMap;\n\n    /// Message payload stream\n    fn take_payload(&mut self) -> Payload<Self::Stream>;\n\n    /// Returns a reference to the request-local data/extensions container.\n    fn extensions(&self) -> Ref<'_, Extensions>;\n\n    /// Returns a mutable reference to the request-local data/extensions container.\n    fn extensions_mut(&self) -> RefMut<'_, Extensions>;\n\n    /// Get a header.\n    #[doc(hidden)]\n    fn get_header<H: Header>(&self) -> Option<H>\n    where\n        Self: Sized,\n    {\n        if self.headers().contains_key(H::name()) {\n            H::parse(self).ok()\n        } else {\n            None\n        }\n    }\n\n    /// Read the request content type. If request did not contain a *Content-Type* header, an empty\n    /// string is returned.\n    fn content_type(&self) -> &str {\n        if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {\n            if let Ok(content_type) = content_type.to_str() {\n                return content_type.split(';').next().unwrap().trim();\n            }\n        }\n        \"\"\n    }\n\n    /// Get content type encoding.\n    ///\n    /// UTF-8 is used by default, If request charset is not set.\n    fn encoding(&self) -> Result<&'static Encoding, ContentTypeError> {\n        if let Some(mime_type) = self.mime_type()? {\n            if let Some(charset) = mime_type.get_param(\"charset\") {\n                if let Some(enc) = Encoding::for_label_no_replacement(charset.as_str().as_bytes()) {\n                    Ok(enc)\n                } else {\n                    Err(ContentTypeError::UnknownEncoding)\n                }\n            } else {\n                Ok(UTF_8)\n            }\n        } else {\n            Ok(UTF_8)\n        }\n    }\n\n    /// Convert the request content type to a known mime type.\n    fn mime_type(&self) -> Result<Option<Mime>, ContentTypeError> {\n        if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {\n            if let Ok(content_type) = content_type.to_str() {\n                return match content_type.parse() {\n                    Ok(mt) => Ok(Some(mt)),\n                    Err(_) => Err(ContentTypeError::ParseError),\n                };\n            } else {\n                return Err(ContentTypeError::ParseError);\n            }\n        }\n        Ok(None)\n    }\n\n    /// Check if request has chunked transfer encoding.\n    fn chunked(&self) -> Result<bool, ParseError> {\n        if let Some(encodings) = self.headers().get(header::TRANSFER_ENCODING) {\n            if let Ok(s) = encodings.to_str() {\n                Ok(s.to_lowercase().contains(\"chunked\"))\n            } else {\n                Err(ParseError::Header)\n            }\n        } else {\n            Ok(false)\n        }\n    }\n}\n\nimpl<T> HttpMessage for &mut T\nwhere\n    T: HttpMessage,\n{\n    type Stream = T::Stream;\n\n    fn headers(&self) -> &HeaderMap {\n        (**self).headers()\n    }\n\n    /// Message payload stream\n    fn take_payload(&mut self) -> Payload<Self::Stream> {\n        (**self).take_payload()\n    }\n\n    /// Request's extensions container\n    fn extensions(&self) -> Ref<'_, Extensions> {\n        (**self).extensions()\n    }\n\n    /// Mutable reference to a the request's extensions container\n    fn extensions_mut(&self) -> RefMut<'_, Extensions> {\n        (**self).extensions_mut()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::Bytes;\n    use encoding_rs::ISO_8859_2;\n\n    use super::*;\n    use crate::test::TestRequest;\n\n    #[test]\n    fn test_content_type() {\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"text/plain\"))\n            .finish();\n        assert_eq!(req.content_type(), \"text/plain\");\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json; charset=utf-8\"))\n            .finish();\n        assert_eq!(req.content_type(), \"application/json\");\n        let req = TestRequest::default().finish();\n        assert_eq!(req.content_type(), \"\");\n    }\n\n    #[test]\n    fn test_mime_type() {\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json\"))\n            .finish();\n        assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON));\n        let req = TestRequest::default().finish();\n        assert_eq!(req.mime_type().unwrap(), None);\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json; charset=utf-8\"))\n            .finish();\n        let mt = req.mime_type().unwrap().unwrap();\n        assert_eq!(mt.get_param(mime::CHARSET), Some(mime::UTF_8));\n        assert_eq!(mt.type_(), mime::APPLICATION);\n        assert_eq!(mt.subtype(), mime::JSON);\n    }\n\n    #[test]\n    fn test_mime_type_error() {\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"applicationadfadsfasdflknadsfklnadsfjson\"))\n            .finish();\n        assert_eq!(Err(ContentTypeError::ParseError), req.mime_type());\n    }\n\n    #[test]\n    fn test_encoding() {\n        let req = TestRequest::default().finish();\n        assert_eq!(UTF_8.name(), req.encoding().unwrap().name());\n\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json\"))\n            .finish();\n        assert_eq!(UTF_8.name(), req.encoding().unwrap().name());\n\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json; charset=ISO-8859-2\"))\n            .finish();\n        assert_eq!(ISO_8859_2, req.encoding().unwrap());\n    }\n\n    #[test]\n    fn test_encoding_error() {\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"applicatjson\"))\n            .finish();\n        assert_eq!(Some(ContentTypeError::ParseError), req.encoding().err());\n\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"application/json; charset=kkkttktk\"))\n            .finish();\n        assert_eq!(\n            Some(ContentTypeError::UnknownEncoding),\n            req.encoding().err()\n        );\n    }\n\n    #[test]\n    fn test_chunked() {\n        let req = TestRequest::default().finish();\n        assert!(!req.chunked().unwrap());\n\n        let req = TestRequest::default()\n            .insert_header((header::TRANSFER_ENCODING, \"chunked\"))\n            .finish();\n        assert!(req.chunked().unwrap());\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::TRANSFER_ENCODING,\n                Bytes::from_static(b\"some va\\xadscc\\xacas0xsdasdlue\"),\n            ))\n            .finish();\n        assert!(req.chunked().is_err());\n    }\n}\n"
  },
  {
    "path": "actix-http/src/keep_alive.rs",
    "content": "use std::time::Duration;\n\n/// Connection keep-alive config.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum KeepAlive {\n    /// Keep-alive duration.\n    ///\n    /// `KeepAlive::Timeout(Duration::ZERO)` is mapped to `KeepAlive::Disabled`.\n    Timeout(Duration),\n\n    /// Rely on OS to shutdown TCP connection.\n    ///\n    /// Some defaults can be very long, check your OS documentation.\n    Os,\n\n    /// Keep-alive is disabled.\n    ///\n    /// Connections will be closed immediately.\n    Disabled,\n}\n\nimpl KeepAlive {\n    pub(crate) fn enabled(&self) -> bool {\n        !matches!(self, Self::Disabled)\n    }\n\n    #[allow(unused)] // used with `http2` feature flag\n    pub(crate) fn duration(&self) -> Option<Duration> {\n        match self {\n            KeepAlive::Timeout(dur) => Some(*dur),\n            _ => None,\n        }\n    }\n\n    /// Map zero duration to disabled.\n    pub(crate) fn normalize(self) -> KeepAlive {\n        match self {\n            KeepAlive::Timeout(Duration::ZERO) => KeepAlive::Disabled,\n            ka => ka,\n        }\n    }\n}\n\nimpl Default for KeepAlive {\n    fn default() -> Self {\n        Self::Timeout(Duration::from_secs(5))\n    }\n}\n\nimpl From<Duration> for KeepAlive {\n    fn from(dur: Duration) -> Self {\n        KeepAlive::Timeout(dur).normalize()\n    }\n}\n\nimpl From<Option<Duration>> for KeepAlive {\n    fn from(ka_dur: Option<Duration>) -> Self {\n        match ka_dur {\n            Some(dur) => KeepAlive::from(dur),\n            None => KeepAlive::Disabled,\n        }\n        .normalize()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn from_impls() {\n        let test: KeepAlive = Duration::from_secs(1).into();\n        assert_eq!(test, KeepAlive::Timeout(Duration::from_secs(1)));\n\n        let test: KeepAlive = Duration::from_secs(0).into();\n        assert_eq!(test, KeepAlive::Disabled);\n\n        let test: KeepAlive = Some(Duration::from_secs(0)).into();\n        assert_eq!(test, KeepAlive::Disabled);\n\n        let test: KeepAlive = None.into();\n        assert_eq!(test, KeepAlive::Disabled);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/lib.rs",
    "content": "//! HTTP types and services for the Actix ecosystem.\n//!\n//! ## Crate Features\n//!\n//! | Feature             | Functionality                               |\n//! | ------------------- | ------------------------------------------- |\n//! | `http2`             | HTTP/2 support via [h2].                    |\n//! | `openssl`           | TLS support via [OpenSSL].                  |\n//! | `rustls-0_20`       | TLS support via rustls 0.20.                |\n//! | `rustls-0_21`       | TLS support via rustls 0.21.                |\n//! | `rustls-0_22`       | TLS support via rustls 0.22.                |\n//! | `rustls-0_23`       | TLS support via [rustls] 0.23.              |\n//! | `compress-brotli`   | Payload compression support: Brotli.        |\n//! | `compress-gzip`     | Payload compression support: Deflate, Gzip. |\n//! | `compress-zstd`     | Payload compression support: Zstd.          |\n//! | `trust-dns`         | Use [trust-dns] as the client DNS resolver. |\n//!\n//! [h2]: https://crates.io/crates/h2\n//! [OpenSSL]: https://crates.io/crates/openssl\n//! [rustls]: https://crates.io/crates/rustls\n//! [trust-dns]: https://crates.io/crates/trust-dns\n\n#![allow(\n    clippy::type_complexity,\n    clippy::too_many_arguments,\n    clippy::borrow_interior_mutable_const\n)]\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\npub use http::{uri, uri::Uri, Method, StatusCode, Version};\n\npub mod body;\nmod builder;\nmod config;\nmod date;\n#[cfg(feature = \"__compress\")]\npub mod encoding;\npub mod error;\nmod extensions;\npub mod h1;\n#[cfg(feature = \"http2\")]\npub mod h2;\npub mod header;\nmod helpers;\nmod http_message;\nmod keep_alive;\nmod message;\n#[cfg(test)]\nmod notify_on_drop;\nmod payload;\nmod requests;\nmod responses;\nmod service;\npub mod test;\n#[cfg(feature = \"ws\")]\npub mod ws;\n\n#[allow(deprecated)]\npub use self::payload::PayloadStream;\n#[cfg(feature = \"__tls\")]\npub use self::service::TlsAcceptorConfig;\npub use self::{\n    builder::HttpServiceBuilder,\n    config::{ServiceConfig, ServiceConfigBuilder},\n    error::Error,\n    extensions::Extensions,\n    header::ContentEncoding,\n    http_message::HttpMessage,\n    keep_alive::KeepAlive,\n    message::{ConnectionType, Message},\n    payload::{BoxedPayloadStream, Payload},\n    requests::{Request, RequestHead, RequestHeadType},\n    responses::{Response, ResponseBuilder, ResponseHead},\n    service::HttpService,\n};\n\n/// A major HTTP protocol version.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\n#[non_exhaustive]\npub enum Protocol {\n    Http1,\n    Http2,\n    Http3,\n}\n\ntype ConnectCallback<IO> = dyn Fn(&IO, &mut Extensions);\n\n/// Container for data that extract with ConnectCallback.\n///\n/// # Implementation Details\n/// Uses Option to reduce necessary allocations when merging with request extensions.\n#[derive(Default)]\npub(crate) struct OnConnectData(Option<Extensions>);\n\nimpl OnConnectData {\n    /// Construct by calling the on-connect callback with the underlying transport I/O.\n    pub(crate) fn from_io<T>(io: &T, on_connect_ext: Option<&ConnectCallback<T>>) -> Self {\n        let ext = on_connect_ext.map(|handler| {\n            let mut extensions = Extensions::default();\n            handler(io, &mut extensions);\n            extensions\n        });\n\n        Self(ext)\n    }\n}\n"
  },
  {
    "path": "actix-http/src/message.rs",
    "content": "use std::{cell::RefCell, ops, rc::Rc};\n\nuse bitflags::bitflags;\n\n/// Represents various types of connection\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ConnectionType {\n    /// Close connection after response.\n    Close,\n\n    /// Keep connection alive after response.\n    KeepAlive,\n\n    /// Connection is upgraded to different type.\n    Upgrade,\n}\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    pub(crate) struct Flags: u8 {\n        const CLOSE       = 0b0000_0001;\n        const KEEP_ALIVE  = 0b0000_0010;\n        const UPGRADE     = 0b0000_0100;\n        const EXPECT      = 0b0000_1000;\n        const NO_CHUNKING = 0b0001_0000;\n        const CAMEL_CASE  = 0b0010_0000;\n    }\n}\n\n#[doc(hidden)]\npub trait Head: Default + 'static {\n    fn clear(&mut self);\n\n    fn with_pool<F, R>(f: F) -> R\n    where\n        F: FnOnce(&MessagePool<Self>) -> R;\n}\n\npub struct Message<T: Head> {\n    /// Rc here should not be cloned by anyone.\n    /// It's used to reuse allocation of T and no shared ownership is allowed.\n    head: Rc<T>,\n}\n\nimpl<T: Head> Message<T> {\n    /// Get new message from the pool of objects\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        T::with_pool(MessagePool::get_message)\n    }\n}\n\nimpl<T: Head> ops::Deref for Message<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.head.as_ref()\n    }\n}\n\nimpl<T: Head> ops::DerefMut for Message<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        Rc::get_mut(&mut self.head).expect(\"Multiple copies exist\")\n    }\n}\n\nimpl<T: Head> Drop for Message<T> {\n    fn drop(&mut self) {\n        T::with_pool(|p| p.release(Rc::clone(&self.head)))\n    }\n}\n\n/// Generic `Head` object pool.\n#[doc(hidden)]\npub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>);\n\nimpl<T: Head> MessagePool<T> {\n    pub(crate) fn create() -> MessagePool<T> {\n        MessagePool(RefCell::new(Vec::with_capacity(128)))\n    }\n\n    /// Get message from the pool\n    #[inline]\n    fn get_message(&self) -> Message<T> {\n        if let Some(mut msg) = self.0.borrow_mut().pop() {\n            // Message is put in pool only when it's the last copy.\n            // which means it's guaranteed to be unique when popped out.\n            Rc::get_mut(&mut msg)\n                .expect(\"Multiple copies exist\")\n                .clear();\n            Message { head: msg }\n        } else {\n            Message {\n                head: Rc::new(T::default()),\n            }\n        }\n    }\n\n    #[inline]\n    /// Release message instance\n    fn release(&self, msg: Rc<T>) {\n        let pool = &mut self.0.borrow_mut();\n        if pool.len() < 128 {\n            pool.push(msg);\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/notify_on_drop.rs",
    "content": "/// Test Module for checking the drop state of certain async tasks that are spawned\n/// with `actix_rt::spawn`\n///\n/// The target task must explicitly generate `NotifyOnDrop` when spawn the task\nuse std::cell::RefCell;\n\nthread_local! {\n    static NOTIFY_DROPPED: RefCell<Option<bool>> = const { RefCell::new(None) };\n}\n\n/// Check if the spawned task is dropped.\n///\n/// # Panics\n/// Panics when there was no `NotifyOnDrop` instance on current thread.\npub(crate) fn is_dropped() -> bool {\n    NOTIFY_DROPPED.with(|bool| {\n        bool.borrow()\n            .expect(\"No NotifyOnDrop existed on current thread\")\n    })\n}\n\npub(crate) struct NotifyOnDrop;\n\nimpl NotifyOnDrop {\n    /// # Panics\n    /// Panics hen construct multiple instances on any given thread.\n    pub(crate) fn new() -> Self {\n        NOTIFY_DROPPED.with(|bool| {\n            let mut bool = bool.borrow_mut();\n            if bool.is_some() {\n                panic!(\"NotifyOnDrop existed on current thread\");\n            } else {\n                *bool = Some(false);\n            }\n        });\n\n        NotifyOnDrop\n    }\n}\n\nimpl Drop for NotifyOnDrop {\n    fn drop(&mut self) {\n        NOTIFY_DROPPED.with(|bool| {\n            if let Some(b) = bool.borrow_mut().as_mut() {\n                *b = true;\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "actix-http/src/payload.rs",
    "content": "use std::{\n    mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse pin_project_lite::pin_project;\n\nuse crate::error::PayloadError;\n\n/// A boxed payload stream.\npub type BoxedPayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>;\n\n#[doc(hidden)]\n#[deprecated(since = \"3.0.0\", note = \"Renamed to `BoxedPayloadStream`.\")]\npub type PayloadStream = BoxedPayloadStream;\n\n#[cfg(not(feature = \"http2\"))]\npin_project! {\n    /// A streaming payload.\n    #[project = PayloadProj]\n    pub enum Payload<S = BoxedPayloadStream> {\n        None,\n        H1 { payload: crate::h1::Payload },\n        Stream { #[pin] payload: S },\n    }\n}\n\n#[cfg(feature = \"http2\")]\npin_project! {\n    /// A streaming payload.\n    #[project = PayloadProj]\n    pub enum Payload<S = BoxedPayloadStream> {\n        None,\n        H1 { payload: crate::h1::Payload },\n        H2 { payload: crate::h2::Payload },\n        Stream { #[pin] payload: S },\n    }\n}\n\nimpl<S> From<crate::h1::Payload> for Payload<S> {\n    #[inline]\n    fn from(payload: crate::h1::Payload) -> Self {\n        Payload::H1 { payload }\n    }\n}\n\nimpl<S> From<Bytes> for Payload<S> {\n    #[inline]\n    fn from(bytes: Bytes) -> Self {\n        let (_, mut pl) = crate::h1::Payload::create(true);\n        pl.unread_data(bytes);\n        self::Payload::from(pl)\n    }\n}\n\nimpl<S> From<Vec<u8>> for Payload<S> {\n    #[inline]\n    fn from(vec: Vec<u8>) -> Self {\n        Payload::from(Bytes::from(vec))\n    }\n}\n\n#[cfg(feature = \"http2\")]\nimpl<S> From<crate::h2::Payload> for Payload<S> {\n    #[inline]\n    fn from(payload: crate::h2::Payload) -> Self {\n        Payload::H2 { payload }\n    }\n}\n\n#[cfg(feature = \"http2\")]\nimpl<S> From<::h2::RecvStream> for Payload<S> {\n    #[inline]\n    fn from(stream: ::h2::RecvStream) -> Self {\n        Payload::H2 {\n            payload: crate::h2::Payload::new(stream),\n        }\n    }\n}\n\nimpl From<BoxedPayloadStream> for Payload {\n    #[inline]\n    fn from(payload: BoxedPayloadStream) -> Self {\n        Payload::Stream { payload }\n    }\n}\n\nimpl<S> Payload<S> {\n    /// Takes current payload and replaces it with `None` value.\n    #[must_use]\n    pub fn take(&mut self) -> Payload<S> {\n        mem::replace(self, Payload::None)\n    }\n}\n\nimpl<S> Stream for Payload<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    type Item = Result<Bytes, PayloadError>;\n\n    #[inline]\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        match self.project() {\n            PayloadProj::None => Poll::Ready(None),\n            PayloadProj::H1 { payload } => Pin::new(payload).poll_next(cx),\n\n            #[cfg(feature = \"http2\")]\n            PayloadProj::H2 { payload } => Pin::new(payload).poll_next(cx),\n\n            PayloadProj::Stream { payload } => payload.poll_next(cx),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n\n    use super::*;\n\n    assert_impl_all!(Payload: Unpin);\n    assert_not_impl_any!(Payload: Send, Sync);\n}\n"
  },
  {
    "path": "actix-http/src/requests/head.rs",
    "content": "use std::{net, rc::Rc};\n\nuse crate::{\n    header::{self, HeaderMap},\n    message::{Flags, Head, MessagePool},\n    ConnectionType, Method, Uri, Version,\n};\n\nthread_local! {\n    static REQUEST_POOL: MessagePool<RequestHead> = MessagePool::<RequestHead>::create()\n}\n\n#[derive(Debug, Clone)]\npub struct RequestHead {\n    pub method: Method,\n    pub uri: Uri,\n    pub version: Version,\n    pub headers: HeaderMap,\n\n    /// Will only be None when called in unit tests unless set manually.\n    pub peer_addr: Option<net::SocketAddr>,\n\n    flags: Flags,\n}\n\nimpl Default for RequestHead {\n    fn default() -> RequestHead {\n        RequestHead {\n            method: Method::default(),\n            uri: Uri::default(),\n            version: Version::HTTP_11,\n            headers: HeaderMap::with_capacity(16),\n            peer_addr: None,\n            flags: Flags::empty(),\n        }\n    }\n}\n\nimpl Head for RequestHead {\n    fn clear(&mut self) {\n        self.flags = Flags::empty();\n        self.headers.clear();\n    }\n\n    fn with_pool<F, R>(f: F) -> R\n    where\n        F: FnOnce(&MessagePool<Self>) -> R,\n    {\n        REQUEST_POOL.with(|p| f(p))\n    }\n}\n\nimpl RequestHead {\n    /// Read the message headers.\n    pub fn headers(&self) -> &HeaderMap {\n        &self.headers\n    }\n\n    /// Mutable reference to the message headers.\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.headers\n    }\n\n    /// Returns whether headers should be sent in Camel-Case.\n    ///\n    /// Default is `false`.\n    #[inline]\n    pub fn camel_case_headers(&self) -> bool {\n        self.flags.contains(Flags::CAMEL_CASE)\n    }\n\n    /// Sets whether to send headers formatted as Camel-Case.\n    #[inline]\n    pub fn set_camel_case_headers(&mut self, val: bool) {\n        if val {\n            self.flags.insert(Flags::CAMEL_CASE);\n        } else {\n            self.flags.remove(Flags::CAMEL_CASE);\n        }\n    }\n\n    #[inline]\n    /// Set connection type of the message\n    pub fn set_connection_type(&mut self, ctype: ConnectionType) {\n        match ctype {\n            ConnectionType::Close => self.flags.insert(Flags::CLOSE),\n            ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),\n            ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),\n        }\n    }\n\n    #[inline]\n    /// Connection type\n    pub fn connection_type(&self) -> ConnectionType {\n        if self.flags.contains(Flags::CLOSE) {\n            ConnectionType::Close\n        } else if self.flags.contains(Flags::KEEP_ALIVE) {\n            ConnectionType::KeepAlive\n        } else if self.flags.contains(Flags::UPGRADE) {\n            ConnectionType::Upgrade\n        } else if self.version < Version::HTTP_11 {\n            ConnectionType::Close\n        } else {\n            ConnectionType::KeepAlive\n        }\n    }\n\n    /// Connection upgrade status\n    pub fn upgrade(&self) -> bool {\n        self.headers()\n            .get(header::CONNECTION)\n            .map(|hdr| {\n                if let Ok(s) = hdr.to_str() {\n                    s.to_ascii_lowercase().contains(\"upgrade\")\n                } else {\n                    false\n                }\n            })\n            .unwrap_or(false)\n    }\n\n    #[inline]\n    /// Get response body chunking state\n    pub fn chunked(&self) -> bool {\n        !self.flags.contains(Flags::NO_CHUNKING)\n    }\n\n    #[inline]\n    pub fn no_chunking(&mut self, val: bool) {\n        if val {\n            self.flags.insert(Flags::NO_CHUNKING);\n        } else {\n            self.flags.remove(Flags::NO_CHUNKING);\n        }\n    }\n\n    /// Request contains `EXPECT` header.\n    #[inline]\n    pub fn expect(&self) -> bool {\n        self.flags.contains(Flags::EXPECT)\n    }\n\n    #[inline]\n    pub(crate) fn set_expect(&mut self) {\n        self.flags.insert(Flags::EXPECT);\n    }\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug)]\npub enum RequestHeadType {\n    Owned(RequestHead),\n    Rc(Rc<RequestHead>, Option<HeaderMap>),\n}\n\nimpl RequestHeadType {\n    pub fn extra_headers(&self) -> Option<&HeaderMap> {\n        match self {\n            RequestHeadType::Owned(_) => None,\n            RequestHeadType::Rc(_, headers) => headers.as_ref(),\n        }\n    }\n}\n\nimpl AsRef<RequestHead> for RequestHeadType {\n    fn as_ref(&self) -> &RequestHead {\n        match self {\n            RequestHeadType::Owned(head) => head,\n            RequestHeadType::Rc(head, _) => head.as_ref(),\n        }\n    }\n}\n\nimpl From<RequestHead> for RequestHeadType {\n    fn from(head: RequestHead) -> Self {\n        RequestHeadType::Owned(head)\n    }\n}\n"
  },
  {
    "path": "actix-http/src/requests/mod.rs",
    "content": "//! HTTP requests.\n\nmod head;\nmod request;\n\npub use self::{\n    head::{RequestHead, RequestHeadType},\n    request::Request,\n};\n"
  },
  {
    "path": "actix-http/src/requests/request.rs",
    "content": "//! HTTP requests.\n\nuse std::{\n    cell::{Ref, RefCell, RefMut},\n    fmt, mem, net,\n    rc::Rc,\n    str,\n};\n\nuse http::{header, Method, Uri, Version};\n\nuse crate::{\n    header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Message, Payload, RequestHead,\n};\n\n/// An HTTP request.\npub struct Request<P = BoxedPayloadStream> {\n    pub(crate) payload: Payload<P>,\n    pub(crate) head: Message<RequestHead>,\n    pub(crate) conn_data: Option<Rc<Extensions>>,\n    pub(crate) extensions: RefCell<Extensions>,\n}\n\nimpl<P> HttpMessage for Request<P> {\n    type Stream = P;\n\n    #[inline]\n    fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    fn take_payload(&mut self) -> Payload<P> {\n        mem::replace(&mut self.payload, Payload::None)\n    }\n\n    #[inline]\n    fn extensions(&self) -> Ref<'_, Extensions> {\n        self.extensions.borrow()\n    }\n\n    #[inline]\n    fn extensions_mut(&self) -> RefMut<'_, Extensions> {\n        self.extensions.borrow_mut()\n    }\n}\n\nimpl From<Message<RequestHead>> for Request<BoxedPayloadStream> {\n    fn from(head: Message<RequestHead>) -> Self {\n        Request {\n            head,\n            payload: Payload::None,\n            extensions: RefCell::new(Extensions::default()),\n            conn_data: None,\n        }\n    }\n}\n\nimpl Request<BoxedPayloadStream> {\n    /// Create new Request instance\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Request<BoxedPayloadStream> {\n        Request {\n            head: Message::new(),\n            payload: Payload::None,\n            extensions: RefCell::new(Extensions::default()),\n            conn_data: None,\n        }\n    }\n}\n\nimpl<P> Request<P> {\n    /// Create new Request instance\n    pub fn with_payload(payload: Payload<P>) -> Request<P> {\n        Request {\n            payload,\n            head: Message::new(),\n            extensions: RefCell::new(Extensions::default()),\n            conn_data: None,\n        }\n    }\n\n    /// Create new Request instance\n    pub fn replace_payload<P1>(self, payload: Payload<P1>) -> (Request<P1>, Payload<P>) {\n        let pl = self.payload;\n\n        (\n            Request {\n                payload,\n                head: self.head,\n                extensions: self.extensions,\n                conn_data: self.conn_data,\n            },\n            pl,\n        )\n    }\n\n    /// Get request's payload\n    pub fn payload(&mut self) -> &mut Payload<P> {\n        &mut self.payload\n    }\n\n    /// Get request's payload\n    pub fn take_payload(&mut self) -> Payload<P> {\n        mem::replace(&mut self.payload, Payload::None)\n    }\n\n    /// Split request into request head and payload\n    pub fn into_parts(self) -> (Message<RequestHead>, Payload<P>) {\n        (self.head, self.payload)\n    }\n\n    #[inline]\n    /// Http message part of the request\n    pub fn head(&self) -> &RequestHead {\n        &self.head\n    }\n\n    #[inline]\n    #[doc(hidden)]\n    /// Mutable reference to a HTTP message part of the request\n    pub fn head_mut(&mut self) -> &mut RequestHead {\n        &mut self.head\n    }\n\n    /// Mutable reference to the message's headers.\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.head.headers\n    }\n\n    /// Request's uri.\n    #[inline]\n    pub fn uri(&self) -> &Uri {\n        &self.head().uri\n    }\n\n    /// Mutable reference to the request's uri.\n    #[inline]\n    pub fn uri_mut(&mut self) -> &mut Uri {\n        &mut self.head.uri\n    }\n\n    /// Read the Request method.\n    #[inline]\n    pub fn method(&self) -> &Method {\n        &self.head().method\n    }\n\n    /// Read the Request Version.\n    #[inline]\n    pub fn version(&self) -> Version {\n        self.head().version\n    }\n\n    /// The target path of this Request.\n    #[inline]\n    pub fn path(&self) -> &str {\n        self.head().uri.path()\n    }\n\n    /// Check if request requires connection upgrade\n    #[inline]\n    pub fn upgrade(&self) -> bool {\n        if let Some(conn) = self.head().headers.get(header::CONNECTION) {\n            if let Ok(s) = conn.to_str() {\n                return s.to_lowercase().contains(\"upgrade\");\n            }\n        }\n        self.head().method == Method::CONNECT\n    }\n\n    /// Peer socket address.\n    ///\n    /// Peer address is the directly connected peer's socket address. If a proxy is used in front of\n    /// the Actix Web server, then it would be address of this proxy.\n    ///\n    /// Will only return None when called in unit tests unless set manually.\n    #[inline]\n    pub fn peer_addr(&self) -> Option<net::SocketAddr> {\n        self.head().peer_addr\n    }\n\n    /// Returns a reference a piece of connection data set in an [on-connect] callback.\n    ///\n    /// ```ignore\n    /// let opt_t = req.conn_data::<PeerCertificate>();\n    /// ```\n    ///\n    /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext\n    pub fn conn_data<T: 'static>(&self) -> Option<&T> {\n        self.conn_data\n            .as_deref()\n            .and_then(|container| container.get::<T>())\n    }\n\n    /// Returns the connection-level data/extensions container if an [on-connect] callback was\n    /// registered, leaving an empty one in its place.\n    ///\n    /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext\n    pub fn take_conn_data(&mut self) -> Option<Rc<Extensions>> {\n        self.conn_data.take()\n    }\n\n    /// Returns the request-local data/extensions container, leaving an empty one in its place.\n    pub fn take_req_data(&mut self) -> Extensions {\n        mem::take(self.extensions.get_mut())\n    }\n}\n\nimpl<P> fmt::Debug for Request<P> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(\n            f,\n            \"\\nRequest {:?} {}:{}\",\n            self.version(),\n            self.method(),\n            self.path()\n        )?;\n\n        if let Some(q) = self.uri().query().as_ref() {\n            writeln!(f, \"  query: ?{:?}\", q)?;\n        }\n\n        writeln!(f, \"  headers:\")?;\n\n        for (key, val) in self.headers().iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_basics() {\n        let msg = Message::new();\n        let mut req = Request::from(msg);\n        req.headers_mut().insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\"text/plain\"),\n        );\n        assert!(req.headers().contains_key(header::CONTENT_TYPE));\n\n        *req.uri_mut() = Uri::try_from(\"/index.html?q=1\").unwrap();\n        assert_eq!(req.uri().path(), \"/index.html\");\n        assert_eq!(req.uri().query(), Some(\"q=1\"));\n\n        let s = format!(\"{:?}\", req);\n        assert!(s.contains(\"Request HTTP/1.1 GET:/index.html\"));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/responses/builder.rs",
    "content": "//! HTTP response builder.\n\nuse std::{cell::RefCell, fmt, str};\n\nuse crate::{\n    body::{EitherBody, MessageBody},\n    error::{Error, HttpError},\n    header::{self, TryIntoHeaderPair, TryIntoHeaderValue},\n    responses::{BoxedResponseHead, ResponseHead},\n    ConnectionType, Extensions, Response, StatusCode,\n};\n\n/// An HTTP response builder.\n///\n/// Used to construct an instance of `Response` using a builder pattern. Response builders are often\n/// created using [`Response::build`].\n///\n/// # Examples\n/// ```\n/// use actix_http::{Response, ResponseBuilder, StatusCode, body, header};\n///\n/// # actix_rt::System::new().block_on(async {\n/// let mut res: Response<_> = Response::build(StatusCode::OK)\n///     .content_type(mime::APPLICATION_JSON)\n///     .insert_header((header::SERVER, \"my-app/1.0\"))\n///     .append_header((header::SET_COOKIE, \"a=1\"))\n///     .append_header((header::SET_COOKIE, \"b=2\"))\n///     .body(\"1234\");\n///\n/// assert_eq!(res.status(), StatusCode::OK);\n///\n/// assert!(res.headers().contains_key(\"server\"));\n/// assert_eq!(res.headers().get_all(\"set-cookie\").count(), 2);\n///\n/// assert_eq!(body::to_bytes(res.into_body()).await.unwrap(), &b\"1234\"[..]);\n/// # })\n/// ```\npub struct ResponseBuilder {\n    head: Option<BoxedResponseHead>,\n    err: Option<HttpError>,\n}\n\nimpl ResponseBuilder {\n    /// Create response builder\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_http::{Response, ResponseBuilder, StatusCode};\n    /// let res: Response<_> = ResponseBuilder::default().finish();\n    /// assert_eq!(res.status(), StatusCode::OK);\n    /// ```\n    #[inline]\n    pub fn new(status: StatusCode) -> Self {\n        ResponseBuilder {\n            head: Some(BoxedResponseHead::new(status)),\n            err: None,\n        }\n    }\n\n    /// Set HTTP status code of this response.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_http::{ResponseBuilder, StatusCode};\n    /// let res = ResponseBuilder::default().status(StatusCode::NOT_FOUND).finish();\n    /// assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    /// ```\n    #[inline]\n    pub fn status(&mut self, status: StatusCode) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.status = status;\n        }\n        self\n    }\n\n    /// Insert a header, replacing any that were set with an equivalent field name.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_http::{ResponseBuilder, header};\n    ///\n    /// let res = ResponseBuilder::default()\n    ///     .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))\n    ///     .insert_header((\"X-TEST\", \"value\"))\n    ///     .finish();\n    ///\n    /// assert!(res.headers().contains_key(\"content-type\"));\n    /// assert!(res.headers().contains_key(\"x-test\"));\n    /// ```\n    pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => {\n                    parts.headers.insert(key, value);\n                }\n                Err(err) => self.err = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    /// Append a header, keeping any that were set with an equivalent field name.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_http::{ResponseBuilder, header};\n    ///\n    /// let res = ResponseBuilder::default()\n    ///     .append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))\n    ///     .append_header((\"X-TEST\", \"value1\"))\n    ///     .append_header((\"X-TEST\", \"value2\"))\n    ///     .finish();\n    ///\n    /// assert_eq!(res.headers().get_all(\"content-type\").count(), 1);\n    /// assert_eq!(res.headers().get_all(\"x-test\").count(), 2);\n    /// ```\n    pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => parts.headers.append(key, value),\n                Err(err) => self.err = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    /// Set the custom reason for the response.\n    #[inline]\n    pub fn reason(&mut self, reason: &'static str) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.reason = Some(reason);\n        }\n        self\n    }\n\n    /// Set connection type to KeepAlive\n    #[inline]\n    pub fn keep_alive(&mut self) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::KeepAlive);\n        }\n        self\n    }\n\n    /// Set connection type to `Upgrade`.\n    #[inline]\n    pub fn upgrade<V>(&mut self, value: V) -> &mut Self\n    where\n        V: TryIntoHeaderValue,\n    {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::Upgrade);\n        }\n\n        if let Ok(value) = value.try_into_value() {\n            self.insert_header((header::UPGRADE, value));\n        }\n\n        self\n    }\n\n    /// Force-close connection, even if it is marked as keep-alive.\n    #[inline]\n    pub fn force_close(&mut self) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::Close);\n        }\n        self\n    }\n\n    /// Disable chunked transfer encoding for HTTP/1.1 streaming responses.\n    #[inline]\n    pub fn no_chunking(&mut self, len: u64) -> &mut Self {\n        let mut buf = itoa::Buffer::new();\n        self.insert_header((header::CONTENT_LENGTH, buf.format(len)));\n\n        if let Some(parts) = self.inner() {\n            parts.no_chunking(true);\n        }\n        self\n    }\n\n    /// Set response content type.\n    #[inline]\n    pub fn content_type<V>(&mut self, value: V) -> &mut Self\n    where\n        V: TryIntoHeaderValue,\n    {\n        if let Some(parts) = self.inner() {\n            match value.try_into_value() {\n                Ok(value) => {\n                    parts.headers.insert(header::CONTENT_TYPE, value);\n                }\n                Err(err) => self.err = Some(err.into()),\n            };\n        }\n        self\n    }\n\n    /// Generate response with a wrapped body.\n    ///\n    /// This `ResponseBuilder` will be left in a useless state.\n    pub fn body<B>(&mut self, body: B) -> Response<EitherBody<B>>\n    where\n        B: MessageBody + 'static,\n    {\n        match self.message_body(body) {\n            Ok(res) => res.map_body(|_, body| EitherBody::left(body)),\n            Err(err) => Response::from(err).map_body(|_, body| EitherBody::right(body)),\n        }\n    }\n\n    /// Generate response with a body.\n    ///\n    /// This `ResponseBuilder` will be left in a useless state.\n    pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {\n        if let Some(err) = self.err.take() {\n            return Err(Error::new_http().with_cause(err));\n        }\n\n        let head = self.head.take().expect(\"cannot reuse response builder\");\n\n        Ok(Response {\n            head,\n            body,\n            extensions: RefCell::new(Extensions::new()),\n        })\n    }\n\n    /// Generate response with an empty body.\n    ///\n    /// This `ResponseBuilder` will be left in a useless state.\n    #[inline]\n    pub fn finish(&mut self) -> Response<EitherBody<()>> {\n        self.body(())\n    }\n\n    /// Create an owned `ResponseBuilder`, leaving the original in a useless state.\n    pub fn take(&mut self) -> ResponseBuilder {\n        ResponseBuilder {\n            head: self.head.take(),\n            err: self.err.take(),\n        }\n    }\n\n    /// Get access to the inner response head if there has been no error.\n    fn inner(&mut self) -> Option<&mut ResponseHead> {\n        if self.err.is_some() {\n            return None;\n        }\n\n        self.head.as_deref_mut()\n    }\n}\n\nimpl Default for ResponseBuilder {\n    fn default() -> Self {\n        Self::new(StatusCode::OK)\n    }\n}\n\n/// Convert `Response` to a `ResponseBuilder`. Body get dropped.\nimpl<B> From<Response<B>> for ResponseBuilder {\n    fn from(res: Response<B>) -> ResponseBuilder {\n        ResponseBuilder {\n            head: Some(res.head),\n            err: None,\n        }\n    }\n}\n\n/// Convert `ResponseHead` to a `ResponseBuilder`\nimpl<'a> From<&'a ResponseHead> for ResponseBuilder {\n    fn from(head: &'a ResponseHead) -> ResponseBuilder {\n        let mut msg = BoxedResponseHead::new(head.status);\n        msg.version = head.version;\n        msg.reason = head.reason;\n\n        for (k, v) in head.headers.iter() {\n            msg.headers.append(k.clone(), v.clone());\n        }\n\n        msg.no_chunking(!head.chunked());\n\n        ResponseBuilder {\n            head: Some(msg),\n            err: None,\n        }\n    }\n}\n\nimpl fmt::Debug for ResponseBuilder {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let head = self.head.as_ref().unwrap();\n\n        let res = writeln!(\n            f,\n            \"\\nResponseBuilder {:?} {}{}\",\n            head.version,\n            head.status,\n            head.reason.unwrap_or(\"\"),\n        );\n        let _ = writeln!(f, \"  headers:\");\n        for (key, val) in head.headers.iter() {\n            let _ = writeln!(f, \"    {:?}: {:?}\", key, val);\n        }\n        res\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::header::{HeaderName, HeaderValue, CONTENT_TYPE};\n\n    #[test]\n    fn test_basic_builder() {\n        let resp = Response::build(StatusCode::OK)\n            .insert_header((\"X-TEST\", \"value\"))\n            .finish();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[test]\n    fn test_upgrade() {\n        let resp = Response::build(StatusCode::OK)\n            .upgrade(\"websocket\")\n            .finish();\n        assert!(resp.upgrade());\n        assert_eq!(\n            resp.headers().get(header::UPGRADE).unwrap(),\n            HeaderValue::from_static(\"websocket\")\n        );\n    }\n\n    #[test]\n    fn test_force_close() {\n        let resp = Response::build(StatusCode::OK).force_close().finish();\n        assert!(!resp.keep_alive());\n    }\n\n    #[test]\n    fn test_content_type() {\n        let resp = Response::build(StatusCode::OK)\n            .content_type(\"text/plain\")\n            .body(Bytes::new());\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"text/plain\");\n\n        let resp = Response::build(StatusCode::OK)\n            .content_type(mime::TEXT_JAVASCRIPT)\n            .body(Bytes::new());\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"text/javascript\");\n    }\n\n    #[test]\n    fn test_into_builder() {\n        let mut resp: Response<_> = \"test\".into();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        resp.headers_mut().insert(\n            HeaderName::from_static(\"cookie\"),\n            HeaderValue::from_static(\"cookie1=val100\"),\n        );\n\n        let mut builder: ResponseBuilder = resp.into();\n        let resp = builder.status(StatusCode::BAD_REQUEST).finish();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let cookie = resp.headers().get_all(\"Cookie\").next().unwrap();\n        assert_eq!(cookie.to_str().unwrap(), \"cookie1=val100\");\n    }\n\n    #[test]\n    fn response_builder_header_insert_kv() {\n        let mut res = Response::build(StatusCode::OK);\n        res.insert_header((\"Content-Type\", \"application/octet-stream\"));\n        let res = res.finish();\n\n        assert_eq!(\n            res.headers().get(\"Content-Type\"),\n            Some(&HeaderValue::from_static(\"application/octet-stream\"))\n        );\n    }\n\n    #[test]\n    fn response_builder_header_insert_typed() {\n        let mut res = Response::build(StatusCode::OK);\n        res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));\n        let res = res.finish();\n\n        assert_eq!(\n            res.headers().get(\"Content-Type\"),\n            Some(&HeaderValue::from_static(\"application/octet-stream\"))\n        );\n    }\n\n    #[test]\n    fn response_builder_header_append_kv() {\n        let mut res = Response::build(StatusCode::OK);\n        res.append_header((\"Content-Type\", \"application/octet-stream\"));\n        res.append_header((\"Content-Type\", \"application/json\"));\n        let res = res.finish();\n\n        let headers: Vec<_> = res.headers().get_all(\"Content-Type\").cloned().collect();\n        assert_eq!(headers.len(), 2);\n        assert!(headers.contains(&HeaderValue::from_static(\"application/octet-stream\")));\n        assert!(headers.contains(&HeaderValue::from_static(\"application/json\")));\n    }\n\n    #[test]\n    fn response_builder_header_append_typed() {\n        let mut res = Response::build(StatusCode::OK);\n        res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));\n        res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));\n        let res = res.finish();\n\n        let headers: Vec<_> = res.headers().get_all(\"Content-Type\").cloned().collect();\n        assert_eq!(headers.len(), 2);\n        assert!(headers.contains(&HeaderValue::from_static(\"application/octet-stream\")));\n        assert!(headers.contains(&HeaderValue::from_static(\"application/json\")));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/responses/head.rs",
    "content": "//! Response head type and caching pool.\n\nuse std::{cell::RefCell, ops};\n\nuse crate::{header::HeaderMap, message::Flags, ConnectionType, StatusCode, Version};\n\nthread_local! {\n    static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create();\n}\n\n#[derive(Debug, Clone)]\npub struct ResponseHead {\n    pub version: Version,\n    pub status: StatusCode,\n    pub headers: HeaderMap,\n    pub reason: Option<&'static str>,\n    pub(crate) flags: Flags,\n}\n\nimpl ResponseHead {\n    /// Create new instance of `ResponseHead` type\n    #[inline]\n    pub fn new(status: StatusCode) -> ResponseHead {\n        ResponseHead {\n            status,\n            version: Version::HTTP_11,\n            headers: HeaderMap::with_capacity(12),\n            reason: None,\n            flags: Flags::empty(),\n        }\n    }\n\n    /// Read the message headers.\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        &self.headers\n    }\n\n    /// Mutable reference to the message headers.\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.headers\n    }\n\n    /// Sets the flag that controls whether to send headers formatted as Camel-Case.\n    ///\n    /// Only applicable to HTTP/1.x responses; HTTP/2 header names are always lowercase.\n    #[inline]\n    pub fn set_camel_case_headers(&mut self, camel_case: bool) {\n        if camel_case {\n            self.flags.insert(Flags::CAMEL_CASE);\n        } else {\n            self.flags.remove(Flags::CAMEL_CASE);\n        }\n    }\n\n    /// Set connection type of the message\n    #[inline]\n    pub fn set_connection_type(&mut self, ctype: ConnectionType) {\n        match ctype {\n            ConnectionType::Close => self.flags.insert(Flags::CLOSE),\n            ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),\n            ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),\n        }\n    }\n\n    #[inline]\n    pub fn connection_type(&self) -> ConnectionType {\n        if self.flags.contains(Flags::CLOSE) {\n            ConnectionType::Close\n        } else if self.flags.contains(Flags::KEEP_ALIVE) {\n            ConnectionType::KeepAlive\n        } else if self.flags.contains(Flags::UPGRADE) {\n            ConnectionType::Upgrade\n        } else if self.version < Version::HTTP_11 {\n            ConnectionType::Close\n        } else {\n            ConnectionType::KeepAlive\n        }\n    }\n\n    /// Check if keep-alive is enabled\n    #[inline]\n    pub fn keep_alive(&self) -> bool {\n        self.connection_type() == ConnectionType::KeepAlive\n    }\n\n    /// Check upgrade status of this message\n    #[inline]\n    pub fn upgrade(&self) -> bool {\n        self.connection_type() == ConnectionType::Upgrade\n    }\n\n    /// Get custom reason for the response\n    #[inline]\n    pub fn reason(&self) -> &str {\n        self.reason.unwrap_or_else(|| {\n            self.status\n                .canonical_reason()\n                .unwrap_or(\"<unknown status code>\")\n        })\n    }\n\n    #[inline]\n    pub(crate) fn conn_type(&self) -> Option<ConnectionType> {\n        if self.flags.contains(Flags::CLOSE) {\n            Some(ConnectionType::Close)\n        } else if self.flags.contains(Flags::KEEP_ALIVE) {\n            Some(ConnectionType::KeepAlive)\n        } else if self.flags.contains(Flags::UPGRADE) {\n            Some(ConnectionType::Upgrade)\n        } else {\n            None\n        }\n    }\n\n    /// Get response body chunking state\n    #[inline]\n    pub fn chunked(&self) -> bool {\n        !self.flags.contains(Flags::NO_CHUNKING)\n    }\n\n    /// Set no chunking for payload\n    #[inline]\n    pub fn no_chunking(&mut self, val: bool) {\n        if val {\n            self.flags.insert(Flags::NO_CHUNKING);\n        } else {\n            self.flags.remove(Flags::NO_CHUNKING);\n        }\n    }\n}\n\npub(crate) struct BoxedResponseHead {\n    head: Option<Box<ResponseHead>>,\n}\n\nimpl BoxedResponseHead {\n    /// Get new message from the pool of objects\n    pub fn new(status: StatusCode) -> Self {\n        RESPONSE_POOL.with(|p| p.get_message(status))\n    }\n}\n\nimpl ops::Deref for BoxedResponseHead {\n    type Target = ResponseHead;\n\n    fn deref(&self) -> &Self::Target {\n        self.head.as_ref().unwrap()\n    }\n}\n\nimpl ops::DerefMut for BoxedResponseHead {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.head.as_mut().unwrap()\n    }\n}\n\nimpl Drop for BoxedResponseHead {\n    fn drop(&mut self) {\n        if let Some(head) = self.head.take() {\n            RESPONSE_POOL.with(move |p| p.release(head))\n        }\n    }\n}\n\n/// Response head object pool.\n#[doc(hidden)]\npub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell<Vec<Box<ResponseHead>>>);\n\nimpl BoxedResponsePool {\n    fn create() -> BoxedResponsePool {\n        BoxedResponsePool(RefCell::new(Vec::with_capacity(128)))\n    }\n\n    /// Get message from the pool.\n    #[inline]\n    fn get_message(&self, status: StatusCode) -> BoxedResponseHead {\n        if let Some(mut head) = self.0.borrow_mut().pop() {\n            head.reason = None;\n            head.status = status;\n            head.headers.clear();\n            head.flags = Flags::empty();\n            BoxedResponseHead { head: Some(head) }\n        } else {\n            BoxedResponseHead {\n                head: Some(Box::new(ResponseHead::new(status))),\n            }\n        }\n    }\n\n    /// Release request instance.\n    #[inline]\n    fn release(&self, msg: Box<ResponseHead>) {\n        let pool = &mut self.0.borrow_mut();\n\n        if pool.len() < 128 {\n            pool.push(msg);\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{\n        io::{Read as _, Write as _},\n        net,\n    };\n\n    use memchr::memmem;\n\n    use crate::{\n        h1::H1Service,\n        header::{HeaderName, HeaderValue},\n        Error, Request, Response, ServiceConfig,\n    };\n\n    #[actix_rt::test]\n    async fn camel_case_headers() {\n        let mut srv = actix_http_test::test_server(|| {\n            H1Service::with_config(ServiceConfig::default(), |req: Request| async move {\n                let mut res = Response::ok();\n\n                if req.path().contains(\"camel\") {\n                    res.head_mut().set_camel_case_headers(true);\n                }\n\n                res.headers_mut().insert(\n                    HeaderName::from_static(\"foo-bar\"),\n                    HeaderValue::from_static(\"baz\"),\n                );\n\n                Ok::<_, Error>(res)\n            })\n            .tcp()\n        })\n        .await;\n\n        let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n        stream\n            .write_all(b\"GET /camel HTTP/1.1\\r\\nConnection: Close\\r\\n\\r\\n\")\n            .unwrap();\n        let mut data = vec![];\n        let _ = stream.read_to_end(&mut data).unwrap();\n        assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n        assert!(memmem::find(&data, b\"Foo-Bar\").is_some());\n        assert!(memmem::find(&data, b\"foo-bar\").is_none());\n        assert!(memmem::find(&data, b\"Date\").is_some());\n        assert!(memmem::find(&data, b\"date\").is_none());\n        assert!(memmem::find(&data, b\"Content-Length\").is_some());\n        assert!(memmem::find(&data, b\"content-length\").is_none());\n\n        let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n        stream\n            .write_all(b\"GET /lower HTTP/1.1\\r\\nConnection: Close\\r\\n\\r\\n\")\n            .unwrap();\n        let mut data = vec![];\n        let _ = stream.read_to_end(&mut data).unwrap();\n        assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n        assert!(memmem::find(&data, b\"Foo-Bar\").is_none());\n        assert!(memmem::find(&data, b\"foo-bar\").is_some());\n        assert!(memmem::find(&data, b\"Date\").is_none());\n        assert!(memmem::find(&data, b\"date\").is_some());\n        assert!(memmem::find(&data, b\"Content-Length\").is_none());\n        assert!(memmem::find(&data, b\"content-length\").is_some());\n\n        srv.stop().await;\n    }\n}\n"
  },
  {
    "path": "actix-http/src/responses/mod.rs",
    "content": "//! HTTP response.\n\nmod builder;\nmod head;\n#[allow(clippy::module_inception)]\nmod response;\n\npub(crate) use self::head::BoxedResponseHead;\npub use self::{builder::ResponseBuilder, head::ResponseHead, response::Response};\n"
  },
  {
    "path": "actix-http/src/responses/response.rs",
    "content": "//! HTTP response.\n\nuse std::{\n    cell::{Ref, RefCell, RefMut},\n    fmt, str,\n};\n\nuse bytes::{Bytes, BytesMut};\nuse bytestring::ByteString;\n\nuse crate::{\n    body::{BoxBody, EitherBody, MessageBody},\n    header::{self, HeaderMap, TryIntoHeaderValue},\n    responses::BoxedResponseHead,\n    Error, Extensions, ResponseBuilder, ResponseHead, StatusCode,\n};\n\n/// An HTTP response.\npub struct Response<B> {\n    pub(crate) head: BoxedResponseHead,\n    pub(crate) body: B,\n    pub(crate) extensions: RefCell<Extensions>,\n}\n\nimpl Response<BoxBody> {\n    /// Constructs a new response with default body.\n    #[inline]\n    pub fn new(status: StatusCode) -> Self {\n        Response {\n            head: BoxedResponseHead::new(status),\n            body: BoxBody::new(()),\n            extensions: RefCell::new(Extensions::new()),\n        }\n    }\n\n    /// Constructs a new response builder.\n    #[inline]\n    pub fn build(status: StatusCode) -> ResponseBuilder {\n        ResponseBuilder::new(status)\n    }\n\n    // just a couple frequently used shortcuts\n    // this list should not grow larger than a few\n\n    /// Constructs a new response with status 200 OK.\n    #[inline]\n    pub fn ok() -> Self {\n        Response::new(StatusCode::OK)\n    }\n\n    /// Constructs a new response with status 400 Bad Request.\n    #[inline]\n    pub fn bad_request() -> Self {\n        Response::new(StatusCode::BAD_REQUEST)\n    }\n\n    /// Constructs a new response with status 404 Not Found.\n    #[inline]\n    pub fn not_found() -> Self {\n        Response::new(StatusCode::NOT_FOUND)\n    }\n\n    /// Constructs a new response with status 500 Internal Server Error.\n    #[inline]\n    pub fn internal_server_error() -> Self {\n        Response::new(StatusCode::INTERNAL_SERVER_ERROR)\n    }\n\n    // end shortcuts\n}\n\nimpl<B> Response<B> {\n    /// Constructs a new response with given body.\n    #[inline]\n    pub fn with_body(status: StatusCode, body: B) -> Response<B> {\n        Response {\n            head: BoxedResponseHead::new(status),\n            body,\n            extensions: RefCell::new(Extensions::new()),\n        }\n    }\n\n    /// Returns a reference to the head of this response.\n    #[inline]\n    pub fn head(&self) -> &ResponseHead {\n        &self.head\n    }\n\n    /// Returns a mutable reference to the head of this response.\n    #[inline]\n    pub fn head_mut(&mut self) -> &mut ResponseHead {\n        &mut self.head\n    }\n\n    /// Returns the status code of this response.\n    #[inline]\n    pub fn status(&self) -> StatusCode {\n        self.head.status\n    }\n\n    /// Returns a mutable reference the status code of this response.\n    #[inline]\n    pub fn status_mut(&mut self) -> &mut StatusCode {\n        &mut self.head.status\n    }\n\n    /// Returns a reference to response headers.\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head.headers\n    }\n\n    /// Returns a mutable reference to response headers.\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.head.headers\n    }\n\n    /// Returns true if connection upgrade is enabled.\n    #[inline]\n    pub fn upgrade(&self) -> bool {\n        self.head.upgrade()\n    }\n\n    /// Returns true if keep-alive is enabled.\n    #[inline]\n    pub fn keep_alive(&self) -> bool {\n        self.head.keep_alive()\n    }\n\n    /// Returns a reference to the request-local data/extensions container.\n    #[inline]\n    pub fn extensions(&self) -> Ref<'_, Extensions> {\n        self.extensions.borrow()\n    }\n\n    /// Returns a mutable reference to the request-local data/extensions container.\n    #[inline]\n    pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {\n        self.extensions.borrow_mut()\n    }\n\n    /// Returns a reference to the body of this response.\n    #[inline]\n    pub fn body(&self) -> &B {\n        &self.body\n    }\n\n    /// Sets new body.\n    #[inline]\n    pub fn set_body<B2>(self, body: B2) -> Response<B2> {\n        Response {\n            head: self.head,\n            body,\n            extensions: self.extensions,\n        }\n    }\n\n    /// Drops body and returns new response.\n    #[inline]\n    pub fn drop_body(self) -> Response<()> {\n        self.set_body(())\n    }\n\n    /// Sets new body, returning new response and previous body value.\n    #[inline]\n    pub(crate) fn replace_body<B2>(self, body: B2) -> (Response<B2>, B) {\n        (\n            Response {\n                head: self.head,\n                body,\n                extensions: self.extensions,\n            },\n            self.body,\n        )\n    }\n\n    /// Returns split head and body.\n    ///\n    /// # Implementation Notes\n    /// Due to internal performance optimizations, the first element of the returned tuple is a\n    /// `Response` as well but only contains the head of the response this was called on.\n    #[inline]\n    pub fn into_parts(self) -> (Response<()>, B) {\n        self.replace_body(())\n    }\n\n    /// Map the current body type to another using a closure, returning a new response.\n    ///\n    /// Closure receives the response head and the current body type.\n    #[inline]\n    pub fn map_body<F, B2>(mut self, f: F) -> Response<B2>\n    where\n        F: FnOnce(&mut ResponseHead, B) -> B2,\n    {\n        let body = f(&mut self.head, self.body);\n\n        Response {\n            head: self.head,\n            body,\n            extensions: self.extensions,\n        }\n    }\n\n    /// Map the current body to a type-erased `BoxBody`.\n    #[inline]\n    pub fn map_into_boxed_body(self) -> Response<BoxBody>\n    where\n        B: MessageBody + 'static,\n    {\n        self.map_body(|_, body| body.boxed())\n    }\n\n    /// Returns the response body, dropping all other parts.\n    #[inline]\n    pub fn into_body(self) -> B {\n        self.body\n    }\n}\n\nimpl<B> fmt::Debug for Response<B>\nwhere\n    B: MessageBody,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let res = writeln!(\n            f,\n            \"\\nResponse {:?} {}{}\",\n            self.head.version,\n            self.head.status,\n            self.head.reason.unwrap_or(\"\"),\n        );\n        let _ = writeln!(f, \"  headers:\");\n        for (key, val) in self.head.headers.iter() {\n            let _ = writeln!(f, \"    {:?}: {:?}\", key, val);\n        }\n        let _ = writeln!(f, \"  body: {:?}\", self.body.size());\n        res\n    }\n}\n\nimpl<B: Default> Default for Response<B> {\n    #[inline]\n    fn default() -> Response<B> {\n        Response::with_body(StatusCode::default(), B::default())\n    }\n}\n\nimpl<I: Into<Response<BoxBody>>, E: Into<Error>> From<Result<I, E>> for Response<BoxBody> {\n    fn from(res: Result<I, E>) -> Self {\n        match res {\n            Ok(val) => val.into(),\n            Err(err) => Response::from(err.into()),\n        }\n    }\n}\n\nimpl From<ResponseBuilder> for Response<EitherBody<()>> {\n    fn from(mut builder: ResponseBuilder) -> Self {\n        builder.finish()\n    }\n}\n\nimpl From<std::convert::Infallible> for Response<BoxBody> {\n    fn from(val: std::convert::Infallible) -> Self {\n        match val {}\n    }\n}\n\nimpl From<&'static str> for Response<&'static str> {\n    fn from(val: &'static str) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<&'static [u8]> for Response<&'static [u8]> {\n    fn from(val: &'static [u8]) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<Vec<u8>> for Response<Vec<u8>> {\n    fn from(val: Vec<u8>) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<&Vec<u8>> for Response<Vec<u8>> {\n    fn from(val: &Vec<u8>) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val.clone());\n        let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<String> for Response<String> {\n    fn from(val: String) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<&String> for Response<String> {\n    fn from(val: &String) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val.clone());\n        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<Bytes> for Response<Bytes> {\n    fn from(val: Bytes) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<BytesMut> for Response<BytesMut> {\n    fn from(val: BytesMut) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\nimpl From<ByteString> for Response<ByteString> {\n    fn from(val: ByteString) -> Self {\n        let mut res = Response::with_body(StatusCode::OK, val);\n        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n        res\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        body::to_bytes,\n        header::{HeaderValue, CONTENT_TYPE, COOKIE},\n    };\n\n    #[test]\n    fn test_debug() {\n        let resp = Response::build(StatusCode::OK)\n            .append_header((COOKIE, HeaderValue::from_static(\"cookie1=value1; \")))\n            .append_header((COOKIE, HeaderValue::from_static(\"cookie2=value2; \")))\n            .finish();\n        let dbg = format!(\"{:?}\", resp);\n        assert!(dbg.contains(\"Response\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_into_response() {\n        let res = Response::from(\"test\");\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let res = Response::from(b\"test\".as_ref());\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let res = Response::from(\"test\".to_owned());\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let res = Response::from(\"test\".to_owned());\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let b = Bytes::from_static(b\"test\");\n        let res = Response::from(b);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let b = Bytes::from_static(b\"test\");\n        let res = Response::from(b);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n\n        let b = BytesMut::from(\"test\");\n        let res = Response::from(b);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(to_bytes(res.into_body()).await.unwrap(), &b\"test\"[..]);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/service.rs",
    "content": "use std::{\n    fmt,\n    future::Future,\n    marker::PhantomData,\n    net,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse actix_rt::net::TcpStream;\nuse actix_service::{\n    fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,\n};\nuse futures_core::{future::LocalBoxFuture, ready};\nuse pin_project_lite::pin_project;\nuse tracing::error;\n\nuse crate::{\n    body::{BoxBody, MessageBody},\n    builder::HttpServiceBuilder,\n    error::DispatchError,\n    h1, ConnectCallback, OnConnectData, Protocol, Request, Response, ServiceConfig,\n};\n\n#[inline]\nfn desired_nodelay(tcp_nodelay: Option<bool>) -> Option<bool> {\n    tcp_nodelay\n}\n\n#[inline]\nfn set_nodelay(stream: &TcpStream, nodelay: bool) {\n    let _ = stream.set_nodelay(nodelay);\n}\n\n/// A [`ServiceFactory`] for HTTP/1.1 and HTTP/2 connections.\n///\n/// Use [`build`](Self::build) to begin constructing service. Also see [`HttpServiceBuilder`].\n///\n/// # Automatic HTTP Version Selection\n/// There are two ways to select the HTTP version of an incoming connection:\n/// - One is to rely on the ALPN information that is provided when using TLS (HTTPS); both versions\n///   are supported automatically when using either of the `.rustls()` or `.openssl()` finalizing\n///   methods.\n/// - The other is to read the first few bytes of the TCP stream. This is the only viable approach\n///   for supporting H2C, which allows the HTTP/2 protocol to work over plaintext connections. Use\n///   the `.tcp_auto_h2c()` finalizing method to enable this behavior.\n///\n/// # Examples\n/// ```\n/// # use std::convert::Infallible;\n/// use actix_http::{HttpService, Request, Response, StatusCode};\n///\n/// // this service would constructed in an actix_server::Server\n///\n/// # actix_rt::System::new().block_on(async {\n/// HttpService::build()\n///     // the builder finalizing method, other finalizers would not return an `HttpService`\n///     .finish(|_req: Request| async move {\n///         Ok::<_, Infallible>(\n///             Response::build(StatusCode::OK).body(\"Hello!\")\n///         )\n///     })\n///     // the service finalizing method method\n///     // you can use `.tcp_auto_h2c()`, `.rustls()`, or `.openssl()` instead of `.tcp()`\n///     .tcp();\n/// # })\n/// ```\npub struct HttpService<T, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler> {\n    srv: S,\n    cfg: ServiceConfig,\n    expect: X,\n    upgrade: Option<U>,\n    on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<B>,\n}\n\nimpl<T, S, B> HttpService<T, S, B>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    B: MessageBody + 'static,\n{\n    /// Constructs builder for `HttpService` instance.\n    pub fn build() -> HttpServiceBuilder<T, S> {\n        HttpServiceBuilder::default()\n    }\n}\n\nimpl<T, S, B> HttpService<T, S, B>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    B: MessageBody + 'static,\n{\n    /// Constructs new `HttpService` instance from service with default config.\n    pub fn new<F: IntoServiceFactory<S, Request>>(service: F) -> Self {\n        HttpService {\n            cfg: ServiceConfig::default(),\n            srv: service.into_factory(),\n            expect: h1::ExpectHandler,\n            upgrade: None,\n            on_connect_ext: None,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Constructs new `HttpService` instance from config and service.\n    pub(crate) fn with_config<F: IntoServiceFactory<S, Request>>(\n        cfg: ServiceConfig,\n        service: F,\n    ) -> Self {\n        HttpService {\n            cfg,\n            srv: service.into_factory(),\n            expect: h1::ExpectHandler,\n            upgrade: None,\n            on_connect_ext: None,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<T, S, B, X, U> HttpService<T, S, B, X, U>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    B: MessageBody,\n{\n    /// Sets service for `Expect: 100-Continue` handling.\n    ///\n    /// An expect service is called with requests that contain an `Expect` header. A successful\n    /// response type is also a request which will be forwarded to the main service.\n    pub fn expect<X1>(self, expect: X1) -> HttpService<T, S, B, X1, U>\n    where\n        X1: ServiceFactory<Request, Config = (), Response = Request>,\n        X1::Error: Into<Response<BoxBody>>,\n        X1::InitError: fmt::Debug,\n    {\n        HttpService {\n            expect,\n            cfg: self.cfg,\n            srv: self.srv,\n            upgrade: self.upgrade,\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Sets service for custom `Connection: Upgrade` handling.\n    ///\n    /// If service is provided then normal requests handling get halted and this service get called\n    /// with original request and framed object.\n    pub fn upgrade<U1>(self, upgrade: Option<U1>) -> HttpService<T, S, B, X, U1>\n    where\n        U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,\n        U1::Error: fmt::Display,\n        U1::InitError: fmt::Debug,\n    {\n        HttpService {\n            upgrade,\n            cfg: self.cfg,\n            srv: self.srv,\n            expect: self.expect,\n            on_connect_ext: self.on_connect_ext,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Set connect callback with mutable access to request data container.\n    pub(crate) fn on_connect_ext(mut self, f: Option<Rc<ConnectCallback<T>>>) -> Self {\n        self.on_connect_ext = f;\n        self\n    }\n}\n\nimpl<S, B, X, U> HttpService<TcpStream, S, B, X, U>\nwhere\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n\n    B: MessageBody + 'static,\n\n    X: ServiceFactory<Request, Config = (), Response = Request>,\n    X::Future: 'static,\n    X::Error: Into<Response<BoxBody>>,\n    X::InitError: fmt::Debug,\n\n    U: ServiceFactory<(Request, Framed<TcpStream, h1::Codec>), Config = (), Response = ()>,\n    U::Future: 'static,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n    U::InitError: fmt::Debug,\n{\n    /// Creates TCP stream service from HTTP service.\n    ///\n    /// The resulting service only supports HTTP/1.x.\n    pub fn tcp(\n        self,\n    ) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>\n    {\n        let tcp_nodelay = self.cfg.tcp_nodelay();\n\n        fn_service(move |io: TcpStream| async move {\n            if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                set_nodelay(&io, nodelay);\n            }\n\n            let peer_addr = io.peer_addr().ok();\n            Ok((io, Protocol::Http1, peer_addr))\n        })\n        .and_then(self)\n    }\n\n    /// Creates TCP stream service from HTTP service that automatically selects HTTP/1.x or HTTP/2\n    /// on plaintext connections.\n    #[cfg(feature = \"http2\")]\n    pub fn tcp_auto_h2c(\n        self,\n    ) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>\n    {\n        let tcp_nodelay = self.cfg.tcp_nodelay();\n\n        fn_service(move |io: TcpStream| async move {\n            // subset of HTTP/2 preface defined by RFC 9113 §3.4\n            // this subset was chosen to maximize likelihood that peeking only once will allow us to\n            // reliably determine version or else it should fallback to h1 and fail quickly if data\n            // on the wire is junk\n            const H2_PREFACE: &[u8] = b\"PRI * HTTP/2\";\n\n            let mut buf = [0; 12];\n\n            io.peek(&mut buf).await?;\n\n            let proto = if buf == H2_PREFACE {\n                Protocol::Http2\n            } else {\n                Protocol::Http1\n            };\n\n            if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                set_nodelay(&io, nodelay);\n            }\n\n            let peer_addr = io.peer_addr().ok();\n            Ok((io, proto, peer_addr))\n        })\n        .and_then(self)\n    }\n}\n\n/// Configuration options used when accepting TLS connection.\n#[cfg(feature = \"__tls\")]\n#[derive(Debug, Default)]\npub struct TlsAcceptorConfig {\n    pub(crate) handshake_timeout: Option<std::time::Duration>,\n}\n\n#[cfg(feature = \"__tls\")]\nimpl TlsAcceptorConfig {\n    /// Set TLS handshake timeout duration.\n    pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {\n        Self {\n            handshake_timeout: Some(dur),\n            // ..self\n        }\n    }\n}\n\n#[cfg(feature = \"openssl\")]\nmod openssl {\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        openssl::{\n            reexports::{Error as SslError, SslAcceptor},\n            Acceptor, TlsStream,\n        },\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create OpenSSL based service.\n        pub fn openssl(\n            self,\n            acceptor: SslAcceptor,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<SslError, DispatchError>,\n            InitError = (),\n        > {\n            self.openssl_with_config(acceptor, TlsAcceptorConfig::default())\n        }\n\n        /// Create OpenSSL based service with custom TLS acceptor configuration.\n        pub fn openssl_with_config(\n            self,\n            acceptor: SslAcceptor,\n            tls_acceptor_config: TlsAcceptorConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<SslError, DispatchError>,\n            InitError = (),\n        > {\n            let tcp_nodelay = self.cfg.tcp_nodelay();\n            let mut acceptor = Acceptor::new(acceptor);\n\n            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {\n                acceptor.set_handshake_timeout(handshake_timeout);\n            }\n\n            acceptor\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .map(move |io: TlsStream<TcpStream>| {\n                    let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() {\n                        if protos.windows(2).any(|window| window == b\"h2\") {\n                            Protocol::Http2\n                        } else {\n                            Protocol::Http1\n                        }\n                    } else {\n                        Protocol::Http1\n                    };\n\n                    if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                        set_nodelay(io.get_ref(), nodelay);\n                    }\n\n                    let peer_addr = io.get_ref().peer_addr().ok();\n                    (io, proto, peer_addr)\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_20\")]\nmod rustls_0_20 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_20::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.20 based service.\n        pub fn rustls(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            self.rustls_with_config(config, TlsAcceptorConfig::default())\n        }\n\n        /// Create Rustls v0.20 based service with custom TLS acceptor configuration.\n        pub fn rustls_with_config(\n            self,\n            mut config: ServerConfig,\n            tls_acceptor_config: TlsAcceptorConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            let tcp_nodelay = self.cfg.tcp_nodelay();\n            let mut protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            let mut acceptor = Acceptor::new(config);\n\n            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {\n                acceptor.set_handshake_timeout(handshake_timeout);\n            }\n\n            acceptor\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .and_then(move |io: TlsStream<TcpStream>| async move {\n                    let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {\n                        if protos.windows(2).any(|window| window == b\"h2\") {\n                            Protocol::Http2\n                        } else {\n                            Protocol::Http1\n                        }\n                    } else {\n                        Protocol::Http1\n                    };\n\n                    if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    Ok((io, proto, peer_addr))\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_21\")]\nmod rustls_0_21 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.21 based service.\n        pub fn rustls_021(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            self.rustls_021_with_config(config, TlsAcceptorConfig::default())\n        }\n\n        /// Create Rustls v0.21 based service with custom TLS acceptor configuration.\n        pub fn rustls_021_with_config(\n            self,\n            mut config: ServerConfig,\n            tls_acceptor_config: TlsAcceptorConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            let tcp_nodelay = self.cfg.tcp_nodelay();\n            let mut protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            let mut acceptor = Acceptor::new(config);\n\n            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {\n                acceptor.set_handshake_timeout(handshake_timeout);\n            }\n\n            acceptor\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .and_then(move |io: TlsStream<TcpStream>| async move {\n                    let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {\n                        if protos.windows(2).any(|window| window == b\"h2\") {\n                            Protocol::Http2\n                        } else {\n                            Protocol::Http1\n                        }\n                    } else {\n                        Protocol::Http1\n                    };\n\n                    if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    Ok((io, proto, peer_addr))\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_22\")]\nmod rustls_0_22 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.22 based service.\n        pub fn rustls_0_22(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            self.rustls_0_22_with_config(config, TlsAcceptorConfig::default())\n        }\n\n        /// Create Rustls v0.22 based service with custom TLS acceptor configuration.\n        pub fn rustls_0_22_with_config(\n            self,\n            mut config: ServerConfig,\n            tls_acceptor_config: TlsAcceptorConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            let tcp_nodelay = self.cfg.tcp_nodelay();\n            let mut protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            let mut acceptor = Acceptor::new(config);\n\n            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {\n                acceptor.set_handshake_timeout(handshake_timeout);\n            }\n\n            acceptor\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .and_then(move |io: TlsStream<TcpStream>| async move {\n                    let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {\n                        if protos.windows(2).any(|window| window == b\"h2\") {\n                            Protocol::Http2\n                        } else {\n                            Protocol::Http1\n                        }\n                    } else {\n                        Protocol::Http1\n                    };\n\n                    if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    Ok((io, proto, peer_addr))\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\n#[cfg(feature = \"rustls-0_23\")]\nmod rustls_0_23 {\n    use std::io;\n\n    use actix_service::ServiceFactoryExt as _;\n    use actix_tls::accept::{\n        rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},\n        TlsError,\n    };\n\n    use super::*;\n\n    impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>\n    where\n        S: ServiceFactory<Request, Config = ()>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>> + 'static,\n        S::InitError: fmt::Debug,\n        S::Response: Into<Response<B>> + 'static,\n        <S::Service as Service<Request>>::Future: 'static,\n\n        B: MessageBody + 'static,\n\n        X: ServiceFactory<Request, Config = (), Response = Request>,\n        X::Future: 'static,\n        X::Error: Into<Response<BoxBody>>,\n        X::InitError: fmt::Debug,\n\n        U: ServiceFactory<\n            (Request, Framed<TlsStream<TcpStream>, h1::Codec>),\n            Config = (),\n            Response = (),\n        >,\n        U::Future: 'static,\n        U::Error: fmt::Display + Into<Response<BoxBody>>,\n        U::InitError: fmt::Debug,\n    {\n        /// Create Rustls v0.23 based service.\n        pub fn rustls_0_23(\n            self,\n            config: ServerConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            self.rustls_0_23_with_config(config, TlsAcceptorConfig::default())\n        }\n\n        /// Create Rustls v0.23 based service with custom TLS acceptor configuration.\n        pub fn rustls_0_23_with_config(\n            self,\n            mut config: ServerConfig,\n            tls_acceptor_config: TlsAcceptorConfig,\n        ) -> impl ServiceFactory<\n            TcpStream,\n            Config = (),\n            Response = (),\n            Error = TlsError<io::Error, DispatchError>,\n            InitError = (),\n        > {\n            let tcp_nodelay = self.cfg.tcp_nodelay();\n            let mut protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n            protos.extend_from_slice(&config.alpn_protocols);\n            config.alpn_protocols = protos;\n\n            let mut acceptor = Acceptor::new(config);\n\n            if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {\n                acceptor.set_handshake_timeout(handshake_timeout);\n            }\n\n            acceptor\n                .map_init_err(|_| {\n                    unreachable!(\"TLS acceptor service factory does not error on init\")\n                })\n                .map_err(TlsError::into_service_error)\n                .and_then(move |io: TlsStream<TcpStream>| async move {\n                    let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {\n                        if protos.windows(2).any(|window| window == b\"h2\") {\n                            Protocol::Http2\n                        } else {\n                            Protocol::Http1\n                        }\n                    } else {\n                        Protocol::Http1\n                    };\n\n                    if let Some(nodelay) = desired_nodelay(tcp_nodelay) {\n                        set_nodelay(io.get_ref().0, nodelay);\n                    }\n\n                    let peer_addr = io.get_ref().0.peer_addr().ok();\n                    Ok((io, proto, peer_addr))\n                })\n                .and_then(self.map_err(TlsError::Service))\n        }\n    }\n}\n\nimpl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>\n    for HttpService<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin + 'static,\n\n    S: ServiceFactory<Request, Config = ()>,\n    S::Future: 'static,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n\n    B: MessageBody + 'static,\n\n    X: ServiceFactory<Request, Config = (), Response = Request>,\n    X::Future: 'static,\n    X::Error: Into<Response<BoxBody>>,\n    X::InitError: fmt::Debug,\n\n    U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,\n    U::Future: 'static,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n    U::InitError: fmt::Debug,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Config = ();\n    type Service = HttpServiceHandler<T, S::Service, B, X::Service, U::Service>;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let service = self.srv.new_service(());\n        let expect = self.expect.new_service(());\n        let upgrade = self.upgrade.as_ref().map(|s| s.new_service(()));\n        let on_connect_ext = self.on_connect_ext.clone();\n        let cfg = self.cfg.clone();\n\n        Box::pin(async move {\n            let expect = expect.await.map_err(|err| {\n                tracing::error!(\"Initialization of HTTP expect service error: {err:?}\");\n            })?;\n\n            let upgrade = match upgrade {\n                Some(upgrade) => {\n                    let upgrade = upgrade.await.map_err(|err| {\n                        tracing::error!(\"Initialization of HTTP upgrade service error: {err:?}\");\n                    })?;\n                    Some(upgrade)\n                }\n                None => None,\n            };\n\n            let service = service.await.map_err(|err| {\n                tracing::error!(\"Initialization of HTTP service error: {err:?}\");\n            })?;\n\n            Ok(HttpServiceHandler::new(\n                cfg,\n                service,\n                expect,\n                upgrade,\n                on_connect_ext,\n            ))\n        })\n    }\n}\n\n/// `Service` implementation for HTTP/1 and HTTP/2 transport\npub struct HttpServiceHandler<T, S, B, X, U>\nwhere\n    S: Service<Request>,\n    X: Service<Request>,\n    U: Service<(Request, Framed<T, h1::Codec>)>,\n{\n    pub(super) flow: Rc<HttpFlow<S, X, U>>,\n    pub(super) cfg: ServiceConfig,\n    pub(super) on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    _phantom: PhantomData<B>,\n}\n\nimpl<T, S, B, X, U> HttpServiceHandler<T, S, B, X, U>\nwhere\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>>,\n    X: Service<Request>,\n    X::Error: Into<Response<BoxBody>>,\n    U: Service<(Request, Framed<T, h1::Codec>)>,\n    U::Error: Into<Response<BoxBody>>,\n{\n    pub(super) fn new(\n        cfg: ServiceConfig,\n        service: S,\n        expect: X,\n        upgrade: Option<U>,\n        on_connect_ext: Option<Rc<ConnectCallback<T>>>,\n    ) -> HttpServiceHandler<T, S, B, X, U> {\n        HttpServiceHandler {\n            cfg,\n            on_connect_ext,\n            flow: HttpFlow::new(service, expect, upgrade),\n            _phantom: PhantomData,\n        }\n    }\n\n    pub(super) fn _poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Response<BoxBody>>> {\n        ready!(self.flow.expect.poll_ready(cx).map_err(Into::into))?;\n\n        ready!(self.flow.service.poll_ready(cx).map_err(Into::into))?;\n\n        if let Some(ref upg) = self.flow.upgrade {\n            ready!(upg.poll_ready(cx).map_err(Into::into))?;\n        };\n\n        Poll::Ready(Ok(()))\n    }\n}\n\n/// A collection of services that describe an HTTP request flow.\npub(super) struct HttpFlow<S, X, U> {\n    pub(super) service: S,\n    pub(super) expect: X,\n    pub(super) upgrade: Option<U>,\n}\n\nimpl<S, X, U> HttpFlow<S, X, U> {\n    pub(super) fn new(service: S, expect: X, upgrade: Option<U>) -> Rc<Self> {\n        Rc::new(Self {\n            service,\n            expect,\n            upgrade,\n        })\n    }\n}\n\nimpl<T, S, B, X, U> Service<(T, Protocol, Option<net::SocketAddr>)>\n    for HttpServiceHandler<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n\n    B: MessageBody + 'static,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,\n    U::Error: fmt::Display + Into<Response<BoxBody>>,\n{\n    type Response = ();\n    type Error = DispatchError;\n    type Future = HttpServiceHandlerResponse<T, S, B, X, U>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self._poll_ready(cx).map_err(|err| {\n            error!(\"HTTP service readiness error: {:?}\", err);\n            DispatchError::Service(err)\n        })\n    }\n\n    fn call(&self, (io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>)) -> Self::Future {\n        let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());\n\n        match proto {\n            #[cfg(feature = \"http2\")]\n            Protocol::Http2 => HttpServiceHandlerResponse {\n                state: State::H2Handshake {\n                    handshake: Some((\n                        crate::h2::handshake_with_timeout(io, &self.cfg),\n                        self.cfg.clone(),\n                        Rc::clone(&self.flow),\n                        conn_data,\n                        peer_addr,\n                    )),\n                },\n            },\n\n            #[cfg(not(feature = \"http2\"))]\n            Protocol::Http2 => {\n                panic!(\"HTTP/2 support is disabled (enable with the `http2` feature flag)\")\n            }\n\n            Protocol::Http1 => HttpServiceHandlerResponse {\n                state: State::H1 {\n                    dispatcher: h1::Dispatcher::new(\n                        io,\n                        Rc::clone(&self.flow),\n                        self.cfg.clone(),\n                        peer_addr,\n                        conn_data,\n                    ),\n                },\n            },\n\n            proto => unimplemented!(\"Unsupported HTTP version: {:?}.\", proto),\n        }\n    }\n}\n\n#[cfg(not(feature = \"http2\"))]\npin_project! {\n    #[project = StateProj]\n    enum State<T, S, B, X, U>\n    where\n        T: AsyncRead,\n        T: AsyncWrite,\n        T: Unpin,\n\n        S: Service<Request>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        H1 { #[pin] dispatcher: h1::Dispatcher<T, S, B, X, U> },\n    }\n}\n\n#[cfg(feature = \"http2\")]\npin_project! {\n    #[project = StateProj]\n    enum State<T, S, B, X, U>\n    where\n        T: AsyncRead,\n        T: AsyncWrite,\n        T: Unpin,\n\n        S: Service<Request>,\n        S::Future: 'static,\n        S::Error: Into<Response<BoxBody>>,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        H1 { #[pin] dispatcher: h1::Dispatcher<T, S, B, X, U> },\n\n        H2 { #[pin] dispatcher: crate::h2::Dispatcher<T, S, B, X, U> },\n\n        H2Handshake {\n            handshake: Option<(\n                crate::h2::HandshakeWithTimeout<T>,\n                ServiceConfig,\n                Rc<HttpFlow<S, X, U>>,\n                OnConnectData,\n                Option<net::SocketAddr>,\n            )>,\n        },\n    }\n}\n\npin_project! {\n    pub struct HttpServiceHandlerResponse<T, S, B, X, U>\n    where\n        T: AsyncRead,\n        T: AsyncWrite,\n        T: Unpin,\n\n        S: Service<Request>,\n        S::Error: Into<Response<BoxBody>>,\n        S::Error: 'static,\n        S::Future: 'static,\n        S::Response: Into<Response<B>>,\n        S::Response: 'static,\n\n        B: MessageBody,\n\n        X: Service<Request, Response = Request>,\n        X::Error: Into<Response<BoxBody>>,\n\n        U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,\n        U::Error: fmt::Display,\n    {\n        #[pin]\n        state: State<T, S, B, X, U>,\n    }\n}\n\nimpl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>\nwhere\n    T: AsyncRead + AsyncWrite + Unpin,\n\n    S: Service<Request>,\n    S::Error: Into<Response<BoxBody>> + 'static,\n    S::Future: 'static,\n    S::Response: Into<Response<B>> + 'static,\n\n    B: MessageBody + 'static,\n\n    X: Service<Request, Response = Request>,\n    X::Error: Into<Response<BoxBody>>,\n\n    U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,\n    U::Error: fmt::Display,\n{\n    type Output = Result<(), DispatchError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.as_mut().project().state.project() {\n            StateProj::H1 { dispatcher } => dispatcher.poll(cx),\n\n            #[cfg(feature = \"http2\")]\n            StateProj::H2 { dispatcher } => dispatcher.poll(cx),\n\n            #[cfg(feature = \"http2\")]\n            StateProj::H2Handshake { handshake: data } => {\n                match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) {\n                    Ok((conn, timer)) => {\n                        let (_, config, flow, conn_data, peer_addr) = data.take().unwrap();\n\n                        self.as_mut().project().state.set(State::H2 {\n                            dispatcher: crate::h2::Dispatcher::new(\n                                conn, flow, config, peer_addr, conn_data, timer,\n                            ),\n                        });\n                        self.poll(cx)\n                    }\n                    Err(err) => {\n                        tracing::trace!(\"H2 handshake error: {}\", err);\n                        Poll::Ready(Err(err))\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/test.rs",
    "content": "//! Various testing helpers for use in internal and app tests.\n\nuse std::{\n    cell::{Ref, RefCell, RefMut},\n    io::{self, Read, Write},\n    pin::Pin,\n    rc::Rc,\n    str::FromStr,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, ReadBuf};\nuse bytes::{Bytes, BytesMut};\nuse http::{header, Method, Uri, Version};\n\nuse crate::{\n    header::{HeaderMap, TryIntoHeaderPair},\n    payload::Payload,\n    Request,\n};\n\n/// Test `Request` builder.\npub struct TestRequest(Option<Inner>);\n\nstruct Inner {\n    version: Version,\n    method: Method,\n    uri: Uri,\n    headers: HeaderMap,\n    payload: Option<Payload>,\n}\n\nimpl Default for TestRequest {\n    fn default() -> TestRequest {\n        TestRequest(Some(Inner {\n            method: Method::GET,\n            uri: Uri::from_str(\"/\").unwrap(),\n            version: Version::HTTP_11,\n            headers: HeaderMap::new(),\n            payload: None,\n        }))\n    }\n}\n\nimpl TestRequest {\n    /// Create a default TestRequest and then set its URI.\n    pub fn with_uri(path: &str) -> TestRequest {\n        TestRequest::default().uri(path).take()\n    }\n\n    /// Set HTTP version of this request.\n    pub fn version(&mut self, ver: Version) -> &mut Self {\n        parts(&mut self.0).version = ver;\n        self\n    }\n\n    /// Set HTTP method of this request.\n    pub fn method(&mut self, meth: Method) -> &mut Self {\n        parts(&mut self.0).method = meth;\n        self\n    }\n\n    /// Set URI of this request.\n    ///\n    /// # Panics\n    /// If provided URI is invalid.\n    pub fn uri(&mut self, path: &str) -> &mut Self {\n        parts(&mut self.0).uri = Uri::from_str(path).unwrap();\n        self\n    }\n\n    /// Insert a header, replacing any that were set with an equivalent field name.\n    pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => {\n                parts(&mut self.0).headers.insert(key, value);\n            }\n            Err(err) => {\n                panic!(\"Error inserting test header: {}.\", err.into());\n            }\n        }\n\n        self\n    }\n\n    /// Append a header, keeping any that were set with an equivalent field name.\n    pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => {\n                parts(&mut self.0).headers.append(key, value);\n            }\n            Err(err) => {\n                panic!(\"Error inserting test header: {}.\", err.into());\n            }\n        }\n\n        self\n    }\n\n    /// Set request payload.\n    ///\n    /// This sets the `Content-Length` header with the size of `data`.\n    pub fn set_payload(&mut self, data: impl Into<Bytes>) -> &mut Self {\n        let mut payload = crate::h1::Payload::empty();\n        let bytes = data.into();\n        self.insert_header((header::CONTENT_LENGTH, bytes.len()));\n        payload.unread_data(bytes);\n        parts(&mut self.0).payload = Some(payload.into());\n        self\n    }\n\n    pub fn take(&mut self) -> TestRequest {\n        TestRequest(self.0.take())\n    }\n\n    /// Complete request creation and generate `Request` instance.\n    pub fn finish(&mut self) -> Request {\n        let inner = self.0.take().expect(\"cannot reuse test request builder\");\n\n        let mut req = if let Some(pl) = inner.payload {\n            Request::with_payload(pl)\n        } else {\n            Request::with_payload(crate::h1::Payload::empty().into())\n        };\n\n        let head = req.head_mut();\n        head.uri = inner.uri;\n        head.method = inner.method;\n        head.version = inner.version;\n        head.headers = inner.headers;\n\n        req\n    }\n}\n\n#[inline]\nfn parts(parts: &mut Option<Inner>) -> &mut Inner {\n    parts.as_mut().expect(\"cannot reuse test request builder\")\n}\n\n/// Async I/O test buffer.\n#[derive(Debug)]\npub struct TestBuffer {\n    pub read_buf: Rc<RefCell<BytesMut>>,\n    pub write_buf: Rc<RefCell<BytesMut>>,\n    pub err: Option<Rc<io::Error>>,\n}\n\nimpl TestBuffer {\n    /// Create new `TestBuffer` instance with initial read buffer.\n    pub fn new<T>(data: T) -> Self\n    where\n        T: Into<BytesMut>,\n    {\n        Self {\n            read_buf: Rc::new(RefCell::new(data.into())),\n            write_buf: Rc::new(RefCell::new(BytesMut::new())),\n            err: None,\n        }\n    }\n\n    // intentionally not using Clone trait\n    #[allow(dead_code)]\n    pub(crate) fn clone(&self) -> Self {\n        Self {\n            read_buf: Rc::clone(&self.read_buf),\n            write_buf: Rc::clone(&self.write_buf),\n            err: self.err.clone(),\n        }\n    }\n\n    /// Create new empty `TestBuffer` instance.\n    pub fn empty() -> Self {\n        Self::new(\"\")\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn read_buf_slice(&self) -> Ref<'_, [u8]> {\n        Ref::map(self.read_buf.borrow(), |b| b.as_ref())\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn read_buf_slice_mut(&self) -> RefMut<'_, [u8]> {\n        RefMut::map(self.read_buf.borrow_mut(), |b| b.as_mut())\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn write_buf_slice(&self) -> Ref<'_, [u8]> {\n        Ref::map(self.write_buf.borrow(), |b| b.as_ref())\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn write_buf_slice_mut(&self) -> RefMut<'_, [u8]> {\n        RefMut::map(self.write_buf.borrow_mut(), |b| b.as_mut())\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn take_write_buf(&self) -> Bytes {\n        self.write_buf.borrow_mut().split().freeze()\n    }\n\n    /// Add data to read buffer.\n    pub fn extend_read_buf<T: AsRef<[u8]>>(&mut self, data: T) {\n        self.read_buf.borrow_mut().extend_from_slice(data.as_ref())\n    }\n}\n\nimpl io::Read for TestBuffer {\n    fn read(&mut self, dst: &mut [u8]) -> Result<usize, io::Error> {\n        if self.read_buf.borrow().is_empty() {\n            if self.err.is_some() {\n                Err(Rc::try_unwrap(self.err.take().unwrap()).unwrap())\n            } else {\n                Err(io::Error::new(io::ErrorKind::WouldBlock, \"\"))\n            }\n        } else {\n            let size = std::cmp::min(self.read_buf.borrow().len(), dst.len());\n            let b = self.read_buf.borrow_mut().split_to(size);\n            dst[..size].copy_from_slice(&b);\n            Ok(size)\n        }\n    }\n}\n\nimpl io::Write for TestBuffer {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.write_buf.borrow_mut().extend(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for TestBuffer {\n    fn poll_read(\n        self: Pin<&mut Self>,\n        _: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        let dst = buf.initialize_unfilled();\n        let res = self.get_mut().read(dst).map(|n| buf.advance(n));\n        Poll::Ready(res)\n    }\n}\n\nimpl AsyncWrite for TestBuffer {\n    fn poll_write(\n        self: Pin<&mut Self>,\n        _: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        Poll::Ready(self.get_mut().write(buf))\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n}\n\n/// Async I/O test buffer with ability to incrementally add to the read buffer.\n#[derive(Clone)]\npub struct TestSeqBuffer(Rc<RefCell<TestSeqInner>>);\n\nimpl TestSeqBuffer {\n    /// Create new `TestBuffer` instance with initial read buffer.\n    pub fn new<T>(data: T) -> Self\n    where\n        T: Into<BytesMut>,\n    {\n        Self(Rc::new(RefCell::new(TestSeqInner {\n            read_buf: data.into(),\n            read_closed: false,\n            write_buf: BytesMut::new(),\n            err: None,\n        })))\n    }\n\n    /// Create new empty `TestBuffer` instance.\n    pub fn empty() -> Self {\n        Self::new(BytesMut::new())\n    }\n\n    pub fn read_buf(&self) -> Ref<'_, BytesMut> {\n        Ref::map(self.0.borrow(), |inner| &inner.read_buf)\n    }\n\n    pub fn write_buf(&self) -> Ref<'_, BytesMut> {\n        Ref::map(self.0.borrow(), |inner| &inner.write_buf)\n    }\n\n    pub fn take_write_buf(&self) -> Bytes {\n        self.0.borrow_mut().write_buf.split().freeze()\n    }\n\n    pub fn err(&self) -> Ref<'_, Option<io::Error>> {\n        Ref::map(self.0.borrow(), |inner| &inner.err)\n    }\n\n    /// Add data to read buffer.\n    ///\n    /// # Panics\n    ///\n    /// Panics if called after [`TestSeqBuffer::close_read`] has been called\n    pub fn extend_read_buf<T: AsRef<[u8]>>(&mut self, data: T) {\n        let mut inner = self.0.borrow_mut();\n        if inner.read_closed {\n            panic!(\"Tried to extend the read buffer after calling close_read\");\n        }\n\n        inner.read_buf.extend_from_slice(data.as_ref())\n    }\n\n    /// Closes the [`AsyncRead`]/[`Read`] part of this test buffer.\n    ///\n    /// The current data in the buffer will still be returned by a call to read/poll_read, however,\n    /// after the buffer is empty, it will return `Ok(0)` to signify the EOF condition\n    pub fn close_read(&self) {\n        self.0.borrow_mut().read_closed = true;\n    }\n}\n\npub struct TestSeqInner {\n    read_buf: BytesMut,\n    read_closed: bool,\n    write_buf: BytesMut,\n    err: Option<io::Error>,\n}\n\nimpl io::Read for TestSeqBuffer {\n    fn read(&mut self, dst: &mut [u8]) -> Result<usize, io::Error> {\n        let mut inner = self.0.borrow_mut();\n\n        if inner.read_buf.is_empty() {\n            if let Some(err) = inner.err.take() {\n                Err(err)\n            } else if inner.read_closed {\n                Ok(0)\n            } else {\n                Err(io::Error::new(io::ErrorKind::WouldBlock, \"\"))\n            }\n        } else {\n            let size = std::cmp::min(inner.read_buf.len(), dst.len());\n            let b = inner.read_buf.split_to(size);\n            dst[..size].copy_from_slice(&b);\n            Ok(size)\n        }\n    }\n}\n\nimpl io::Write for TestSeqBuffer {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.0.borrow_mut().write_buf.extend(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for TestSeqBuffer {\n    fn poll_read(\n        self: Pin<&mut Self>,\n        _: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        let dst = buf.initialize_unfilled();\n        let r = self.get_mut().read(dst);\n        match r {\n            Ok(n) => {\n                buf.advance(n);\n                Poll::Ready(Ok(()))\n            }\n            Err(err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending,\n            Err(err) => Poll::Ready(Err(err)),\n        }\n    }\n}\n\nimpl AsyncWrite for TestSeqBuffer {\n    fn poll_write(\n        self: Pin<&mut Self>,\n        _: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        Poll::Ready(self.get_mut().write(buf))\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/codec.rs",
    "content": "use bitflags::bitflags;\nuse bytes::{Bytes, BytesMut};\nuse bytestring::ByteString;\nuse tokio_util::codec::{Decoder, Encoder};\nuse tracing::error;\n\nuse super::{\n    frame::Parser,\n    proto::{CloseReason, OpCode},\n    ProtocolError,\n};\n\n/// A WebSocket message.\n#[derive(Debug, PartialEq, Eq)]\npub enum Message {\n    /// Text message.\n    Text(ByteString),\n\n    /// Binary message.\n    Binary(Bytes),\n\n    /// Continuation.\n    Continuation(Item),\n\n    /// Ping message.\n    Ping(Bytes),\n\n    /// Pong message.\n    Pong(Bytes),\n\n    /// Close message with optional reason.\n    Close(Option<CloseReason>),\n\n    /// No-op. Useful for low-level services.\n    Nop,\n}\n\n/// A WebSocket frame.\n#[derive(Debug, PartialEq, Eq)]\npub enum Frame {\n    /// Text frame. Note that the codec does not validate UTF-8 encoding.\n    Text(Bytes),\n\n    /// Binary frame.\n    Binary(Bytes),\n\n    /// Continuation.\n    Continuation(Item),\n\n    /// Ping message.\n    Ping(Bytes),\n\n    /// Pong message.\n    Pong(Bytes),\n\n    /// Close message with optional reason.\n    Close(Option<CloseReason>),\n}\n\n/// A WebSocket continuation item.\n#[derive(Debug, PartialEq, Eq)]\npub enum Item {\n    FirstText(Bytes),\n    FirstBinary(Bytes),\n    Continue(Bytes),\n    Last(Bytes),\n}\n\n/// WebSocket protocol codec.\n#[derive(Debug, Clone)]\npub struct Codec {\n    flags: Flags,\n    max_size: usize,\n}\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    struct Flags: u8 {\n        const SERVER         = 0b0000_0001;\n        const CONTINUATION   = 0b0000_0010;\n        const W_CONTINUATION = 0b0000_0100;\n    }\n}\n\nimpl Codec {\n    /// Create new WebSocket frames decoder.\n    pub const fn new() -> Codec {\n        Codec {\n            max_size: 65_536,\n            flags: Flags::SERVER,\n        }\n    }\n\n    /// Set max frame size.\n    ///\n    /// By default max size is set to 64KiB.\n    #[must_use = \"This returns the a new Codec, without modifying the original.\"]\n    pub fn max_size(mut self, size: usize) -> Self {\n        self.max_size = size;\n        self\n    }\n\n    /// Set decoder to client mode.\n    ///\n    /// By default decoder works in server mode.\n    #[must_use = \"This returns the a new Codec, without modifying the original.\"]\n    pub fn client_mode(mut self) -> Self {\n        self.flags.remove(Flags::SERVER);\n        self\n    }\n}\n\nimpl Default for Codec {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Encoder<Message> for Codec {\n    type Error = ProtocolError;\n\n    fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> {\n        match item {\n            Message::Text(txt) => Parser::write_message(\n                dst,\n                txt,\n                OpCode::Text,\n                true,\n                !self.flags.contains(Flags::SERVER),\n            ),\n            Message::Binary(bin) => Parser::write_message(\n                dst,\n                bin,\n                OpCode::Binary,\n                true,\n                !self.flags.contains(Flags::SERVER),\n            ),\n            Message::Ping(txt) => Parser::write_message(\n                dst,\n                txt,\n                OpCode::Ping,\n                true,\n                !self.flags.contains(Flags::SERVER),\n            ),\n            Message::Pong(txt) => Parser::write_message(\n                dst,\n                txt,\n                OpCode::Pong,\n                true,\n                !self.flags.contains(Flags::SERVER),\n            ),\n            Message::Close(reason) => {\n                Parser::write_close(dst, reason, !self.flags.contains(Flags::SERVER))\n            }\n            Message::Continuation(cont) => match cont {\n                Item::FirstText(data) => {\n                    if self.flags.contains(Flags::W_CONTINUATION) {\n                        return Err(ProtocolError::ContinuationStarted);\n                    } else {\n                        self.flags.insert(Flags::W_CONTINUATION);\n                        Parser::write_message(\n                            dst,\n                            &data[..],\n                            OpCode::Text,\n                            false,\n                            !self.flags.contains(Flags::SERVER),\n                        )\n                    }\n                }\n                Item::FirstBinary(data) => {\n                    if self.flags.contains(Flags::W_CONTINUATION) {\n                        return Err(ProtocolError::ContinuationStarted);\n                    } else {\n                        self.flags.insert(Flags::W_CONTINUATION);\n                        Parser::write_message(\n                            dst,\n                            &data[..],\n                            OpCode::Binary,\n                            false,\n                            !self.flags.contains(Flags::SERVER),\n                        )\n                    }\n                }\n                Item::Continue(data) => {\n                    if self.flags.contains(Flags::W_CONTINUATION) {\n                        Parser::write_message(\n                            dst,\n                            &data[..],\n                            OpCode::Continue,\n                            false,\n                            !self.flags.contains(Flags::SERVER),\n                        )\n                    } else {\n                        return Err(ProtocolError::ContinuationNotStarted);\n                    }\n                }\n                Item::Last(data) => {\n                    if self.flags.contains(Flags::W_CONTINUATION) {\n                        self.flags.remove(Flags::W_CONTINUATION);\n                        Parser::write_message(\n                            dst,\n                            &data[..],\n                            OpCode::Continue,\n                            true,\n                            !self.flags.contains(Flags::SERVER),\n                        )\n                    } else {\n                        return Err(ProtocolError::ContinuationNotStarted);\n                    }\n                }\n            },\n            Message::Nop => {}\n        }\n        Ok(())\n    }\n}\n\nimpl Decoder for Codec {\n    type Item = Frame;\n    type Error = ProtocolError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        match Parser::parse(src, self.flags.contains(Flags::SERVER), self.max_size) {\n            Ok(Some((finished, opcode, payload))) => {\n                // continuation is not supported\n                if !finished {\n                    return match opcode {\n                        OpCode::Continue => {\n                            if self.flags.contains(Flags::CONTINUATION) {\n                                Ok(Some(Frame::Continuation(Item::Continue(\n                                    payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                                ))))\n                            } else {\n                                Err(ProtocolError::ContinuationNotStarted)\n                            }\n                        }\n                        OpCode::Binary => {\n                            if !self.flags.contains(Flags::CONTINUATION) {\n                                self.flags.insert(Flags::CONTINUATION);\n                                Ok(Some(Frame::Continuation(Item::FirstBinary(\n                                    payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                                ))))\n                            } else {\n                                Err(ProtocolError::ContinuationStarted)\n                            }\n                        }\n                        OpCode::Text => {\n                            if !self.flags.contains(Flags::CONTINUATION) {\n                                self.flags.insert(Flags::CONTINUATION);\n                                Ok(Some(Frame::Continuation(Item::FirstText(\n                                    payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                                ))))\n                            } else {\n                                Err(ProtocolError::ContinuationStarted)\n                            }\n                        }\n                        _ => {\n                            error!(\"Unfinished fragment {:?}\", opcode);\n                            Err(ProtocolError::ContinuationFragment(opcode))\n                        }\n                    };\n                }\n\n                match opcode {\n                    OpCode::Continue => {\n                        if self.flags.contains(Flags::CONTINUATION) {\n                            self.flags.remove(Flags::CONTINUATION);\n                            Ok(Some(Frame::Continuation(Item::Last(\n                                payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                            ))))\n                        } else {\n                            Err(ProtocolError::ContinuationNotStarted)\n                        }\n                    }\n                    OpCode::Bad => Err(ProtocolError::BadOpCode),\n                    OpCode::Close => {\n                        if let Some(ref pl) = payload {\n                            let close_reason = Parser::parse_close_payload(pl);\n                            Ok(Some(Frame::Close(close_reason)))\n                        } else {\n                            Ok(Some(Frame::Close(None)))\n                        }\n                    }\n                    OpCode::Ping => Ok(Some(Frame::Ping(\n                        payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                    ))),\n                    OpCode::Pong => Ok(Some(Frame::Pong(\n                        payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                    ))),\n                    OpCode::Binary => Ok(Some(Frame::Binary(\n                        payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                    ))),\n                    OpCode::Text => Ok(Some(Frame::Text(\n                        payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new),\n                    ))),\n                }\n            }\n            Ok(None) => Ok(None),\n            Err(err) => Err(err),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/dispatcher.rs",
    "content": "use std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse actix_service::{IntoService, Service};\nuse pin_project_lite::pin_project;\n\nuse super::{Codec, Frame, Message};\n\npin_project! {\n    pub struct Dispatcher<S, T>\n    where\n        S: Service<Frame, Response = Message>,\n        S: 'static,\n        T: AsyncRead,\n        T: AsyncWrite,\n    {\n        #[pin]\n        inner: inner::Dispatcher<S, T, Codec, Message>,\n    }\n}\n\nimpl<S, T> Dispatcher<S, T>\nwhere\n    T: AsyncRead + AsyncWrite,\n    S: Service<Frame, Response = Message>,\n    S::Future: 'static,\n    S::Error: 'static,\n{\n    pub fn new<F: IntoService<S, Frame>>(io: T, service: F) -> Self {\n        Dispatcher {\n            inner: inner::Dispatcher::new(Framed::new(io, Codec::new()), service),\n        }\n    }\n\n    pub fn with<F: IntoService<S, Frame>>(framed: Framed<T, Codec>, service: F) -> Self {\n        Dispatcher {\n            inner: inner::Dispatcher::new(framed, service),\n        }\n    }\n}\n\nimpl<S, T> Future for Dispatcher<S, T>\nwhere\n    T: AsyncRead + AsyncWrite,\n    S: Service<Frame, Response = Message>,\n    S::Future: 'static,\n    S::Error: 'static,\n{\n    type Output = Result<(), inner::DispatcherError<S::Error, Codec, Message>>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        self.project().inner.poll(cx)\n    }\n}\n\n/// Framed dispatcher service and related utilities.\nmod inner {\n    // allow dead code since this mod was ripped from actix-utils\n    #![allow(dead_code)]\n\n    use core::{\n        fmt,\n        future::Future,\n        mem,\n        pin::Pin,\n        task::{Context, Poll},\n    };\n\n    use actix_codec::Framed;\n    use actix_service::{IntoService, Service};\n    use futures_core::stream::Stream;\n    use local_channel::mpsc;\n    use pin_project_lite::pin_project;\n    use tokio::io::{AsyncRead, AsyncWrite};\n    use tokio_util::codec::{Decoder, Encoder};\n    use tracing::debug;\n\n    use crate::{body::BoxBody, Response};\n\n    /// Framed transport errors\n    pub enum DispatcherError<E, U, I>\n    where\n        U: Encoder<I> + Decoder,\n    {\n        /// Inner service error.\n        Service(E),\n\n        /// Frame encoding error.\n        Encoder(<U as Encoder<I>>::Error),\n\n        /// Frame decoding error.\n        Decoder(<U as Decoder>::Error),\n    }\n\n    impl<E, U, I> From<E> for DispatcherError<E, U, I>\n    where\n        U: Encoder<I> + Decoder,\n    {\n        fn from(err: E) -> Self {\n            DispatcherError::Service(err)\n        }\n    }\n\n    impl<E, U, I> fmt::Debug for DispatcherError<E, U, I>\n    where\n        E: fmt::Debug,\n        U: Encoder<I> + Decoder,\n        <U as Encoder<I>>::Error: fmt::Debug,\n        <U as Decoder>::Error: fmt::Debug,\n    {\n        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {\n            match *self {\n                DispatcherError::Service(ref err) => {\n                    write!(fmt, \"DispatcherError::Service({err:?})\")\n                }\n                DispatcherError::Encoder(ref err) => {\n                    write!(fmt, \"DispatcherError::Encoder({err:?})\")\n                }\n                DispatcherError::Decoder(ref err) => {\n                    write!(fmt, \"DispatcherError::Decoder({err:?})\")\n                }\n            }\n        }\n    }\n\n    impl<E, U, I> fmt::Display for DispatcherError<E, U, I>\n    where\n        E: fmt::Display,\n        U: Encoder<I> + Decoder,\n        <U as Encoder<I>>::Error: fmt::Debug,\n        <U as Decoder>::Error: fmt::Debug,\n    {\n        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {\n            match *self {\n                DispatcherError::Service(ref err) => write!(fmt, \"{err}\"),\n                DispatcherError::Encoder(ref err) => write!(fmt, \"{err:?}\"),\n                DispatcherError::Decoder(ref err) => write!(fmt, \"{err:?}\"),\n            }\n        }\n    }\n\n    impl<E, U, I> From<DispatcherError<E, U, I>> for Response<BoxBody>\n    where\n        E: fmt::Debug + fmt::Display,\n        U: Encoder<I> + Decoder,\n        <U as Encoder<I>>::Error: fmt::Debug,\n        <U as Decoder>::Error: fmt::Debug,\n    {\n        fn from(err: DispatcherError<E, U, I>) -> Self {\n            Response::internal_server_error().set_body(BoxBody::new(err.to_string()))\n        }\n    }\n\n    /// Message type wrapper for signalling end of message stream.\n    pub enum Message<T> {\n        /// Message item.\n        Item(T),\n\n        /// Signal from service to flush all messages and stop processing.\n        Close,\n    }\n\n    pin_project! {\n        /// A future that reads frames from a [`Framed`] object and passes them to a [`Service`].\n        pub struct Dispatcher<S, T, U, I>\n        where\n            S: Service<<U as Decoder>::Item, Response = I>,\n            S::Error: 'static,\n            S::Future: 'static,\n            T: AsyncRead,\n            T: AsyncWrite,\n            U: Encoder<I>,\n            U: Decoder,\n            I: 'static,\n            <U as Encoder<I>>::Error: fmt::Debug,\n        {\n            service: S,\n            state: State<S, U, I>,\n            #[pin]\n            framed: Framed<T, U>,\n            rx: mpsc::Receiver<Result<Message<I>, S::Error>>,\n            tx: mpsc::Sender<Result<Message<I>, S::Error>>,\n        }\n    }\n\n    enum State<S, U, I>\n    where\n        S: Service<<U as Decoder>::Item>,\n        U: Encoder<I> + Decoder,\n    {\n        Processing,\n        Error(DispatcherError<S::Error, U, I>),\n        FramedError(DispatcherError<S::Error, U, I>),\n        FlushAndStop,\n        Stopping,\n    }\n\n    impl<S, U, I> State<S, U, I>\n    where\n        S: Service<<U as Decoder>::Item>,\n        U: Encoder<I> + Decoder,\n    {\n        fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {\n            match mem::replace(self, State::Processing) {\n                State::Error(err) => err,\n                _ => panic!(),\n            }\n        }\n\n        fn take_framed_error(&mut self) -> DispatcherError<S::Error, U, I> {\n            match mem::replace(self, State::Processing) {\n                State::FramedError(err) => err,\n                _ => panic!(),\n            }\n        }\n    }\n\n    impl<S, T, U, I> Dispatcher<S, T, U, I>\n    where\n        S: Service<<U as Decoder>::Item, Response = I>,\n        S::Error: 'static,\n        S::Future: 'static,\n        T: AsyncRead + AsyncWrite,\n        U: Decoder + Encoder<I>,\n        I: 'static,\n        <U as Decoder>::Error: fmt::Debug,\n        <U as Encoder<I>>::Error: fmt::Debug,\n    {\n        /// Create new `Dispatcher`.\n        pub fn new<F>(framed: Framed<T, U>, service: F) -> Self\n        where\n            F: IntoService<S, <U as Decoder>::Item>,\n        {\n            let (tx, rx) = mpsc::channel();\n            Dispatcher {\n                framed,\n                rx,\n                tx,\n                service: service.into_service(),\n                state: State::Processing,\n            }\n        }\n\n        /// Construct new `Dispatcher` instance with customer `mpsc::Receiver`\n        pub fn with_rx<F>(\n            framed: Framed<T, U>,\n            service: F,\n            rx: mpsc::Receiver<Result<Message<I>, S::Error>>,\n        ) -> Self\n        where\n            F: IntoService<S, <U as Decoder>::Item>,\n        {\n            let tx = rx.sender();\n            Dispatcher {\n                framed,\n                rx,\n                tx,\n                service: service.into_service(),\n                state: State::Processing,\n            }\n        }\n\n        /// Get sender handle.\n        pub fn tx(&self) -> mpsc::Sender<Result<Message<I>, S::Error>> {\n            self.tx.clone()\n        }\n\n        /// Get reference to a service wrapped by `Dispatcher` instance.\n        pub fn service(&self) -> &S {\n            &self.service\n        }\n\n        /// Get mutable reference to a service wrapped by `Dispatcher` instance.\n        pub fn service_mut(&mut self) -> &mut S {\n            &mut self.service\n        }\n\n        /// Get reference to a framed instance wrapped by `Dispatcher` instance.\n        pub fn framed(&self) -> &Framed<T, U> {\n            &self.framed\n        }\n\n        /// Get mutable reference to a framed instance wrapped by `Dispatcher` instance.\n        pub fn framed_mut(&mut self) -> &mut Framed<T, U> {\n            &mut self.framed\n        }\n\n        /// Read from framed object.\n        fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool\n        where\n            S: Service<<U as Decoder>::Item, Response = I>,\n            S::Error: 'static,\n            S::Future: 'static,\n            T: AsyncRead + AsyncWrite,\n            U: Decoder + Encoder<I>,\n            I: 'static,\n            <U as Encoder<I>>::Error: fmt::Debug,\n        {\n            loop {\n                let this = self.as_mut().project();\n                match this.service.poll_ready(cx) {\n                    Poll::Ready(Ok(_)) => {\n                        let item = match this.framed.next_item(cx) {\n                            Poll::Ready(Some(Ok(el))) => el,\n                            Poll::Ready(Some(Err(err))) => {\n                                *this.state = State::FramedError(DispatcherError::Decoder(err));\n                                return true;\n                            }\n                            Poll::Pending => return false,\n                            Poll::Ready(None) => {\n                                *this.state = State::Stopping;\n                                return true;\n                            }\n                        };\n\n                        let tx = this.tx.clone();\n                        let fut = this.service.call(item);\n                        actix_rt::spawn(async move {\n                            let item = fut.await;\n                            let _ = tx.send(item.map(Message::Item));\n                        });\n                    }\n                    Poll::Pending => return false,\n                    Poll::Ready(Err(err)) => {\n                        *this.state = State::Error(DispatcherError::Service(err));\n                        return true;\n                    }\n                }\n            }\n        }\n\n        /// Write to framed object.\n        fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool\n        where\n            S: Service<<U as Decoder>::Item, Response = I>,\n            S::Error: 'static,\n            S::Future: 'static,\n            T: AsyncRead + AsyncWrite,\n            U: Decoder + Encoder<I>,\n            I: 'static,\n            <U as Encoder<I>>::Error: fmt::Debug,\n        {\n            loop {\n                let mut this = self.as_mut().project();\n                while !this.framed.is_write_buf_full() {\n                    match Pin::new(&mut this.rx).poll_next(cx) {\n                        Poll::Ready(Some(Ok(Message::Item(msg)))) => {\n                            if let Err(err) = this.framed.as_mut().write(msg) {\n                                *this.state = State::FramedError(DispatcherError::Encoder(err));\n                                return true;\n                            }\n                        }\n                        Poll::Ready(Some(Ok(Message::Close))) => {\n                            *this.state = State::FlushAndStop;\n                            return true;\n                        }\n                        Poll::Ready(Some(Err(err))) => {\n                            *this.state = State::Error(DispatcherError::Service(err));\n                            return true;\n                        }\n                        Poll::Ready(None) | Poll::Pending => break,\n                    }\n                }\n\n                if !this.framed.is_write_buf_empty() {\n                    match this.framed.flush(cx) {\n                        Poll::Pending => break,\n                        Poll::Ready(Ok(_)) => {}\n                        Poll::Ready(Err(err)) => {\n                            debug!(\"Error sending data: {:?}\", err);\n                            *this.state = State::FramedError(DispatcherError::Encoder(err));\n                            return true;\n                        }\n                    }\n                } else {\n                    break;\n                }\n            }\n\n            false\n        }\n    }\n\n    impl<S, T, U, I> Future for Dispatcher<S, T, U, I>\n    where\n        S: Service<<U as Decoder>::Item, Response = I>,\n        S::Error: 'static,\n        S::Future: 'static,\n        T: AsyncRead + AsyncWrite,\n        U: Decoder + Encoder<I>,\n        I: 'static,\n        <U as Encoder<I>>::Error: fmt::Debug,\n        <U as Decoder>::Error: fmt::Debug,\n    {\n        type Output = Result<(), DispatcherError<S::Error, U, I>>;\n\n        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n            loop {\n                let this = self.as_mut().project();\n\n                return match this.state {\n                    State::Processing => {\n                        if self.as_mut().poll_read(cx) || self.as_mut().poll_write(cx) {\n                            continue;\n                        } else {\n                            Poll::Pending\n                        }\n                    }\n                    State::Error(_) => {\n                        // flush write buffer\n                        if !this.framed.is_write_buf_empty() && this.framed.flush(cx).is_pending() {\n                            return Poll::Pending;\n                        }\n                        Poll::Ready(Err(this.state.take_error()))\n                    }\n                    State::FlushAndStop => {\n                        if !this.framed.is_write_buf_empty() {\n                            this.framed.flush(cx).map(|res| {\n                                if let Err(err) = res {\n                                    debug!(\"Error sending data: {:?}\", err);\n                                }\n\n                                Ok(())\n                            })\n                        } else {\n                            Poll::Ready(Ok(()))\n                        }\n                    }\n                    State::FramedError(_) => Poll::Ready(Err(this.state.take_framed_error())),\n                    State::Stopping => Poll::Ready(Ok(())),\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/frame.rs",
    "content": "use std::cmp::min;\n\nuse bytes::{Buf, BufMut, BytesMut};\nuse tracing::debug;\n\nuse super::{\n    mask::apply_mask,\n    proto::{CloseCode, CloseReason, OpCode},\n    ProtocolError,\n};\n\n/// A struct representing a WebSocket frame.\n#[derive(Debug)]\npub struct Parser;\n\nimpl Parser {\n    fn parse_metadata(\n        src: &[u8],\n        server: bool,\n    ) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> {\n        let chunk_len = src.len();\n\n        let mut idx = 2;\n        if chunk_len < 2 {\n            return Ok(None);\n        }\n\n        let first = src[0];\n        let second = src[1];\n        let finished = first & 0x80 != 0;\n\n        // check masking\n        let masked = second & 0x80 != 0;\n        if !masked && server {\n            return Err(ProtocolError::UnmaskedFrame);\n        } else if masked && !server {\n            return Err(ProtocolError::MaskedFrame);\n        }\n\n        // Op code\n        let opcode = OpCode::from(first & 0x0F);\n\n        if let OpCode::Bad = opcode {\n            return Err(ProtocolError::InvalidOpcode(first & 0x0F));\n        }\n\n        let len = second & 0x7F;\n        let length = if len == 126 {\n            if chunk_len < 4 {\n                return Ok(None);\n            }\n            let len = usize::from(u16::from_be_bytes(\n                TryFrom::try_from(&src[idx..idx + 2]).unwrap(),\n            ));\n            idx += 2;\n            len\n        } else if len == 127 {\n            if chunk_len < 10 {\n                return Ok(None);\n            }\n            let len = u64::from_be_bytes(TryFrom::try_from(&src[idx..idx + 8]).unwrap());\n            idx += 8;\n            len as usize\n        } else {\n            len as usize\n        };\n\n        let mask = if server {\n            if chunk_len < idx + 4 {\n                return Ok(None);\n            }\n\n            let mask = TryFrom::try_from(&src[idx..idx + 4]).unwrap();\n\n            idx += 4;\n\n            Some(mask)\n        } else {\n            None\n        };\n\n        Ok(Some((idx, finished, opcode, length, mask)))\n    }\n\n    /// Parse the input stream into a frame.\n    pub fn parse(\n        src: &mut BytesMut,\n        server: bool,\n        max_size: usize,\n    ) -> Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError> {\n        // try to parse ws frame metadata\n        let (idx, finished, opcode, length, mask) = match Parser::parse_metadata(src, server)? {\n            None => return Ok(None),\n            Some(res) => res,\n        };\n\n        let frame_len = match idx.checked_add(length) {\n            Some(len) => len,\n            None => return Err(ProtocolError::Overflow),\n        };\n\n        // not enough data\n        if src.len() < frame_len {\n            let min_length = min(length, max_size);\n            let required_cap = match idx.checked_add(min_length) {\n                Some(cap) => cap,\n                None => return Err(ProtocolError::Overflow),\n            };\n\n            if src.capacity() < required_cap {\n                src.reserve(required_cap - src.capacity());\n            }\n            return Ok(None);\n        }\n\n        // remove prefix\n        src.advance(idx);\n\n        // check for max allowed size\n        if length > max_size {\n            // drop the payload\n            src.advance(length);\n            return Err(ProtocolError::Overflow);\n        }\n\n        // no need for body\n        if length == 0 {\n            return Ok(Some((finished, opcode, None)));\n        }\n\n        let mut data = src.split_to(length);\n\n        // control frames must have length <= 125\n        match opcode {\n            OpCode::Ping | OpCode::Pong if length > 125 => {\n                return Err(ProtocolError::InvalidLength(length));\n            }\n            OpCode::Close if length > 125 => {\n                debug!(\"Received close frame with payload length exceeding 125. Morphing to protocol close frame.\");\n                return Ok(Some((true, OpCode::Close, None)));\n            }\n            _ => {}\n        }\n\n        // unmask\n        if let Some(mask) = mask {\n            apply_mask(&mut data, mask);\n        }\n\n        Ok(Some((finished, opcode, Some(data))))\n    }\n\n    /// Parse the payload of a close frame.\n    pub fn parse_close_payload(payload: &[u8]) -> Option<CloseReason> {\n        if payload.len() >= 2 {\n            let raw_code = u16::from_be_bytes(TryFrom::try_from(&payload[..2]).unwrap());\n            let code = CloseCode::from(raw_code);\n            let description = if payload.len() > 2 {\n                Some(String::from_utf8_lossy(&payload[2..]).into())\n            } else {\n                None\n            };\n            Some(CloseReason { code, description })\n        } else {\n            None\n        }\n    }\n\n    /// Generate binary representation\n    pub fn write_message<B: AsRef<[u8]>>(\n        dst: &mut BytesMut,\n        pl: B,\n        op: OpCode,\n        fin: bool,\n        mask: bool,\n    ) {\n        let payload = pl.as_ref();\n        let one: u8 = if fin {\n            0x80 | Into::<u8>::into(op)\n        } else {\n            op.into()\n        };\n        let payload_len = payload.len();\n        let (two, p_len) = if mask {\n            (0x80, payload_len + 4)\n        } else {\n            (0, payload_len)\n        };\n\n        if payload_len < 126 {\n            dst.reserve(p_len + 2);\n            dst.put_slice(&[one, two | payload_len as u8]);\n        } else if payload_len <= 65_535 {\n            dst.reserve(p_len + 4);\n            dst.put_slice(&[one, two | 126]);\n            dst.put_u16(payload_len as u16);\n        } else {\n            dst.reserve(p_len + 10);\n            dst.put_slice(&[one, two | 127]);\n            dst.put_u64(payload_len as u64);\n        };\n\n        if mask {\n            let mask = rand::random::<[u8; 4]>();\n            dst.put_slice(mask.as_ref());\n            dst.put_slice(payload.as_ref());\n            let pos = dst.len() - payload_len;\n            apply_mask(&mut dst[pos..], mask);\n        } else {\n            dst.put_slice(payload.as_ref());\n        }\n    }\n\n    /// Create a new Close control frame.\n    #[inline]\n    pub fn write_close(dst: &mut BytesMut, reason: Option<CloseReason>, mask: bool) {\n        let payload = match reason {\n            None => Vec::new(),\n            Some(reason) => {\n                let mut payload = Into::<u16>::into(reason.code).to_be_bytes().to_vec();\n                if let Some(description) = reason.description {\n                    payload.extend(description.as_bytes());\n                }\n                payload\n            }\n        };\n\n        Parser::write_message(dst, payload, OpCode::Close, true, mask)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::Bytes;\n\n    use super::*;\n\n    struct F {\n        finished: bool,\n        opcode: OpCode,\n        payload: Bytes,\n    }\n\n    fn is_none(frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>) -> bool {\n        matches!(*frm, Ok(None))\n    }\n\n    fn extract(frm: Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>) -> F {\n        match frm {\n            Ok(Some((finished, opcode, payload))) => F {\n                finished,\n                opcode,\n                payload: payload\n                    .map(|b| b.freeze())\n                    .unwrap_or_else(|| Bytes::from(\"\")),\n            },\n            _ => unreachable!(\"error\"),\n        }\n    }\n\n    #[test]\n    fn test_parse() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);\n        assert!(is_none(&Parser::parse(&mut buf, false, 1024)));\n\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);\n        buf.extend(b\"1\");\n\n        let frame = extract(Parser::parse(&mut buf, false, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert_eq!(frame.payload.as_ref(), &b\"1\"[..]);\n    }\n\n    #[test]\n    fn test_parse_length0() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0000u8][..]);\n        let frame = extract(Parser::parse(&mut buf, false, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert!(frame.payload.is_empty());\n    }\n\n    #[test]\n    fn test_parse_length2() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);\n        assert!(is_none(&Parser::parse(&mut buf, false, 1024)));\n\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);\n        buf.extend(&[0u8, 4u8][..]);\n        buf.extend(b\"1234\");\n\n        let frame = extract(Parser::parse(&mut buf, false, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert_eq!(frame.payload.as_ref(), &b\"1234\"[..]);\n    }\n\n    #[test]\n    fn test_parse_length4() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);\n        assert!(is_none(&Parser::parse(&mut buf, false, 1024)));\n\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);\n        buf.extend(&[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 4u8][..]);\n        buf.extend(b\"1234\");\n\n        let frame = extract(Parser::parse(&mut buf, false, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert_eq!(frame.payload.as_ref(), &b\"1234\"[..]);\n    }\n\n    #[test]\n    fn test_parse_frame_mask() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b1000_0001u8][..]);\n        buf.extend(b\"0001\");\n        buf.extend(b\"1\");\n\n        assert!(Parser::parse(&mut buf, false, 1024).is_err());\n\n        let frame = extract(Parser::parse(&mut buf, true, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert_eq!(frame.payload, Bytes::from(vec![1u8]));\n    }\n\n    #[test]\n    fn test_parse_frame_no_mask() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);\n        buf.extend([1u8]);\n\n        assert!(Parser::parse(&mut buf, true, 1024).is_err());\n\n        let frame = extract(Parser::parse(&mut buf, false, 1024));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Text);\n        assert_eq!(frame.payload, Bytes::from(vec![1u8]));\n    }\n\n    #[test]\n    fn test_parse_frame_max_size() {\n        let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0010u8][..]);\n        buf.extend([1u8, 1u8]);\n\n        assert!(Parser::parse(&mut buf, true, 1).is_err());\n\n        if let Err(ProtocolError::Overflow) = Parser::parse(&mut buf, false, 0) {\n        } else {\n            unreachable!(\"error\");\n        }\n    }\n\n    #[test]\n    fn test_parse_frame_max_size_recoverability() {\n        let mut buf = BytesMut::new();\n        // The first text frame with length == 2, payload doesn't matter.\n        buf.extend([0b0000_0001u8, 0b0000_0010u8, 0b0000_0000u8, 0b0000_0000u8]);\n        // Next binary frame with length == 2 and payload == `[0x1111_1111u8, 0x1111_1111u8]`.\n        buf.extend([0b0000_0010u8, 0b0000_0010u8, 0b1111_1111u8, 0b1111_1111u8]);\n\n        assert_eq!(buf.len(), 8);\n        assert!(matches!(\n            Parser::parse(&mut buf, false, 1),\n            Err(ProtocolError::Overflow)\n        ));\n        assert_eq!(buf.len(), 4);\n        let frame = extract(Parser::parse(&mut buf, false, 2));\n        assert!(!frame.finished);\n        assert_eq!(frame.opcode, OpCode::Binary);\n        assert_eq!(\n            frame.payload,\n            Bytes::from(vec![0b1111_1111u8, 0b1111_1111u8])\n        );\n        assert_eq!(buf.len(), 0);\n    }\n\n    #[test]\n    fn test_ping_frame() {\n        let mut buf = BytesMut::new();\n        Parser::write_message(&mut buf, Vec::from(\"data\"), OpCode::Ping, true, false);\n\n        let mut v = vec![137u8, 4u8];\n        v.extend(b\"data\");\n        assert_eq!(&buf[..], &v[..]);\n    }\n\n    #[test]\n    fn test_pong_frame() {\n        let mut buf = BytesMut::new();\n        Parser::write_message(&mut buf, Vec::from(\"data\"), OpCode::Pong, true, false);\n\n        let mut v = vec![138u8, 4u8];\n        v.extend(b\"data\");\n        assert_eq!(&buf[..], &v[..]);\n    }\n\n    #[test]\n    fn test_close_frame() {\n        let mut buf = BytesMut::new();\n        let reason = (CloseCode::Normal, \"data\");\n        Parser::write_close(&mut buf, Some(reason.into()), false);\n\n        let mut v = vec![136u8, 6u8, 3u8, 232u8];\n        v.extend(b\"data\");\n        assert_eq!(&buf[..], &v[..]);\n    }\n\n    #[test]\n    fn test_empty_close_frame() {\n        let mut buf = BytesMut::new();\n        Parser::write_close(&mut buf, None, false);\n        assert_eq!(&buf[..], &vec![0x88, 0x00][..]);\n    }\n\n    #[test]\n    fn test_parse_length_overflow() {\n        let buf: [u8; 14] = [\n            0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x0e, 0x8f,\n        ];\n        let mut buf = BytesMut::from(&buf[..]);\n        let result = Parser::parse(&mut buf, true, 65536);\n        assert!(matches!(result, Err(ProtocolError::Overflow)));\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/mask.rs",
    "content": "//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)\n\n/// Mask/unmask a frame.\n#[inline]\npub fn apply_mask(buf: &mut [u8], mask: [u8; 4]) {\n    apply_mask_fast32(buf, mask)\n}\n\n/// A safe unoptimized mask application.\n#[inline]\nfn apply_mask_fallback(buf: &mut [u8], mask: [u8; 4]) {\n    for (i, byte) in buf.iter_mut().enumerate() {\n        *byte ^= mask[i & 3];\n    }\n}\n\n/// Faster version of `apply_mask()` which operates on 4-byte blocks.\n#[inline]\npub fn apply_mask_fast32(buf: &mut [u8], mask: [u8; 4]) {\n    let mask_u32 = u32::from_ne_bytes(mask);\n\n    // SAFETY:\n    //\n    // buf is a valid slice borrowed mutably from bytes::BytesMut.\n    //\n    // un aligned prefix and suffix would be mask/unmask per byte.\n    // proper aligned middle slice goes into fast path and operates on 4-byte blocks.\n    let (prefix, words, suffix) = unsafe { buf.align_to_mut::<u32>() };\n    apply_mask_fallback(prefix, mask);\n    let head = prefix.len() & 3;\n    let mask_u32 = if head > 0 {\n        if cfg!(target_endian = \"big\") {\n            mask_u32.rotate_left(8 * head as u32)\n        } else {\n            mask_u32.rotate_right(8 * head as u32)\n        }\n    } else {\n        mask_u32\n    };\n    for word in words.iter_mut() {\n        *word ^= mask_u32;\n    }\n    apply_mask_fallback(suffix, mask_u32.to_ne_bytes());\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_apply_mask() {\n        let mask = [0x6d, 0xb6, 0xb2, 0x80];\n        let unmasked = [\n            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9,\n            0x12, 0x03,\n        ];\n\n        for data_len in 0..=unmasked.len() {\n            let unmasked = &unmasked[0..data_len];\n            // Check masking with different alignment.\n            for off in 0..=3 {\n                if unmasked.len() < off {\n                    continue;\n                }\n                let mut masked = unmasked.to_vec();\n                apply_mask_fallback(&mut masked[off..], mask);\n\n                let mut masked_fast = unmasked.to_vec();\n                apply_mask_fast32(&mut masked_fast[off..], mask);\n\n                assert_eq!(masked, masked_fast);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/mod.rs",
    "content": "//! WebSocket protocol implementation.\n//!\n//! To setup a WebSocket, first perform the WebSocket handshake then on success convert `Payload` into a\n//! `WsStream` stream and then use `WsWriter` to communicate with the peer.\n\nuse std::io;\n\nuse derive_more::{Display, Error, From};\nuse http::{header, Method, StatusCode};\n\nuse crate::{body::BoxBody, header::HeaderValue, RequestHead, Response, ResponseBuilder};\n\nmod codec;\nmod dispatcher;\nmod frame;\nmod mask;\nmod proto;\n\npub use self::{\n    codec::{Codec, Frame, Item, Message},\n    dispatcher::Dispatcher,\n    frame::Parser,\n    proto::{hash_key, CloseCode, CloseReason, OpCode},\n};\n\n/// WebSocket protocol errors.\n#[derive(Debug, Display, Error, From)]\npub enum ProtocolError {\n    /// Received an unmasked frame from client.\n    #[display(\"received an unmasked frame from client\")]\n    UnmaskedFrame,\n\n    /// Received a masked frame from server.\n    #[display(\"received a masked frame from server\")]\n    MaskedFrame,\n\n    /// Encountered invalid opcode.\n    #[display(\"invalid opcode ({})\", _0)]\n    InvalidOpcode(#[error(not(source))] u8),\n\n    /// Invalid control frame length\n    #[display(\"invalid control frame length ({})\", _0)]\n    InvalidLength(#[error(not(source))] usize),\n\n    /// Bad opcode.\n    #[display(\"bad opcode\")]\n    BadOpCode,\n\n    /// A payload reached size limit.\n    #[display(\"payload reached size limit\")]\n    Overflow,\n\n    /// Continuation has not started.\n    #[display(\"continuation has not started\")]\n    ContinuationNotStarted,\n\n    /// Received new continuation but it is already started.\n    #[display(\"received new continuation but it has already started\")]\n    ContinuationStarted,\n\n    /// Unknown continuation fragment.\n    #[display(\"unknown continuation fragment: {}\", _0)]\n    ContinuationFragment(#[error(not(source))] OpCode),\n\n    /// I/O error.\n    #[display(\"I/O error: {}\", _0)]\n    Io(io::Error),\n}\n\n/// WebSocket handshake errors\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]\npub enum HandshakeError {\n    /// Only get method is allowed.\n    #[display(\"method not allowed\")]\n    GetMethodRequired,\n\n    /// Upgrade header if not set to WebSocket.\n    #[display(\"WebSocket upgrade is expected\")]\n    NoWebsocketUpgrade,\n\n    /// Connection header is not set to upgrade.\n    #[display(\"connection upgrade is expected\")]\n    NoConnectionUpgrade,\n\n    /// WebSocket version header is not set.\n    #[display(\"WebSocket version header is required\")]\n    NoVersionHeader,\n\n    /// Unsupported WebSocket version.\n    #[display(\"unsupported WebSocket version\")]\n    UnsupportedVersion,\n\n    /// WebSocket key is not set or wrong.\n    #[display(\"unknown WebSocket key\")]\n    BadWebsocketKey,\n}\n\nimpl From<HandshakeError> for Response<BoxBody> {\n    fn from(err: HandshakeError) -> Self {\n        match err {\n            HandshakeError::GetMethodRequired => {\n                let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);\n                #[allow(clippy::declare_interior_mutable_const)]\n                const HV_GET: HeaderValue = HeaderValue::from_static(\"GET\");\n                res.headers_mut().insert(header::ALLOW, HV_GET);\n                res\n            }\n\n            HandshakeError::NoWebsocketUpgrade => {\n                let mut res = Response::bad_request();\n                res.head_mut().reason = Some(\"No WebSocket Upgrade header found\");\n                res\n            }\n\n            HandshakeError::NoConnectionUpgrade => {\n                let mut res = Response::bad_request();\n                res.head_mut().reason = Some(\"No Connection upgrade\");\n                res\n            }\n\n            HandshakeError::NoVersionHeader => {\n                let mut res = Response::bad_request();\n                res.head_mut().reason = Some(\"WebSocket version header is required\");\n                res\n            }\n\n            HandshakeError::UnsupportedVersion => {\n                let mut res = Response::bad_request();\n                res.head_mut().reason = Some(\"Unsupported WebSocket version\");\n                res\n            }\n\n            HandshakeError::BadWebsocketKey => {\n                let mut res = Response::bad_request();\n                res.head_mut().reason = Some(\"Handshake error\");\n                res\n            }\n        }\n    }\n}\n\nimpl From<&HandshakeError> for Response<BoxBody> {\n    fn from(err: &HandshakeError) -> Self {\n        (*err).into()\n    }\n}\n\n/// Verify WebSocket handshake request and create handshake response.\npub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {\n    verify_handshake(req)?;\n    Ok(handshake_response(req))\n}\n\n/// Verify WebSocket handshake request.\npub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> {\n    // WebSocket accepts only GET\n    if req.method != Method::GET {\n        return Err(HandshakeError::GetMethodRequired);\n    }\n\n    // Check for \"UPGRADE\" to WebSocket header\n    let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {\n        if let Ok(s) = hdr.to_str() {\n            s.to_ascii_lowercase().contains(\"websocket\")\n        } else {\n            false\n        }\n    } else {\n        false\n    };\n    if !has_hdr {\n        return Err(HandshakeError::NoWebsocketUpgrade);\n    }\n\n    // Upgrade connection\n    if !req.upgrade() {\n        return Err(HandshakeError::NoConnectionUpgrade);\n    }\n\n    // check supported version\n    if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {\n        return Err(HandshakeError::NoVersionHeader);\n    }\n    let supported_ver = {\n        if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {\n            hdr == \"13\" || hdr == \"8\" || hdr == \"7\"\n        } else {\n            false\n        }\n    };\n    if !supported_ver {\n        return Err(HandshakeError::UnsupportedVersion);\n    }\n\n    // check client handshake for validity\n    if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {\n        return Err(HandshakeError::BadWebsocketKey);\n    }\n    Ok(())\n}\n\n/// Create WebSocket handshake response.\n///\n/// This function returns handshake `Response`, ready to send to peer.\npub fn handshake_response(req: &RequestHead) -> ResponseBuilder {\n    let key = {\n        let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();\n        proto::hash_key(key.as_ref())\n    };\n\n    Response::build(StatusCode::SWITCHING_PROTOCOLS)\n        .upgrade(\"websocket\")\n        .insert_header((\n            header::SEC_WEBSOCKET_ACCEPT,\n            // key is known to be header value safe ascii\n            HeaderValue::from_bytes(&key).unwrap(),\n        ))\n        .take()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{header, test::TestRequest};\n\n    #[test]\n    fn test_handshake() {\n        let req = TestRequest::default().method(Method::POST).finish();\n        assert_eq!(\n            HandshakeError::GetMethodRequired,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default().finish();\n        assert_eq!(\n            HandshakeError::NoWebsocketUpgrade,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((header::UPGRADE, header::HeaderValue::from_static(\"test\")))\n            .finish();\n        assert_eq!(\n            HandshakeError::NoWebsocketUpgrade,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .finish();\n        assert_eq!(\n            HandshakeError::NoConnectionUpgrade,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .finish();\n        assert_eq!(\n            HandshakeError::NoVersionHeader,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"5\"),\n            ))\n            .finish();\n        assert_eq!(\n            HandshakeError::UnsupportedVersion,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .finish();\n        assert_eq!(\n            HandshakeError::BadWebsocketKey,\n            verify_handshake(req.head()).unwrap_err(),\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_KEY,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .finish();\n        assert_eq!(\n            StatusCode::SWITCHING_PROTOCOLS,\n            handshake_response(req.head()).finish().status()\n        );\n    }\n\n    #[test]\n    fn test_ws_error_http_response() {\n        let resp: Response<BoxBody> = HandshakeError::GetMethodRequired.into();\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n        let resp: Response<BoxBody> = HandshakeError::NoWebsocketUpgrade.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n        let resp: Response<BoxBody> = HandshakeError::NoConnectionUpgrade.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n        let resp: Response<BoxBody> = HandshakeError::NoVersionHeader.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n        let resp: Response<BoxBody> = HandshakeError::UnsupportedVersion.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n        let resp: Response<BoxBody> = HandshakeError::BadWebsocketKey.into();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n}\n"
  },
  {
    "path": "actix-http/src/ws/proto.rs",
    "content": "use std::fmt;\n\nuse base64::prelude::*;\nuse tracing::error;\n\n/// Operation codes defined in [RFC 6455 §11.8].\n///\n/// [RFC 6455]: https://datatracker.ietf.org/doc/html/rfc6455#section-11.8\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum OpCode {\n    /// Indicates a continuation frame of a fragmented message.\n    Continue,\n\n    /// Indicates a text data frame.\n    Text,\n\n    /// Indicates a binary data frame.\n    Binary,\n\n    /// Indicates a close control frame.\n    Close,\n\n    /// Indicates a ping control frame.\n    Ping,\n\n    /// Indicates a pong control frame.\n    Pong,\n\n    /// Indicates an invalid opcode was received.\n    Bad,\n}\n\nimpl fmt::Display for OpCode {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        use OpCode::*;\n\n        match self {\n            Continue => write!(f, \"CONTINUE\"),\n            Text => write!(f, \"TEXT\"),\n            Binary => write!(f, \"BINARY\"),\n            Close => write!(f, \"CLOSE\"),\n            Ping => write!(f, \"PING\"),\n            Pong => write!(f, \"PONG\"),\n            Bad => write!(f, \"BAD\"),\n        }\n    }\n}\n\nimpl From<OpCode> for u8 {\n    fn from(op: OpCode) -> u8 {\n        use self::OpCode::*;\n\n        match op {\n            Continue => 0,\n            Text => 1,\n            Binary => 2,\n            Close => 8,\n            Ping => 9,\n            Pong => 10,\n            Bad => {\n                error!(\"Attempted to convert invalid opcode to u8. This is a bug.\");\n                8 // if this somehow happens, a close frame will help us tear down quickly\n            }\n        }\n    }\n}\n\nimpl From<u8> for OpCode {\n    fn from(byte: u8) -> OpCode {\n        use self::OpCode::*;\n\n        match byte {\n            0 => Continue,\n            1 => Text,\n            2 => Binary,\n            8 => Close,\n            9 => Ping,\n            10 => Pong,\n            _ => Bad,\n        }\n    }\n}\n\n/// Status code used to indicate why an endpoint is closing the WebSocket connection.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum CloseCode {\n    /// Indicates a normal closure, meaning that the purpose for which the connection was\n    /// established has been fulfilled.\n    Normal,\n\n    /// Indicates that an endpoint is \"going away\", such as a server going down or a browser having\n    /// navigated away from a page.\n    Away,\n\n    /// Indicates that an endpoint is terminating the connection due to a protocol error.\n    Protocol,\n\n    /// Indicates that an endpoint is terminating the connection because it has received a type of\n    /// data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it\n    /// receives a binary message).\n    Unsupported,\n\n    /// Indicates an abnormal closure. If the abnormal closure was due to an error, this close code\n    /// will not be used. Instead, the `on_error` method of the handler will be called with\n    /// the error. However, if the connection is simply dropped, without an error, this close code\n    /// will be sent to the handler.\n    Abnormal,\n\n    /// Indicates that an endpoint is terminating the connection because it has received data within\n    /// a message that was not consistent with the type of the message (e.g., non-UTF-8 \\[RFC 3629\\]\n    /// data within a text message).\n    Invalid,\n\n    /// Indicates that an endpoint is terminating the connection because it has received a message\n    /// that violates its policy. This is a generic status code that can be returned when there is\n    /// no other more suitable status code (e.g., Unsupported or Size) or if there is a need to hide\n    /// specific details about the policy.\n    Policy,\n\n    /// Indicates that an endpoint is terminating the connection because it has received a message\n    /// that is too big for it to process.\n    Size,\n\n    /// Indicates that an endpoint (client) is terminating the connection because it has expected\n    /// the server to negotiate one or more extension, but the server didn't return them in the\n    /// response message of the WebSocket handshake.  The list of extensions that are needed should\n    /// be given as the reason for closing. Note that this status code is not used by the server,\n    /// because it can fail the WebSocket handshake instead.\n    Extension,\n\n    /// Indicates that a server is terminating the connection because it encountered an unexpected\n    /// condition that prevented it from fulfilling the request.\n    Error,\n\n    /// Indicates that the server is restarting. A client may choose to reconnect, and if it does,\n    /// it should use a randomized delay of 5-30 seconds between attempts.\n    Restart,\n\n    /// Indicates that the server is overloaded and the client should either connect to a different\n    /// IP (when multiple targets exist), or reconnect to the same IP when a user has performed\n    /// an action.\n    Again,\n\n    #[doc(hidden)]\n    Tls,\n\n    #[doc(hidden)]\n    Other(u16),\n}\n\nimpl From<CloseCode> for u16 {\n    fn from(code: CloseCode) -> u16 {\n        use self::CloseCode::*;\n\n        match code {\n            Normal => 1000,\n            Away => 1001,\n            Protocol => 1002,\n            Unsupported => 1003,\n            Abnormal => 1006,\n            Invalid => 1007,\n            Policy => 1008,\n            Size => 1009,\n            Extension => 1010,\n            Error => 1011,\n            Restart => 1012,\n            Again => 1013,\n            Tls => 1015,\n            Other(code) => code,\n        }\n    }\n}\n\nimpl From<u16> for CloseCode {\n    fn from(code: u16) -> CloseCode {\n        use self::CloseCode::*;\n\n        match code {\n            1000 => Normal,\n            1001 => Away,\n            1002 => Protocol,\n            1003 => Unsupported,\n            1006 => Abnormal,\n            1007 => Invalid,\n            1008 => Policy,\n            1009 => Size,\n            1010 => Extension,\n            1011 => Error,\n            1012 => Restart,\n            1013 => Again,\n            1015 => Tls,\n            _ => Other(code),\n        }\n    }\n}\n\n#[derive(Debug, Eq, PartialEq, Clone)]\n/// Reason for closing the connection\npub struct CloseReason {\n    /// Exit code\n    pub code: CloseCode,\n\n    /// Optional description of the exit code\n    pub description: Option<String>,\n}\n\nimpl From<CloseCode> for CloseReason {\n    fn from(code: CloseCode) -> Self {\n        CloseReason {\n            code,\n            description: None,\n        }\n    }\n}\n\nimpl<T: Into<String>> From<(CloseCode, T)> for CloseReason {\n    fn from(info: (CloseCode, T)) -> Self {\n        CloseReason {\n            code: info.0,\n            description: Some(info.1.into()),\n        }\n    }\n}\n\n/// The WebSocket GUID as stated in the spec.\n/// See <https://datatracker.ietf.org/doc/html/rfc6455#section-1.3>.\nstatic WS_GUID: &[u8] = b\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\n\n/// Hashes the `Sec-WebSocket-Key` header according to the WebSocket spec.\n///\n/// Result is a Base64 encoded byte array. `base64(sha1(input))` is always 28 bytes.\npub fn hash_key(key: &[u8]) -> [u8; 28] {\n    let hash = {\n        use sha1::Digest as _;\n\n        let mut hasher = sha1::Sha1::new();\n\n        hasher.update(key);\n        hasher.update(WS_GUID);\n\n        hasher.finalize()\n    };\n\n    let mut hash_b64 = [0; 28];\n    let n = BASE64_STANDARD.encode_slice(hash, &mut hash_b64).unwrap();\n    assert_eq!(n, 28);\n\n    hash_b64\n}\n\n#[cfg(test)]\nmod test {\n    #![allow(unused_imports, unused_variables, dead_code)]\n    use super::*;\n\n    macro_rules! opcode_into {\n        ($from:expr => $opcode:pat) => {\n            match OpCode::from($from) {\n                e @ $opcode => {}\n                e => unreachable!(\"{:?}\", e),\n            }\n        };\n    }\n\n    macro_rules! opcode_from {\n        ($from:expr => $opcode:pat) => {\n            let res: u8 = $from.into();\n            match res {\n                e @ $opcode => {}\n                e => unreachable!(\"{:?}\", e),\n            }\n        };\n    }\n\n    #[test]\n    fn test_to_opcode() {\n        opcode_into!(0 => OpCode::Continue);\n        opcode_into!(1 => OpCode::Text);\n        opcode_into!(2 => OpCode::Binary);\n        opcode_into!(8 => OpCode::Close);\n        opcode_into!(9 => OpCode::Ping);\n        opcode_into!(10 => OpCode::Pong);\n        opcode_into!(99 => OpCode::Bad);\n    }\n\n    #[test]\n    fn test_from_opcode() {\n        opcode_from!(OpCode::Continue => 0);\n        opcode_from!(OpCode::Text => 1);\n        opcode_from!(OpCode::Binary => 2);\n        opcode_from!(OpCode::Close => 8);\n        opcode_from!(OpCode::Ping => 9);\n        opcode_from!(OpCode::Pong => 10);\n    }\n\n    #[test]\n    #[should_panic]\n    fn test_from_opcode_debug() {\n        opcode_from!(OpCode::Bad => 99);\n    }\n\n    #[test]\n    fn test_from_opcode_display() {\n        assert_eq!(format!(\"{}\", OpCode::Continue), \"CONTINUE\");\n        assert_eq!(format!(\"{}\", OpCode::Text), \"TEXT\");\n        assert_eq!(format!(\"{}\", OpCode::Binary), \"BINARY\");\n        assert_eq!(format!(\"{}\", OpCode::Close), \"CLOSE\");\n        assert_eq!(format!(\"{}\", OpCode::Ping), \"PING\");\n        assert_eq!(format!(\"{}\", OpCode::Pong), \"PONG\");\n        assert_eq!(format!(\"{}\", OpCode::Bad), \"BAD\");\n    }\n\n    #[test]\n    fn test_hash_key() {\n        let hash = hash_key(b\"hello actix-web\");\n        assert_eq!(&hash, b\"cR1dlyUUJKp0s/Bel25u5TgvC3E=\");\n    }\n\n    #[test]\n    fn close_code_from_u16() {\n        assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);\n        assert_eq!(CloseCode::from(1001u16), CloseCode::Away);\n        assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);\n        assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);\n        assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);\n        assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);\n        assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);\n        assert_eq!(CloseCode::from(1009u16), CloseCode::Size);\n        assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);\n        assert_eq!(CloseCode::from(1011u16), CloseCode::Error);\n        assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);\n        assert_eq!(CloseCode::from(1013u16), CloseCode::Again);\n        assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);\n        assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));\n    }\n\n    #[test]\n    fn close_code_into_u16() {\n        assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));\n        assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));\n        assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));\n        assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));\n        assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));\n        assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));\n        assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));\n        assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));\n        assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));\n        assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));\n        assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));\n        assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));\n        assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));\n        assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));\n    }\n}\n"
  },
  {
    "path": "actix-http/tests/test.binary",
    "content": "TǑɂV2vI\\R˙e\u0004vD:藽R\u0010V\u0003Yp\u001f;\u0007Gp!2C.\fpA\u000b\u0010!ߦx\tj+UcX\u0013c%\u0017;\"y\u0010AI"
  },
  {
    "path": "actix-http/tests/test_client.rs",
    "content": "use std::convert::Infallible;\n\nuse actix_http::{body::BoxBody, HttpMessage, HttpService, Request, Response, StatusCode};\nuse actix_http_test::test_server;\nuse actix_service::ServiceFactoryExt;\nuse actix_utils::future;\nuse bytes::Bytes;\nuse derive_more::{Display, Error};\nuse futures_util::StreamExt as _;\n\nconst STR: &str = \"Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World\";\n\n#[actix_rt::test]\nasync fn h1_v2() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.get(\"/\").insert_header((\"x-test\", \"111\")).send();\n    let mut response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    let mut response = srv.post(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn connection_close() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n            .map(|_| ())\n    })\n    .await;\n\n    let response = srv.get(\"/\").force_close().send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn with_query_parameter() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .finish(|req: Request| async move {\n                if req.uri().query().unwrap().contains(\"qp=\") {\n                    Ok::<_, Infallible>(Response::ok())\n                } else {\n                    Ok(Response::bad_request())\n                }\n            })\n            .tcp()\n            .map(|_| ())\n    })\n    .await;\n\n    let request = srv.request(http::Method::GET, srv.url(\"/?qp=5\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[derive(Debug, Display, Error)]\n#[display(\"expect failed\")]\nstruct ExpectFailed;\n\nimpl From<ExpectFailed> for Response<BoxBody> {\n    fn from(_: ExpectFailed) -> Self {\n        Response::new(StatusCode::EXPECTATION_FAILED)\n    }\n}\n\n#[actix_rt::test]\nasync fn h1_expect() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .expect(|req: Request| async {\n                if req.headers().contains_key(\"AUTH\") {\n                    Ok(req)\n                } else {\n                    Err(ExpectFailed)\n                }\n            })\n            .h1(|req: Request| async move {\n                let (_, mut body) = req.into_parts();\n                let mut buf = Vec::new();\n                while let Some(Ok(chunk)) = body.next().await {\n                    buf.extend_from_slice(&chunk);\n                }\n                let str = std::str::from_utf8(&buf).unwrap();\n                assert_eq!(str, \"expect body\");\n\n                Ok::<_, Infallible>(Response::ok())\n            })\n            .tcp()\n    })\n    .await;\n\n    // test expect without payload.\n    let request = srv\n        .request(http::Method::GET, srv.url(\"/\"))\n        .insert_header((\"Expect\", \"100-continue\"));\n\n    let response = request.send().await;\n    assert!(response.is_err());\n\n    // test expect would fail to continue\n    let request = srv\n        .request(http::Method::GET, srv.url(\"/\"))\n        .insert_header((\"Expect\", \"100-continue\"));\n\n    let response = request.send_body(\"expect body\").await.unwrap();\n    assert_eq!(response.status(), StatusCode::EXPECTATION_FAILED);\n\n    // test expect would continue\n    let request = srv\n        .request(http::Method::GET, srv.url(\"/\"))\n        .insert_header((\"Expect\", \"100-continue\"))\n        .insert_header((\"AUTH\", \"996\"));\n\n    let response = request.send_body(\"expect body\").await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-http/tests/test_h2_timer.rs",
    "content": "use std::{io, time::Duration};\n\nuse actix_http::{error::Error, HttpService, Response};\nuse actix_server::Server;\nuse tokio::io::AsyncWriteExt;\n\n#[actix_rt::test]\nasync fn h2_ping_pong() -> io::Result<()> {\n    let (tx, rx) = std::sync::mpsc::sync_channel(1);\n\n    let lst = std::net::TcpListener::bind(\"127.0.0.1:0\")?;\n\n    let addr = lst.local_addr().unwrap();\n\n    let join = std::thread::spawn(move || {\n        actix_rt::System::new().block_on(async move {\n            let srv = Server::build()\n                .disable_signals()\n                .workers(1)\n                .listen(\"h2_ping_pong\", lst, || {\n                    HttpService::build()\n                        .keep_alive(Duration::from_secs(3))\n                        .h2(|_| async { Ok::<_, Error>(Response::ok()) })\n                        .tcp()\n                })?\n                .run();\n\n            tx.send(srv.handle()).unwrap();\n\n            srv.await\n        })\n    });\n\n    let handle = rx.recv().unwrap();\n\n    let (sync_tx, rx) = std::sync::mpsc::sync_channel(1);\n\n    // use a separate thread for h2 client so it can be blocked.\n    std::thread::spawn(move || {\n        tokio::runtime::Builder::new_current_thread()\n            .enable_all()\n            .build()\n            .unwrap()\n            .block_on(async move {\n                let stream = tokio::net::TcpStream::connect(addr).await.unwrap();\n\n                let (mut tx, conn) = h2::client::handshake(stream).await.unwrap();\n\n                tokio::spawn(async move { conn.await.unwrap() });\n\n                let (res, _) = tx.send_request(::http::Request::new(()), true).unwrap();\n                let res = res.await.unwrap();\n\n                assert_eq!(res.status().as_u16(), 200);\n\n                sync_tx.send(()).unwrap();\n\n                // intentionally block the client thread so it can not answer ping pong.\n                std::thread::sleep(std::time::Duration::from_secs(1000));\n            })\n    });\n\n    rx.recv().unwrap();\n\n    let now = std::time::Instant::now();\n\n    // stop server gracefully. this step would take up to 30 seconds.\n    handle.stop(true).await;\n\n    // join server thread. only when connection are all gone this step would finish.\n    join.join().unwrap()?;\n\n    // check the time used for join server thread so it's known that the server shutdown\n    // is from keep alive and not server graceful shutdown timeout.\n    assert!(now.elapsed() < std::time::Duration::from_secs(30));\n\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_handshake_timeout() -> io::Result<()> {\n    let (tx, rx) = std::sync::mpsc::sync_channel(1);\n\n    let lst = std::net::TcpListener::bind(\"127.0.0.1:0\")?;\n\n    let addr = lst.local_addr().unwrap();\n\n    let join = std::thread::spawn(move || {\n        actix_rt::System::new().block_on(async move {\n            let srv = Server::build()\n                .disable_signals()\n                .workers(1)\n                .listen(\"h2_ping_pong\", lst, || {\n                    HttpService::build()\n                        .keep_alive(Duration::from_secs(30))\n                        // set first request timeout to 5 seconds.\n                        // this is the timeout used for http2 handshake.\n                        .client_request_timeout(Duration::from_secs(5))\n                        .h2(|_| async { Ok::<_, Error>(Response::ok()) })\n                        .tcp()\n                })?\n                .run();\n\n            tx.send(srv.handle()).unwrap();\n\n            srv.await\n        })\n    });\n\n    let handle = rx.recv().unwrap();\n\n    let (sync_tx, rx) = std::sync::mpsc::sync_channel(1);\n\n    // use a separate thread for tcp client so it can be blocked.\n    std::thread::spawn(move || {\n        tokio::runtime::Builder::new_current_thread()\n            .enable_all()\n            .build()\n            .unwrap()\n            .block_on(async move {\n                let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();\n\n                // do not send the last new line intentionally.\n                // This should hang the server handshake\n                let malicious_buf = b\"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\";\n                stream.write_all(malicious_buf).await.unwrap();\n                stream.flush().await.unwrap();\n\n                sync_tx.send(()).unwrap();\n\n                // intentionally block the client thread so it sit idle and not do handshake.\n                std::thread::sleep(std::time::Duration::from_secs(1000));\n\n                drop(stream)\n            })\n    });\n\n    rx.recv().unwrap();\n\n    let now = std::time::Instant::now();\n\n    // stop server gracefully. this step would take up to 30 seconds.\n    handle.stop(true).await;\n\n    // join server thread. only when connection are all gone this step would finish.\n    join.join().unwrap()?;\n\n    // check the time used for join server thread so it's known that the server shutdown\n    // is from handshake timeout and not server graceful shutdown timeout.\n    assert!(now.elapsed() < std::time::Duration::from_secs(30));\n\n    Ok(())\n}\n"
  },
  {
    "path": "actix-http/tests/test_openssl.rs",
    "content": "#![cfg(feature = \"openssl\")]\n\nextern crate tls_openssl as openssl;\n\nuse std::{convert::Infallible, io, time::Duration};\n\nuse actix_http::{\n    body::{BodyStream, BoxBody, SizedStream},\n    error::PayloadError,\n    header::{self, HeaderValue},\n    Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version,\n};\nuse actix_http_test::test_server;\nuse actix_service::{fn_service, ServiceFactoryExt};\nuse actix_utils::future::{err, ok, ready};\nuse bytes::{Bytes, BytesMut};\nuse derive_more::{Display, Error};\nuse futures_core::Stream;\nuse futures_util::{stream::once, StreamExt as _};\nuse openssl::{\n    pkey::PKey,\n    ssl::{SslAcceptor, SslMethod},\n    x509::X509,\n};\n\nasync fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    let body = stream\n        .map(|res| match res {\n            Ok(chunk) => chunk,\n            Err(_) => panic!(),\n        })\n        .fold(BytesMut::new(), move |mut body, chunk| {\n            body.extend_from_slice(&chunk);\n            ready(body)\n        })\n        .await;\n\n    Ok(body)\n}\n\nfn tls_config() -> SslAcceptor {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_file = cert.pem();\n    let key_file = key_pair.serialize_pem();\n\n    let cert = X509::from_pem(cert_file.as_bytes()).unwrap();\n    let key = PKey::private_key_from_pem(key_file.as_bytes()).unwrap();\n\n    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();\n    builder.set_certificate(&cert).unwrap();\n    builder.set_private_key(&key).unwrap();\n\n    builder.set_alpn_select_callback(|_, protos| {\n        const H2: &[u8] = b\"\\x02h2\";\n        if protos.windows(3).any(|window| window == H2) {\n            Ok(b\"h2\")\n        } else {\n            Err(openssl::ssl::AlpnError::NOACK)\n        }\n    });\n    builder.set_alpn_protos(b\"\\x02h2\").unwrap();\n\n    builder.build()\n}\n\n#[actix_rt::test]\nasync fn h2() -> io::Result<()> {\n    let srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Error>(Response::ok()))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_1() -> io::Result<()> {\n    let srv = test_server(move || {\n        HttpService::build()\n            .finish(|req: Request| {\n                assert!(req.peer_addr().is_some());\n                assert_eq!(req.version(), Version::HTTP_2);\n                ok::<_, Error>(Response::ok())\n            })\n            .openssl_with_config(\n                tls_config(),\n                TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)),\n            )\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_body() -> io::Result<()> {\n    let data = \"HELLOWORLD\".to_owned().repeat(64 * 1024); // 640 KiB\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|mut req: Request<_>| async move {\n                let body = load_body(req.take_payload()).await?;\n                Ok::<_, Error>(Response::ok().set_body(body))\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send_body(data.clone()).await.unwrap();\n    assert!(response.status().is_success());\n\n    let body = srv.load_body(response).await.unwrap();\n    assert_eq!(&body, data.as_bytes());\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_content_length() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .h2(|req: Request| {\n                let idx: usize = req.uri().path()[1..].parse().unwrap();\n                let statuses = [\n                    StatusCode::CONTINUE,\n                    StatusCode::NO_CONTENT,\n                    StatusCode::OK,\n                    StatusCode::NOT_FOUND,\n                ];\n                ok::<_, Infallible>(Response::new(statuses[idx]))\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    static VALUE: HeaderValue = HeaderValue::from_static(\"0\");\n\n    {\n        let req = srv.request(Method::HEAD, srv.surl(\"/0\")).send();\n        actix_rt::time::timeout(Duration::from_secs(15), req)\n            .await\n            .expect(\"request future stalled on recv 1xx frame\")\n            .expect_err(\"should timeout on recv 1xx frame\");\n\n        let req = srv.request(Method::GET, srv.surl(\"/0\")).send();\n        actix_rt::time::timeout(Duration::from_secs(15), req)\n            .await\n            .expect(\"request future stalled on recv 1xx frame\")\n            .expect_err(\"should timeout on recv 1xx frame\");\n\n        let req = srv.request(Method::GET, srv.surl(\"/1\")).send();\n        let response = req.await.unwrap();\n        assert!(response.headers().get(\"content-length\").is_none());\n\n        for &i in &[2, 3] {\n            let req = srv\n                .request(Method::GET, srv.surl(&format!(\"/{}\", i)))\n                .send();\n            let response = req.await.unwrap();\n            assert_eq!(response.headers().get(\"content-length\"), Some(&VALUE));\n        }\n    }\n}\n\n#[actix_rt::test]\nasync fn h2_headers() {\n    let data = STR.repeat(10);\n    let data2 = data.clone();\n\n    let mut srv = test_server(move || {\n        let data = data.clone();\n        HttpService::build()\n            .h2(move |_| {\n                let mut builder = Response::build(StatusCode::OK);\n                for idx in 0..90 {\n                    builder.insert_header(\n                    (format!(\"X-TEST-{}\", idx).as_str(),\n                    \"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \",\n                ));\n                }\n                ok::<_, Infallible>(builder.body(data.clone()))\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from(data2));\n}\n\nconst STR: &str = \"Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World\";\n\n#[actix_rt::test]\nasync fn h2_body2() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn h2_head_empty() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.version(), Version::HTTP_2);\n\n    {\n        let len = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n}\n\n#[actix_rt::test]\nasync fn h2_head_binary() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n}\n\n#[actix_rt::test]\nasync fn h2_head_binary2() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response.headers().get(header::CONTENT_LENGTH).unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n}\n\n#[actix_rt::test]\nasync fn h2_body_length() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| async {\n                let body = once(async { Ok::<_, Infallible>(Bytes::from_static(STR.as_ref())) });\n\n                Ok::<_, Infallible>(\n                    Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),\n                )\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn h2_body_chunked_explicit() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| {\n                let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(\n                    Response::build(StatusCode::OK)\n                        .insert_header((header::TRANSFER_ENCODING, \"chunked\"))\n                        .body(BodyStream::new(body)),\n                )\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n\n    // decode\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn h2_response_http_error_handling() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(fn_service(|_| {\n                let broken_header = Bytes::from_static(b\"\\0\\0\\0\");\n                ok::<_, Infallible>(\n                    Response::build(StatusCode::OK)\n                        .insert_header((header::CONTENT_TYPE, broken_header))\n                        .body(STR),\n                )\n            }))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(\n        bytes,\n        Bytes::from_static(b\"error processing HTTP: failed to parse header value\")\n    );\n}\n\n#[derive(Debug, Display, Error)]\n#[display(\"error\")]\nstruct BadRequest;\n\nimpl From<BadRequest> for Response<BoxBody> {\n    fn from(err: BadRequest) -> Self {\n        Response::build(StatusCode::BAD_REQUEST)\n            .body(err.to_string())\n            .map_into_boxed_body()\n    }\n}\n\n#[actix_rt::test]\nasync fn h2_service_error() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| err::<Response<BoxBody>, _>(BadRequest))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(b\"error\"));\n}\n\n#[actix_rt::test]\nasync fn h2_on_connect() {\n    let srv = test_server(move || {\n        HttpService::build()\n            .on_connect_ext(|_, data| {\n                data.insert(20isize);\n            })\n            .h2(|req: Request| {\n                assert!(req.conn_data::<isize>().is_some());\n                ok::<_, Infallible>(Response::ok())\n            })\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-http/tests/test_rustls.rs",
    "content": "#![cfg(feature = \"rustls-0_23\")]\n\nextern crate tls_openssl as openssl;\nextern crate tls_rustls_023 as rustls;\n\nuse std::{\n    convert::Infallible,\n    io::{self, Write},\n    net::{SocketAddr, TcpStream as StdTcpStream},\n    sync::Arc,\n    task::Poll,\n    time::Duration,\n};\n\nuse actix_http::{\n    body::{BodyStream, BoxBody, SizedStream},\n    error::PayloadError,\n    header::{self, HeaderName, HeaderValue},\n    Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version,\n};\nuse actix_http_test::test_server;\nuse actix_rt::{net::TcpStream as RtTcpStream, pin};\nuse actix_service::{fn_factory_with_config, fn_service};\nuse actix_tls::{accept::rustls_0_23::TlsStream, connect::rustls_0_23::webpki_roots_cert_store};\nuse actix_utils::future::{err, ok, poll_fn};\nuse awc::{Client, Connector};\nuse bytes::{Bytes, BytesMut};\nuse derive_more::{Display, Error};\nuse futures_core::{ready, Stream};\nuse futures_util::stream::once;\nuse rustls::{pki_types::ServerName, ServerConfig as RustlsServerConfig};\nuse rustls_pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};\n\nasync fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    let mut buf = BytesMut::new();\n\n    pin!(stream);\n\n    poll_fn(|cx| loop {\n        let body = stream.as_mut();\n\n        match ready!(body.poll_next(cx)) {\n            Some(Ok(bytes)) => buf.extend_from_slice(&bytes),\n            None => return Poll::Ready(Ok(())),\n            Some(Err(err)) => return Poll::Ready(Err(err)),\n        }\n    })\n    .await?;\n\n    Ok(buf)\n}\n\nfn tls_config_with_alpn(protocols: &[&[u8]]) -> RustlsServerConfig {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_chain = vec![cert.der().clone()];\n    let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));\n\n    let mut config = RustlsServerConfig::builder()\n        .with_no_client_auth()\n        .with_single_cert(cert_chain, key_der)\n        .unwrap();\n\n    config.alpn_protocols = protocols.iter().map(|proto| proto.to_vec()).collect();\n\n    config\n}\n\nfn tls_config() -> RustlsServerConfig {\n    tls_config_with_alpn(&[HTTP1_1_ALPN_PROTOCOL, H2_ALPN_PROTOCOL])\n}\n\nfn tls_config_h1() -> RustlsServerConfig {\n    tls_config_with_alpn(&[HTTP1_1_ALPN_PROTOCOL])\n}\n\nfn tls_config_h2() -> RustlsServerConfig {\n    tls_config_with_alpn(&[H2_ALPN_PROTOCOL])\n}\n\nfn h1_client() -> Client {\n    use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};\n\n    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n    builder.set_verify(SslVerifyMode::NONE);\n    builder.set_alpn_protos(b\"\\x08http/1.1\").unwrap();\n\n    let connector = Connector::new()\n        .conn_lifetime(Duration::from_secs(0))\n        .timeout(Duration::from_millis(30_000))\n        .openssl(builder.build());\n\n    Client::builder().connector(connector).finish()\n}\n\npub fn get_negotiated_alpn_protocol(\n    addr: SocketAddr,\n    client_alpn_protocol: &[u8],\n) -> Option<Vec<u8>> {\n    let mut config = rustls::ClientConfig::builder()\n        .with_root_certificates(webpki_roots_cert_store())\n        .with_no_client_auth();\n\n    config.alpn_protocols.push(client_alpn_protocol.to_vec());\n\n    let mut sess =\n        rustls::ClientConnection::new(Arc::new(config), ServerName::try_from(\"localhost\").unwrap())\n            .unwrap();\n\n    let mut sock = StdTcpStream::connect(addr).unwrap();\n    let mut stream = rustls::Stream::new(&mut sess, &mut sock);\n\n    // The handshake will fails because the client will not be able to verify the server\n    // certificate, but it doesn't matter here as we are just interested in the negotiated ALPN\n    // protocol\n    let _ = stream.flush();\n\n    sess.alpn_protocol().map(|proto| proto.to_vec())\n}\n\n#[actix_rt::test]\nasync fn h1() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h1(|_| ok::<_, Error>(Response::ok()))\n            .rustls_0_23(tls_config_h1())\n    })\n    .await;\n\n    let response = h1_client().get(srv.surl(\"/\")).send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Error>(Response::ok()))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h1_1() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h1(|req: Request| {\n                assert!(req.peer_addr().is_some());\n                assert_eq!(req.version(), Version::HTTP_11);\n                ok::<_, Error>(Response::ok())\n            })\n            .rustls_0_23(tls_config_h1())\n    })\n    .await;\n\n    let response = h1_client().get(srv.surl(\"/\")).send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_1() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .finish(|req: Request| {\n                assert!(req.peer_addr().is_some());\n                assert_eq!(req.version(), Version::HTTP_2);\n                ok::<_, Error>(Response::ok())\n            })\n            .rustls_0_23_with_config(\n                tls_config_h2(),\n                TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)),\n            )\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_tcp_nodelay_override_true() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .tcp_nodelay(true)\n            .on_connect_ext(|io: &TlsStream<RtTcpStream>, data| {\n                data.insert(io.get_ref().0.nodelay().unwrap());\n            })\n            .h2(|req: Request| {\n                assert_eq!(req.conn_data::<bool>(), Some(&true));\n                ok::<_, Error>(Response::ok())\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_tcp_nodelay_override_false() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .tcp_nodelay(false)\n            .on_connect_ext(|io: &TlsStream<RtTcpStream>, data| {\n                data.insert(io.get_ref().0.nodelay().unwrap());\n            })\n            .h2(|req: Request| {\n                assert_eq!(req.conn_data::<bool>(), Some(&false));\n                ok::<_, Error>(Response::ok())\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_body1() -> io::Result<()> {\n    let data = \"HELLOWORLD\".to_owned().repeat(64 * 1024);\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|mut req: Request<_>| async move {\n                let body = load_body(req.take_payload()).await?;\n                Ok::<_, Error>(Response::ok().set_body(body))\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send_body(data.clone()).await.unwrap();\n    assert!(response.status().is_success());\n\n    let body = srv.load_body(response).await.unwrap();\n    assert_eq!(&body, data.as_bytes());\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn h2_content_length() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|req: Request| {\n                let indx: usize = req.uri().path()[1..].parse().unwrap();\n                let statuses = [\n                    StatusCode::CONTINUE,\n                    StatusCode::NO_CONTENT,\n                    StatusCode::OK,\n                    StatusCode::NOT_FOUND,\n                ];\n                ok::<_, Infallible>(Response::new(statuses[indx]))\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let header = HeaderName::from_static(\"content-length\");\n    let value = HeaderValue::from_static(\"0\");\n\n    {\n        #[allow(clippy::single_element_loop)]\n        for &i in &[0] {\n            let req = srv\n                .request(Method::HEAD, srv.surl(&format!(\"/{}\", i)))\n                .send();\n            actix_rt::time::timeout(Duration::from_secs(15), req)\n                .await\n                .expect(\"request future stalled on recv 1xx frame\")\n                .expect_err(\"should timeout on recv 1xx frame\");\n            // assert_eq!(response.headers().get(&header), None);\n\n            let req = srv\n                .request(Method::GET, srv.surl(&format!(\"/{}\", i)))\n                .send();\n            actix_rt::time::timeout(Duration::from_secs(15), req)\n                .await\n                .expect(\"request future stalled on recv 1xx frame\")\n                .expect_err(\"should timeout on recv 1xx frame\");\n            // assert_eq!(response.headers().get(&header), None);\n        }\n\n        #[allow(clippy::single_element_loop)]\n        for &i in &[1] {\n            let req = srv\n                .request(Method::GET, srv.surl(&format!(\"/{}\", i)))\n                .send();\n            let response = req.await.unwrap();\n            assert_eq!(response.headers().get(&header), None);\n        }\n\n        for &i in &[2, 3] {\n            let req = srv\n                .request(Method::GET, srv.surl(&format!(\"/{}\", i)))\n                .send();\n            let response = req.await.unwrap();\n            assert_eq!(response.headers().get(&header), Some(&value));\n        }\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_headers() {\n    let data = STR.repeat(10);\n    let data2 = data.clone();\n\n    let mut srv = test_server(move || {\n        let data = data.clone();\n        HttpService::build()\n            .h2(move |_| {\n                let mut config = Response::build(StatusCode::OK);\n                for idx in 0..90 {\n                    config.insert_header((\n                    format!(\"X-TEST-{}\", idx).as_str(),\n                    \"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \",\n                ));\n                }\n                ok::<_, Infallible>(config.body(data.clone()))\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from(data2));\n    srv.stop().await;\n}\n\nconst STR: &str = \"Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World\";\n\n#[actix_rt::test]\nasync fn h2_body2() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_head_empty() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.version(), Version::HTTP_2);\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_head_binary() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_head_binary2() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.shead(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_body_length() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| {\n                let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(\n                    Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),\n                )\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_body_chunked_explicit() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| {\n                let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(\n                    Response::build(StatusCode::OK)\n                        .insert_header((header::TRANSFER_ENCODING, \"chunked\"))\n                        .body(BodyStream::new(body)),\n                )\n            })\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n\n    // decode\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_response_http_error_handling() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(fn_factory_with_config(|_: ()| {\n                ok::<_, Infallible>(fn_service(|_| {\n                    let broken_header = Bytes::from_static(b\"\\0\\0\\0\");\n                    ok::<_, Infallible>(\n                        Response::build(StatusCode::OK)\n                            .insert_header((http::header::CONTENT_TYPE, broken_header))\n                            .body(STR),\n                    )\n                }))\n            }))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(\n        bytes,\n        Bytes::from_static(b\"error processing HTTP: failed to parse header value\")\n    );\n    srv.stop().await;\n}\n\n#[derive(Debug, Display, Error)]\n#[display(\"error\")]\nstruct BadRequest;\n\nimpl From<BadRequest> for Response<BoxBody> {\n    fn from(_: BadRequest) -> Self {\n        Response::bad_request().set_body(BoxBody::new(\"error\"))\n    }\n}\n\n#[actix_rt::test]\nasync fn h2_service_error() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h2(|_| err::<Response<BoxBody>, _>(BadRequest))\n            .rustls_0_23(tls_config_h2())\n    })\n    .await;\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(b\"error\"));\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_service_error() {\n    let mut srv = test_server(move || {\n        HttpService::build()\n            .h1(|_| err::<Response<BoxBody>, _>(BadRequest))\n            .rustls_0_23(tls_config_h1())\n    })\n    .await;\n\n    let response = h1_client().get(srv.surl(\"/\")).send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(b\"error\"));\n    srv.stop().await;\n}\n\nconst H2_ALPN_PROTOCOL: &[u8] = b\"h2\";\nconst HTTP1_1_ALPN_PROTOCOL: &[u8] = b\"http/1.1\";\nconst CUSTOM_ALPN_PROTOCOL: &[u8] = b\"custom\";\n\n#[actix_rt::test]\nasync fn alpn_h1() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        let mut config = tls_config_h1();\n        config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());\n        HttpService::build()\n            .h1(|_| ok::<_, Error>(Response::ok()))\n            .rustls_0_23(config)\n    })\n    .await;\n\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),\n        Some(CUSTOM_ALPN_PROTOCOL.to_vec())\n    );\n\n    let response = h1_client().get(srv.surl(\"/\")).send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn alpn_h2() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        let mut config = tls_config_h2();\n        config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());\n        HttpService::build()\n            .h2(|_| ok::<_, Error>(Response::ok()))\n            .rustls_0_23(config)\n    })\n    .await;\n\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), H2_ALPN_PROTOCOL),\n        Some(H2_ALPN_PROTOCOL.to_vec())\n    );\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),\n        Some(CUSTOM_ALPN_PROTOCOL.to_vec())\n    );\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n    Ok(())\n}\n\n#[actix_rt::test]\nasync fn alpn_h2_1() -> io::Result<()> {\n    let mut srv = test_server(move || {\n        let mut config = tls_config();\n        config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());\n        HttpService::build()\n            .finish(|_| ok::<_, Error>(Response::ok()))\n            .rustls_0_23(config)\n    })\n    .await;\n\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), H2_ALPN_PROTOCOL),\n        Some(H2_ALPN_PROTOCOL.to_vec())\n    );\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), HTTP1_1_ALPN_PROTOCOL),\n        Some(HTTP1_1_ALPN_PROTOCOL.to_vec())\n    );\n    assert_eq!(\n        get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),\n        Some(CUSTOM_ALPN_PROTOCOL.to_vec())\n    );\n\n    let response = srv.sget(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n    Ok(())\n}\n"
  },
  {
    "path": "actix-http/tests/test_server.rs",
    "content": "use std::{\n    convert::Infallible,\n    io::{Read, Write},\n    net, thread,\n    time::{Duration, Instant},\n};\n\nuse actix_http::{\n    body::{self, BodyStream, BoxBody, SizedStream},\n    header, Error, HttpService, KeepAlive, Request, Response, StatusCode, Version,\n};\nuse actix_http_test::test_server;\nuse actix_rt::{\n    net::TcpStream,\n    time::{sleep, timeout},\n};\nuse actix_service::fn_service;\nuse actix_utils::future::{err, ok, ready};\nuse bytes::Bytes;\nuse derive_more::{Display, Error};\nuse futures_util::{stream::once, FutureExt as _, StreamExt as _};\nuse rand::Rng as _;\nuse regex::Regex;\n\n#[actix_rt::test]\nasync fn h1_basic() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(KeepAlive::Disabled)\n            .client_request_timeout(Duration::from_secs(1))\n            .client_disconnect_timeout(Duration::from_secs(1))\n            .h1(|req: Request| {\n                assert!(req.peer_addr().is_some());\n                ok::<_, Infallible>(Response::ok())\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_2() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(KeepAlive::Disabled)\n            .client_request_timeout(Duration::from_secs(1))\n            .client_disconnect_timeout(Duration::from_secs(1))\n            .finish(|req: Request| {\n                assert!(req.peer_addr().is_some());\n                assert_eq!(req.version(), http::Version::HTTP_11);\n                ok::<_, Infallible>(Response::ok())\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n}\n\n#[derive(Debug, Display, Error)]\n#[display(\"expect failed\")]\nstruct ExpectFailed;\n\nimpl From<ExpectFailed> for Response<BoxBody> {\n    fn from(_: ExpectFailed) -> Self {\n        Response::new(StatusCode::EXPECTATION_FAILED)\n    }\n}\n\n#[actix_rt::test]\nasync fn expect_continue() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .expect(fn_service(|req: Request| {\n                if req.head().uri.query() == Some(\"yes=\") {\n                    ok(req)\n                } else {\n                    err(ExpectFailed)\n                }\n            }))\n            .finish(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test HTTP/1.1\\r\\nexpect: 100-continue\\r\\n\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 417 Expectation Failed\\r\\ncontent-length\"));\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test?yes= HTTP/1.1\\r\\nexpect: 100-continue\\r\\n\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 100 Continue\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\n\"));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn expect_continue_h1() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .expect(fn_service(|req: Request| {\n                sleep(Duration::from_millis(20)).then(move |_| {\n                    if req.head().uri.query() == Some(\"yes=\") {\n                        ok(req)\n                    } else {\n                        err(ExpectFailed)\n                    }\n                })\n            }))\n            .h1(fn_service(|_| ok::<_, Infallible>(Response::ok())))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test HTTP/1.1\\r\\nexpect: 100-continue\\r\\n\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 417 Expectation Failed\\r\\ncontent-length\"));\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test?yes= HTTP/1.1\\r\\nexpect: 100-continue\\r\\n\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 100 Continue\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\n\"));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn chunked_payload() {\n    let chunk_sizes = [32768, 32, 32768];\n    let total_size: usize = chunk_sizes.iter().sum();\n\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(fn_service(|mut request: Request| {\n                request\n                    .take_payload()\n                    .map(|res| match res {\n                        Ok(pl) => pl,\n                        Err(err) => panic!(\"Error reading payload: {err}\"),\n                    })\n                    .fold(0usize, |acc, chunk| ready(acc + chunk.len()))\n                    .map(|req_size| {\n                        Ok::<_, Error>(Response::ok().set_body(format!(\"size={}\", req_size)))\n                    })\n            }))\n            .tcp()\n    })\n    .await;\n\n    let returned_size = {\n        let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n        let _ = stream.write_all(b\"POST /test HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n\");\n\n        for chunk_size in chunk_sizes.iter() {\n            let mut bytes = Vec::new();\n            let random_bytes = rand::rng()\n                .sample_iter(rand::distr::StandardUniform)\n                .take(*chunk_size)\n                .collect::<Vec<_>>();\n\n            bytes.extend(format!(\"{:X}\\r\\n\", chunk_size).as_bytes());\n            bytes.extend(&random_bytes[..]);\n            bytes.extend(b\"\\r\\n\");\n            let _ = stream.write_all(&bytes);\n        }\n\n        let _ = stream.write_all(b\"0\\r\\n\\r\\n\");\n        stream.shutdown(net::Shutdown::Write).unwrap();\n\n        let mut data = String::new();\n        let _ = stream.read_to_string(&mut data);\n\n        let re = Regex::new(r\"size=(\\d+)\").unwrap();\n        let size: usize = match re.captures(&data) {\n            Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(),\n            None => panic!(\"Failed to find size in HTTP Response: {}\", data),\n        };\n\n        size\n    };\n\n    assert_eq!(returned_size, total_size);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn slow_request_408() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .client_request_timeout(Duration::from_millis(200))\n            .keep_alive(Duration::from_secs(2))\n            .finish(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let start = Instant::now();\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test HTTP/1.1\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(\n        data.starts_with(\"HTTP/1.1 408 Request Timeout\"),\n        \"response was not 408: {}\",\n        data\n    );\n\n    let diff = start.elapsed();\n\n    if diff < Duration::from_secs(1) {\n        // test success\n    } else if diff < Duration::from_secs(3) {\n        panic!(\"request seems to have wrongly timed-out according to keep-alive\");\n    } else {\n        panic!(\"request took way too long to time out\");\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http1_malformed_request() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP1.1\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 400 Bad Request\"));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http1_keepalive() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.1\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.1\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http1_keepalive_timeout() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(Duration::from_secs(1))\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n\n    let _ = stream.write_all(b\"GET /test HTTP/1.1\\r\\n\\r\\n\");\n    let mut data = vec![0; 256];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n\n    thread::sleep(Duration::from_millis(1100));\n\n    let mut data = vec![0; 256];\n    let res = stream.read(&mut data).unwrap();\n    assert_eq!(res, 0);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http1_keepalive_close() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.1\\r\\nconnection: close\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n\n    let mut data = vec![0; 1024];\n    let res = stream.read(&mut data).unwrap();\n    assert_eq!(res, 0);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http10_keepalive_default_close() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.0\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.0 200 OK\\r\\n\");\n\n    let mut data = vec![0; 1024];\n    let res = stream.read(&mut data).unwrap();\n    assert_eq!(res, 0);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http10_keepalive() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.0\\r\\nconnection: keep-alive\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.0 200 OK\\r\\n\");\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.0\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.0 200 OK\\r\\n\");\n\n    let mut data = vec![0; 1024];\n    let res = stream.read(&mut data).unwrap();\n    assert_eq!(res, 0);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn http1_keepalive_disabled() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(KeepAlive::Disabled)\n            .h1(|_| ok::<_, Infallible>(Response::ok()))\n            .tcp()\n    })\n    .await;\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.1\\r\\n\\r\\n\");\n    let mut data = vec![0; 1024];\n    let _ = stream.read(&mut data);\n    assert_eq!(&data[..17], b\"HTTP/1.1 200 OK\\r\\n\");\n\n    let mut data = vec![0; 1024];\n    let res = stream.read(&mut data).unwrap();\n    assert_eq!(res, 0);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn content_length() {\n    use actix_http::{\n        header::{HeaderName, HeaderValue},\n        StatusCode,\n    };\n\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|req: Request| {\n                let idx: usize = req.uri().path()[1..].parse().unwrap();\n                let statuses = [\n                    StatusCode::NO_CONTENT,\n                    StatusCode::CONTINUE,\n                    StatusCode::SWITCHING_PROTOCOLS,\n                    StatusCode::PROCESSING,\n                    StatusCode::OK,\n                    StatusCode::NOT_FOUND,\n                ];\n                ok::<_, Infallible>(Response::new(statuses[idx]))\n            })\n            .tcp()\n    })\n    .await;\n\n    let header = HeaderName::from_static(\"content-length\");\n    let value = HeaderValue::from_static(\"0\");\n\n    {\n        for i in 0..4 {\n            let req = srv.request(http::Method::GET, srv.url(&format!(\"/{}\", i)));\n            let response = req.send().await.unwrap();\n            assert_eq!(response.headers().get(&header), None);\n\n            let req = srv.request(http::Method::HEAD, srv.url(&format!(\"/{}\", i)));\n            let response = req.send().await.unwrap();\n            assert_eq!(response.headers().get(&header), None);\n        }\n\n        for i in 4..6 {\n            let req = srv.request(http::Method::GET, srv.url(&format!(\"/{}\", i)));\n            let response = req.send().await.unwrap();\n            assert_eq!(response.headers().get(&header), Some(&value));\n        }\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn content_length_truncated() {\n    use tokio::io::{AsyncReadExt, AsyncWriteExt};\n\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|mut req: Request| async move {\n                let expected_length: usize = req.uri().path()[1..].parse().unwrap();\n                let mut payload = req.take_payload();\n\n                let mut length = 0;\n                let mut seen_error = false;\n                while let Some(chunk) = payload.next().await {\n                    match chunk {\n                        Ok(b) => length += b.len(),\n                        Err(_) => {\n                            seen_error = true;\n                            break;\n                        }\n                    }\n                }\n                if seen_error {\n                    return Result::<_, Infallible>::Ok(Response::bad_request());\n                }\n\n                assert_eq!(length, expected_length, \"length must match when no error\");\n                Result::<_, Infallible>::Ok(Response::ok())\n            })\n            .tcp()\n    })\n    .await;\n\n    let addr = srv.addr();\n    let mut buf = [0; 12];\n\n    let mut conn = TcpStream::connect(&addr).await.unwrap();\n    conn.write_all(b\"POST /10000 HTTP/1.1\\r\\nContent-Length: 10000\\r\\n\\r\\ndata_truncated\")\n        .await\n        .unwrap();\n    conn.shutdown().await.unwrap();\n    conn.read_exact(&mut buf).await.unwrap();\n    assert_eq!(&buf, b\"HTTP/1.1 400\");\n\n    let mut conn = TcpStream::connect(&addr).await.unwrap();\n    conn.write_all(b\"POST /4 HTTP/1.1\\r\\nContent-Length: 4\\r\\n\\r\\ndata\")\n        .await\n        .unwrap();\n    conn.shutdown().await.unwrap();\n    conn.read_exact(&mut buf).await.unwrap();\n    assert_eq!(&buf, b\"HTTP/1.1 200\");\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_headers() {\n    let data = STR.repeat(10);\n    let data2 = data.clone();\n\n    let mut srv = test_server(move || {\n        let data = data.clone();\n        HttpService::build()\n            .h1(move |_| {\n                let mut builder = Response::build(StatusCode::OK);\n                for idx in 0..90 {\n                    builder.insert_header((\n                    format!(\"X-TEST-{}\", idx).as_str(),\n                    \"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \\\n                        TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \",\n                ));\n                }\n                ok::<_, Infallible>(builder.body(data.clone()))\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from(data2));\n\n    srv.stop().await;\n}\n\nconst STR: &str = \"Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World\";\n\n#[actix_rt::test]\nasync fn h1_body() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_head_empty() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.head(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_head_binary() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.head(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert!(bytes.is_empty());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_head_binary2() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.head(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    {\n        let len = response\n            .headers()\n            .get(http::header::CONTENT_LENGTH)\n            .unwrap();\n        assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_body_length() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| {\n                let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(\n                    Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),\n                )\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_body_chunked_explicit() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| {\n                let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(\n                    Response::build(StatusCode::OK)\n                        .insert_header((header::TRANSFER_ENCODING, \"chunked\"))\n                        .body(BodyStream::new(body)),\n                )\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(\n        response\n            .headers()\n            .get(header::TRANSFER_ENCODING)\n            .unwrap()\n            .to_str()\n            .unwrap(),\n        \"chunked\"\n    );\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n\n    // decode\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_body_chunked_implicit() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| {\n                let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));\n                ok::<_, Infallible>(Response::build(StatusCode::OK).body(BodyStream::new(body)))\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(\n        response\n            .headers()\n            .get(header::TRANSFER_ENCODING)\n            .unwrap()\n            .to_str()\n            .unwrap(),\n        \"chunked\"\n    );\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_response_http_error_handling() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(fn_service(|_| {\n                let broken_header = Bytes::from_static(b\"\\0\\0\\0\");\n                ok::<_, Infallible>(\n                    Response::build(StatusCode::OK)\n                        .insert_header((http::header::CONTENT_TYPE, broken_header))\n                        .body(STR),\n                )\n            }))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(\n        bytes,\n        Bytes::from_static(b\"error processing HTTP: failed to parse header value\")\n    );\n\n    srv.stop().await;\n}\n\n#[derive(Debug, Display, Error)]\n#[display(\"error\")]\nstruct BadRequest;\n\nimpl From<BadRequest> for Response<BoxBody> {\n    fn from(_: BadRequest) -> Self {\n        Response::bad_request().set_body(BoxBody::new(\"error\"))\n    }\n}\n\n#[actix_rt::test]\nasync fn h1_service_error() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|_| err::<Response<()>, _>(BadRequest))\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);\n\n    // read response\n    let bytes = srv.load_body(response).await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(b\"error\"));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h1_on_connect() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .on_connect_ext(|_, data| {\n                data.insert(20isize);\n            })\n            .h1(|req: Request| {\n                assert!(req.conn_data::<isize>().is_some());\n                ok::<_, Infallible>(Response::ok())\n            })\n            .tcp()\n    })\n    .await;\n\n    let response = srv.get(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop().await;\n}\n\n/// Tests compliance with 304 Not Modified spec in RFC 7232 §4.1.\n/// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1\n#[actix_rt::test]\nasync fn not_modified_spec_h1() {\n    // TODO: this test needing a few seconds to complete reveals some weirdness with either the\n    // dispatcher or the client, though similar hangs occur on other tests in this file, only\n    // succeeding, it seems, because of the keepalive timer\n\n    static CL: header::HeaderName = header::CONTENT_LENGTH;\n\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .h1(|req: Request| {\n                let res: Response<BoxBody> = match req.path() {\n                    // with no content-length\n                    \"/none\" => Response::with_body(StatusCode::NOT_MODIFIED, body::None::new())\n                        .map_into_boxed_body(),\n\n                    // with no content-length\n                    \"/body\" => {\n                        Response::with_body(StatusCode::NOT_MODIFIED, \"1234\").map_into_boxed_body()\n                    }\n\n                    // with manual content-length header and specific None body\n                    \"/cl-none\" => {\n                        let mut res =\n                            Response::with_body(StatusCode::NOT_MODIFIED, body::None::new());\n                        res.headers_mut()\n                            .insert(CL.clone(), header::HeaderValue::from_static(\"24\"));\n                        res.map_into_boxed_body()\n                    }\n\n                    // with manual content-length header and ignore-able body\n                    \"/cl-body\" => {\n                        let mut res = Response::with_body(StatusCode::NOT_MODIFIED, \"1234\");\n                        res.headers_mut()\n                            .insert(CL.clone(), header::HeaderValue::from_static(\"4\"));\n                        res.map_into_boxed_body()\n                    }\n\n                    _ => panic!(\"unknown route\"),\n                };\n\n                ok::<_, Infallible>(res)\n            })\n            .tcp()\n    })\n    .await;\n\n    let res = srv.get(\"/none\").send().await.unwrap();\n    assert_eq!(res.status(), http::StatusCode::NOT_MODIFIED);\n    assert_eq!(res.headers().get(&CL), None);\n    assert!(srv.load_body(res).await.unwrap().is_empty());\n\n    let res = srv.get(\"/body\").send().await.unwrap();\n    assert_eq!(res.status(), http::StatusCode::NOT_MODIFIED);\n    assert_eq!(res.headers().get(&CL), None);\n    assert!(srv.load_body(res).await.unwrap().is_empty());\n\n    let res = srv.get(\"/cl-none\").send().await.unwrap();\n    assert_eq!(res.status(), http::StatusCode::NOT_MODIFIED);\n    assert_eq!(\n        res.headers().get(&CL),\n        Some(&header::HeaderValue::from_static(\"24\")),\n    );\n    assert!(srv.load_body(res).await.unwrap().is_empty());\n\n    let res = srv.get(\"/cl-body\").send().await.unwrap();\n    assert_eq!(res.status(), http::StatusCode::NOT_MODIFIED);\n    assert_eq!(\n        res.headers().get(&CL),\n        Some(&header::HeaderValue::from_static(\"4\")),\n    );\n    // server does not prevent payload from being sent but clients may choose not to read it\n    // TODO: this is probably a bug in the client, especially since CL header can differ in length\n    // from the body\n    assert!(!srv.load_body(res).await.unwrap().is_empty());\n\n    // TODO: add stream response tests\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2c_auto() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(KeepAlive::Disabled)\n            .finish(|req: Request| {\n                let body = match req.version() {\n                    Version::HTTP_11 => \"h1\",\n                    Version::HTTP_2 => \"h2\",\n                    _ => unreachable!(),\n                };\n                ok::<_, Infallible>(Response::ok().set_body(body))\n            })\n            .tcp_auto_h2c()\n    })\n    .await;\n\n    let req = srv.get(\"/\");\n    assert_eq!(req.get_version(), &Version::HTTP_11);\n    let mut res = req.send().await.unwrap();\n    assert!(res.status().is_success());\n    assert_eq!(res.body().await.unwrap(), &b\"h1\"[..]);\n\n    // awc doesn't support forcing the version to http/2 so use h2 manually\n\n    let tcp = TcpStream::connect(srv.addr()).await.unwrap();\n    let (h2, connection) = h2::client::handshake(tcp).await.unwrap();\n    tokio::spawn(async move { connection.await.unwrap() });\n    let mut h2 = h2.ready().await.unwrap();\n\n    let request = ::http::Request::new(());\n    let (response, _) = h2.send_request(request, true).unwrap();\n    let (head, mut body) = response.await.unwrap().into_parts();\n    let body = body.data().await.unwrap().unwrap();\n\n    assert!(head.status.is_success());\n    assert_eq!(body, &b\"h2\"[..]);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn h2_flow_control_window_sizes() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .keep_alive(KeepAlive::Disabled)\n            .finish(|mut req: Request| async move {\n                while let Some(item) = req.take_payload().next().await {\n                    item?;\n                }\n\n                Ok::<_, Error>(Response::ok())\n            })\n            .tcp_auto_h2c()\n    })\n    .await;\n\n    let tcp = TcpStream::connect(srv.addr()).await.unwrap();\n\n    let mut builder = h2::client::Builder::new();\n    builder.max_send_buffer_size(4 * 1024 * 1024);\n\n    let (h2, connection) = builder.handshake(tcp).await.unwrap();\n    tokio::spawn(async move { connection.await.unwrap() });\n    let mut h2 = h2.ready().await.unwrap();\n\n    let request = ::http::Request::builder()\n        .method(\"POST\")\n        .uri(\"/\")\n        .body(())\n        .unwrap();\n\n    let (response, mut send) = h2.send_request(request, false).unwrap();\n\n    // request more than the default 64KiB. if server is advertising larger flow control windows,\n    // we should get at least 1MiB assigned.\n    send.reserve_capacity(2 * 1024 * 1024);\n\n    let cap = timeout(Duration::from_secs(2), async {\n        loop {\n            let cap = std::future::poll_fn(|cx| send.poll_capacity(cx))\n                .await\n                .expect(\"request stream closed before flow control capacity became available\")\n                .expect(\"failed polling flow control capacity\");\n\n            if cap >= 1024 * 1024 {\n                break cap;\n            }\n        }\n    })\n    .await\n    .expect(\"timed out waiting for flow control capacity\");\n\n    assert!(\n        cap >= 1024 * 1024,\n        \"expected >= 1MiB send capacity, got {cap}\"\n    );\n\n    send.send_data(Bytes::new(), true).unwrap();\n\n    let res = response.await.unwrap();\n    assert!(res.status().is_success());\n\n    srv.stop().await;\n}\n"
  },
  {
    "path": "actix-http/tests/test_ws.rs",
    "content": "use std::{\n    cell::Cell,\n    convert::Infallible,\n    task::{Context, Poll},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse actix_http::{\n    body::{BodySize, BoxBody},\n    h1,\n    ws::{self, CloseCode, Frame, Item, Message},\n    Error, HttpService, Request, Response,\n};\nuse actix_http_test::test_server;\nuse actix_service::{fn_factory, Service};\nuse bytes::Bytes;\nuse derive_more::{Display, Error, From};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::{SinkExt as _, StreamExt as _};\n\n#[derive(Clone)]\nstruct WsService(Cell<bool>);\n\nimpl WsService {\n    fn new() -> Self {\n        WsService(Cell::new(false))\n    }\n\n    fn set_polled(&self) {\n        self.0.set(true);\n    }\n\n    fn was_polled(&self) -> bool {\n        self.0.get()\n    }\n}\n\n#[derive(Debug, Display, Error, From)]\nenum WsServiceError {\n    #[display(\"HTTP error\")]\n    Http(actix_http::Error),\n\n    #[display(\"WS handshake error\")]\n    Ws(actix_http::ws::HandshakeError),\n\n    #[display(\"I/O error\")]\n    Io(std::io::Error),\n\n    #[display(\"dispatcher error\")]\n    Dispatcher,\n}\n\nimpl From<WsServiceError> for Response<BoxBody> {\n    fn from(err: WsServiceError) -> Self {\n        match err {\n            WsServiceError::Http(err) => err.into(),\n            WsServiceError::Ws(err) => err.into(),\n            WsServiceError::Io(_err) => unreachable!(),\n            WsServiceError::Dispatcher => {\n                Response::internal_server_error().set_body(BoxBody::new(format!(\"{}\", err)))\n            }\n        }\n    }\n}\n\nimpl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService\nwhere\n    T: AsyncRead + AsyncWrite + Unpin + 'static,\n{\n    type Response = ();\n    type Error = WsServiceError;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.set_polled();\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&self, (req, mut framed): (Request, Framed<T, h1::Codec>)) -> Self::Future {\n        assert!(self.was_polled());\n\n        Box::pin(async move {\n            let res = ws::handshake(req.head())?.message_body(())?;\n\n            framed.send((res, BodySize::None).into()).await?;\n\n            let framed = framed.replace_codec(ws::Codec::new());\n\n            ws::Dispatcher::with(framed, service)\n                .await\n                .map_err(|_| WsServiceError::Dispatcher)?;\n\n            Ok(())\n        })\n    }\n}\n\nasync fn service(msg: Frame) -> Result<Message, Error> {\n    let msg = match msg {\n        Frame::Ping(msg) => Message::Pong(msg),\n        Frame::Text(text) => Message::Text(String::from_utf8_lossy(&text).into_owned().into()),\n        Frame::Binary(bin) => Message::Binary(bin),\n        Frame::Continuation(item) => Message::Continuation(item),\n        Frame::Close(reason) => Message::Close(reason),\n        _ => return Err(ws::ProtocolError::BadOpCode.into()),\n    };\n\n    Ok(msg)\n}\n\n#[actix_rt::test]\nasync fn simple() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .upgrade(fn_factory(|| async {\n                Ok::<_, Infallible>(WsService::new())\n            }))\n            .finish(|_| async { Ok::<_, Infallible>(Response::not_found()) })\n            .tcp()\n    })\n    .await;\n\n    // client service\n    let mut framed = srv.ws().await.unwrap();\n    framed.send(Message::Text(\"text\".into())).await.unwrap();\n\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, Frame::Text(Bytes::from_static(b\"text\")));\n\n    framed.send(Message::Binary(\"text\".into())).await.unwrap();\n\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, Frame::Binary(Bytes::from_static(&b\"text\"[..])));\n\n    framed.send(Message::Ping(\"text\".into())).await.unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, Frame::Pong(\"text\".to_string().into()));\n\n    framed\n        .send(Message::Continuation(Item::FirstText(\"text\".into())))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(\n        item,\n        Frame::Continuation(Item::FirstText(Bytes::from_static(b\"text\")))\n    );\n\n    assert!(framed\n        .send(Message::Continuation(Item::FirstText(\"text\".into())))\n        .await\n        .is_err());\n    assert!(framed\n        .send(Message::Continuation(Item::FirstBinary(\"text\".into())))\n        .await\n        .is_err());\n\n    framed\n        .send(Message::Continuation(Item::Continue(\"text\".into())))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(\n        item,\n        Frame::Continuation(Item::Continue(Bytes::from_static(b\"text\")))\n    );\n\n    framed\n        .send(Message::Continuation(Item::Last(\"text\".into())))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(\n        item,\n        Frame::Continuation(Item::Last(Bytes::from_static(b\"text\")))\n    );\n\n    assert!(framed\n        .send(Message::Continuation(Item::Continue(\"text\".into())))\n        .await\n        .is_err());\n\n    assert!(framed\n        .send(Message::Continuation(Item::Last(\"text\".into())))\n        .await\n        .is_err());\n\n    framed\n        .send(Message::Close(Some(CloseCode::Normal.into())))\n        .await\n        .unwrap();\n\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, Frame::Close(Some(CloseCode::Normal.into())));\n}\n"
  },
  {
    "path": "actix-http-test/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n## 3.2.0\n\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 3.1.0\n\n- Minimum supported Rust version (MSRV) is now 1.59.\n\n## 3.0.0\n\n- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]\n- Added `TestServer::client_headers` method. [#2097]\n- Update `actix-server` dependency to `2`.\n- Update `actix-tls` dependency to `3`.\n- Update `bytes` to `1.0`. [#1813]\n- Minimum supported Rust version (MSRV) is now 1.57.\n\n[#2442]: https://github.com/actix/actix-web/pull/2442\n[#2097]: https://github.com/actix/actix-web/pull/2097\n[#1813]: https://github.com/actix/actix-web/pull/1813\n\n<details>\n<summary>3.0.0 Pre-Releases</summary>\n\n## 3.0.0-beta.13\n\n- No significant changes since `3.0.0-beta.12`.\n\n## 3.0.0-beta.12\n\n- No significant changes since `3.0.0-beta.11`.\n\n## 3.0.0-beta.11\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 3.0.0-beta.10\n\n- Update `actix-server` to `2.0.0-rc.2`. [#2550]\n\n[#2550]: https://github.com/actix/actix-web/pull/2550\n\n## 3.0.0-beta.9\n\n- No significant changes since `3.0.0-beta.8`.\n\n## 3.0.0-beta.8\n\n- Update `actix-tls` to `3.0.0-rc.1`. [#2474]\n\n[#2474]: https://github.com/actix/actix-web/pull/2474\n\n## 3.0.0-beta.7\n\n- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]\n\n[#2408]: https://github.com/actix/actix-web/pull/2408\n\n## 3.0.0-beta.6\n\n- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]\n- Update `actix-server` to `2.0.0-beta.9`. [#2442]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n[#2442]: https://github.com/actix/actix-web/pull/2442\n\n## 3.0.0-beta.5\n\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n## 3.0.0-beta.4\n\n- Added `TestServer::client_headers` method. [#2097]\n\n[#2097]: https://github.com/actix/actix-web/pull/2097\n\n## 3.0.0-beta.3\n\n- No notable changes.\n\n## 3.0.0-beta.2\n\n- No notable changes.\n\n## 3.0.0-beta.1\n\n- Update `bytes` to `1.0`. [#1813]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n\n</details>\n\n## 2.1.0\n\n- Add ability to set address for `TestServer`. [#1645]\n- Upgrade `base64` to `0.13`.\n- Upgrade `serde_urlencoded` to `0.7`. [#1773]\n\n[#1773]: https://github.com/actix/actix-web/pull/1773\n[#1645]: https://github.com/actix/actix-web/pull/1645\n\n## 2.0.0\n\n- Update actix-codec and actix-utils dependencies.\n\n## 2.0.0-alpha.1\n\n- Update the `time` dependency to 0.2.7\n- Update `actix-connect` dependency to 2.0.0-alpha.2\n- Make `test_server` `async` fn.\n- Bump minimum supported Rust version to 1.40\n- Replace deprecated `net2` crate with `socket2`\n- Update `base64` dependency to 0.12\n- Update `env_logger` dependency to 0.7\n\n## 1.0.0\n\n- Replaced `TestServer::start()` with `test_server()`\n\n## 1.0.0-alpha.3\n\n- Migrate to `std::future`\n\n## 0.2.5\n\n- Update serde_urlencoded to \"0.6.1\"\n- Increase TestServerRuntime timeouts from 500ms to 3000ms\n- Do not override current `System`\n\n## 0.2.4\n\n- Update actix-server to 0.6\n\n## 0.2.3\n\n- Add `delete`, `options`, `patch` methods to `TestServerRunner`\n\n## 0.2.2\n\n- Add .put() and .sput() methods\n\n## 0.2.1\n\n- Add license files\n\n## 0.2.0\n\n- Update awc and actix-http deps\n\n## 0.1.1\n\n- Always make new connection for http client\n\n## 0.1.0\n\n- No changes\n\n## 0.1.0-alpha.3\n\n- Request functions accept path #743\n\n## 0.1.0-alpha.2\n\n- Added TestServerRuntime::load_body() method\n- Update actix-http and awc libraries\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "actix-http-test/Cargo.toml",
    "content": "[package]\nname = \"actix-http-test\"\nversion = \"3.2.0\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\"]\ndescription = \"Various helpers for Actix applications to use during testing\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\", \"futures\"]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\ncategories = [\n  \"network-programming\",\n  \"asynchronous\",\n  \"web-programming::http-server\",\n  \"web-programming::websocket\",\n]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\n\n[package.metadata.docs.rs]\nfeatures = []\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix_codec::*\",\n  \"actix_http::*\",\n  \"actix_server::*\",\n  \"awc::*\",\n  \"bytes::*\",\n  \"futures_core::*\",\n  \"http::*\",\n  \"tokio::*\",\n]\n\n[features]\ndefault = []\n\n# openssl\nopenssl = [\"tls-openssl\", \"awc/openssl\"]\n\n[dependencies]\nactix-codec = \"0.5\"\nactix-rt = \"2.2\"\nactix-server = \"2\"\nactix-service = \"2\"\nactix-tls = \"3\"\nactix-utils = \"3\"\nawc = { version = \"3\", default-features = false }\n\nbytes = \"1\"\nfutures-core = { version = \"0.3.17\", default-features = false }\nhttp = \"0.2.7\"\nlog = \"0.4\"\nserde = \"1\"\nserde_json = \"1\"\nserde_urlencoded = \"0.7\"\nslab = \"0.4\"\nsocket2 = \"0.6\"\ntls-openssl = { version = \"0.10.55\", package = \"openssl\", optional = true }\ntokio = { version = \"1.38.2\", features = [\"sync\"] }\n\n[dev-dependencies]\nactix-http = \"3\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-http-test/README.md",
    "content": "# `actix-http-test`\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test)\n[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.2.0)](https://docs.rs/actix-http-test/3.2.0)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)\n<br>\n[![Dependency Status](https://deps.rs/crate/actix-http-test/3.2.0/status.svg)](https://deps.rs/crate/actix-http-test/3.2.0)\n[![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n<!-- cargo-rdme start -->\n\nVarious helpers for Actix applications to use during testing.\n\n<!-- cargo-rdme end -->\n"
  },
  {
    "path": "actix-http-test/src/lib.rs",
    "content": "//! Various helpers for Actix applications to use during testing.\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\n#[cfg(feature = \"openssl\")]\nextern crate tls_openssl as openssl;\n\nuse std::{net, thread, time::Duration};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\nuse actix_rt::{net::TcpStream, System};\nuse actix_server::{Server, ServerServiceFactory};\nuse awc::{\n    error::PayloadError, http::header::HeaderMap, ws, Client, ClientRequest, ClientResponse,\n    Connector,\n};\nuse bytes::Bytes;\nuse futures_core::stream::Stream;\nuse http::Method;\nuse socket2::{Domain, Protocol, Socket, Type};\nuse tokio::sync::mpsc;\n\n/// Start test server.\n///\n/// `TestServer` is very simple test server that simplify process of writing integration tests cases\n/// for HTTP applications.\n///\n/// # Examples\n///\n/// ```\n/// use actix_http::{HttpService, Response, Error, StatusCode};\n/// use actix_http_test::test_server;\n/// use actix_service::{fn_service, map_config, ServiceFactoryExt as _};\n///\n/// #[actix_rt::test]\n/// # async fn hidden_test() {}\n/// async fn test_example() {\n///     let srv = test_server(|| {\n///         HttpService::build()\n///             .h1(fn_service(|req| async move {\n///                 Ok::<_, Error>(Response::ok())\n///             }))\n///             .tcp()\n///             .map_err(|_| ())\n///     })\n///     .await;\n///\n///     let req = srv.get(\"/\");\n///     let response = req.send().await.unwrap();\n///\n///     assert_eq!(response.status(), StatusCode::OK);\n/// }\n/// # actix_rt::System::new().block_on(test_example());\n/// ```\npub async fn test_server<F: ServerServiceFactory<TcpStream>>(factory: F) -> TestServer {\n    let tcp = net::TcpListener::bind(\"127.0.0.1:0\").unwrap();\n    test_server_with_addr(tcp, factory).await\n}\n\n/// Start [`test server`](test_server()) on an existing address binding.\npub async fn test_server_with_addr<F: ServerServiceFactory<TcpStream>>(\n    tcp: net::TcpListener,\n    factory: F,\n) -> TestServer {\n    let (started_tx, started_rx) = std::sync::mpsc::channel();\n    let (thread_stop_tx, thread_stop_rx) = mpsc::channel(1);\n\n    // run server in separate thread\n    thread::spawn(move || {\n        System::new().block_on(async move {\n            let local_addr = tcp.local_addr().unwrap();\n\n            let srv = Server::build()\n                .workers(1)\n                .disable_signals()\n                .system_exit()\n                .listen(\"test\", tcp, factory)\n                .expect(\"test server could not be created\");\n\n            let srv = srv.run();\n            started_tx\n                .send((System::current(), srv.handle(), local_addr))\n                .unwrap();\n\n            // drive server loop\n            srv.await.unwrap();\n        });\n\n        // notify TestServer that server and system have shut down\n        // all thread managed resources should be dropped at this point\n        #[allow(clippy::let_underscore_future)]\n        let _ = thread_stop_tx.send(());\n    });\n\n    let (system, server, addr) = started_rx.recv().unwrap();\n\n    let client = {\n        #[cfg(feature = \"openssl\")]\n        let connector = {\n            use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};\n\n            let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n\n            builder.set_verify(SslVerifyMode::NONE);\n            let _ = builder\n                .set_alpn_protos(b\"\\x02h2\\x08http/1.1\")\n                .map_err(|err| log::error!(\"Can not set ALPN protocol: {err}\"));\n\n            Connector::new()\n                .conn_lifetime(Duration::from_secs(0))\n                .timeout(Duration::from_millis(30000))\n                .openssl(builder.build())\n        };\n\n        #[cfg(not(feature = \"openssl\"))]\n        let connector = {\n            Connector::new()\n                .conn_lifetime(Duration::from_secs(0))\n                .timeout(Duration::from_millis(30000))\n        };\n\n        Client::builder().connector(connector).finish()\n    };\n\n    TestServer {\n        server,\n        client,\n        system,\n        addr,\n        thread_stop_rx,\n    }\n}\n\n/// Test server controller\npub struct TestServer {\n    server: actix_server::ServerHandle,\n    client: awc::Client,\n    system: actix_rt::System,\n    addr: net::SocketAddr,\n    thread_stop_rx: mpsc::Receiver<()>,\n}\n\nimpl TestServer {\n    /// Construct test server url\n    pub fn addr(&self) -> net::SocketAddr {\n        self.addr\n    }\n\n    /// Construct test server url\n    pub fn url(&self, uri: &str) -> String {\n        if uri.starts_with('/') {\n            format!(\"http://localhost:{}{}\", self.addr.port(), uri)\n        } else {\n            format!(\"http://localhost:{}/{}\", self.addr.port(), uri)\n        }\n    }\n\n    /// Construct test HTTPS server URL.\n    pub fn surl(&self, uri: &str) -> String {\n        if uri.starts_with('/') {\n            format!(\"https://localhost:{}{}\", self.addr.port(), uri)\n        } else {\n            format!(\"https://localhost:{}/{}\", self.addr.port(), uri)\n        }\n    }\n\n    /// Create `GET` request\n    pub fn get<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.get(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `GET` request\n    pub fn sget<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.get(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `POST` request\n    pub fn post<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.post(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `POST` request\n    pub fn spost<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.post(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `HEAD` request\n    pub fn head<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.head(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `HEAD` request\n    pub fn shead<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.head(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `PUT` request\n    pub fn put<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.put(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `PUT` request\n    pub fn sput<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.put(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `PATCH` request\n    pub fn patch<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.patch(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `PATCH` request\n    pub fn spatch<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.patch(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `DELETE` request\n    pub fn delete<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.delete(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `DELETE` request\n    pub fn sdelete<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.delete(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Create `OPTIONS` request\n    pub fn options<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.options(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create HTTPS `OPTIONS` request\n    pub fn soptions<S: AsRef<str>>(&self, path: S) -> ClientRequest {\n        self.client.options(self.surl(path.as_ref()).as_str())\n    }\n\n    /// Connect to test HTTP server\n    pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {\n        self.client.request(method, path.as_ref())\n    }\n\n    pub async fn load_body<S>(\n        &mut self,\n        mut response: ClientResponse<S>,\n    ) -> Result<Bytes, PayloadError>\n    where\n        S: Stream<Item = Result<Bytes, PayloadError>> + Unpin + 'static,\n    {\n        response.body().limit(10_485_760).await\n    }\n\n    /// Connect to WebSocket server at a given path.\n    pub async fn ws_at(\n        &mut self,\n        path: &str,\n    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {\n        let url = self.url(path);\n        let connect = self.client.ws(url).connect();\n        connect.await.map(|(_, framed)| framed)\n    }\n\n    /// Connect to a WebSocket server.\n    pub async fn ws(\n        &mut self,\n    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {\n        self.ws_at(\"/\").await\n    }\n\n    /// Get default HeaderMap of Client.\n    ///\n    /// Returns Some(&mut HeaderMap) when Client object is unique\n    /// (No other clone of client exists at the same time).\n    pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {\n        self.client.headers()\n    }\n\n    /// Stop HTTP server.\n    ///\n    /// Waits for spawned `Server` and `System` to (force) shutdown.\n    pub async fn stop(&mut self) {\n        // signal server to stop\n        self.server.stop(false).await;\n\n        // also signal system to stop\n        // though this is handled by `ServerBuilder::exit_system` too\n        self.system.stop();\n\n        // wait for thread to be stopped but don't care about result\n        let _ = self.thread_stop_rx.recv().await;\n    }\n}\n\nimpl Drop for TestServer {\n    fn drop(&mut self) {\n        // calls in this Drop impl should be enough to shut down the server, system, and thread\n        // without needing to await anything\n\n        // signal server to stop\n        #[allow(clippy::let_underscore_future)]\n        let _ = self.server.stop(true);\n\n        // signal system to stop\n        self.system.stop();\n    }\n}\n\n/// Get a localhost socket address with random, unused port.\npub fn unused_addr() -> net::SocketAddr {\n    let addr: net::SocketAddr = \"127.0.0.1:0\".parse().unwrap();\n    let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();\n    socket.bind(&addr.into()).unwrap();\n    socket.set_reuse_address(true).unwrap();\n    let tcp = net::TcpListener::from(socket);\n    tcp.local_addr().unwrap()\n}\n"
  },
  {
    "path": "actix-multipart/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Add `MultipartForm` support for `Option<Vec<T>>` fields. [#3577]\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n[#3577]: https://github.com/actix/actix-web/pull/3577\n\n## 0.7.2\n\n- Fix re-exported version of `actix-multipart-derive`.\n\n## 0.7.1\n\n- Expose `LimitExceeded` error type.\n\n## 0.7.0\n\n- Add `MultipartError::ContentTypeIncompatible` variant.\n- Add `MultipartError::ContentDispositionNameMissing` variant.\n- Add `Field::bytes()` method.\n- Rename `MultipartError::{NoContentDisposition => ContentDispositionMissing}` variant.\n- Rename `MultipartError::{NoContentType => ContentTypeMissing}` variant.\n- Rename `MultipartError::{ParseContentType => ContentTypeParse}` variant.\n- Rename `MultipartError::{Boundary => BoundaryMissing}` variant.\n- Rename `MultipartError::{UnsupportedField => UnknownField}` variant.\n- Remove top-level re-exports of `test` utilities.\n\n## 0.6.2\n\n- Add testing utilities under new module `test`.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 0.6.1\n\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 0.6.0\n\n- Added `MultipartForm` typed data extractor. [#2883]\n\n[#2883]: https://github.com/actix/actix-web/pull/2883\n\n## 0.5.0\n\n- `Field::content_type()` now returns `Option<&mime::Mime>`. [#2885]\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n[#2885]: https://github.com/actix/actix-web/pull/2885\n\n## 0.4.0\n\n- No significant changes since `0.4.0-beta.13`.\n\n## 0.4.0-beta.13\n\n- No significant changes since `0.4.0-beta.12`.\n\n## 0.4.0-beta.12\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 0.4.0-beta.11\n\n- No significant changes since `0.4.0-beta.10`.\n\n## 0.4.0-beta.10\n\n- No significant changes since `0.4.0-beta.9`.\n\n## 0.4.0-beta.9\n\n- Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463]\n\n[#2463]: https://github.com/actix/actix-web/pull/2463\n\n## 0.4.0-beta.8\n\n- Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451]\n- Added `MultipartError::NoContentDisposition` variant. [#2451]\n- Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451]\n- Added `Field::name` method for getting the field name. [#2451]\n- `MultipartError` now marks variants with inner errors as the source. [#2451]\n- `MultipartError` is now marked as non-exhaustive. [#2451]\n\n[#2451]: https://github.com/actix/actix-web/pull/2451\n\n## 0.4.0-beta.7\n\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n## 0.4.0-beta.6\n\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n## 0.4.0-beta.5\n\n- No notable changes.\n\n## 0.4.0-beta.4\n\n- No notable changes.\n\n## 0.4.0-beta.3\n\n- No notable changes.\n\n## 0.4.0-beta.2\n\n- No notable changes.\n\n## 0.4.0-beta.1\n\n- Fix multipart consuming payload before header checks. [#1513]\n- Update `bytes` to `1.0`. [#1813]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1513]: https://github.com/actix/actix-web/pull/1513\n\n## 0.3.0\n\n- No significant changes from `0.3.0-beta.2`.\n\n## 0.3.0-beta.2\n\n- Update `actix-*` dependencies to latest versions.\n\n## 0.3.0-beta.1\n\n- Update `actix-web` to 3.0.0-beta.1\n\n## 0.3.0-alpha.1\n\n- Update `actix-web` to 3.0.0-alpha.3\n- Bump minimum supported Rust version to 1.40\n- Minimize `futures` dependencies\n- Remove the unused `time` dependency\n- Fix missing `std::error::Error` implement for `MultipartError`.\n\n## 0.2.0\n\n- Release\n\n## 0.2.0-alpha.4\n\n- Multipart handling now handles Pending during read of boundary #1205\n\n## 0.2.0-alpha.2\n\n- Migrate to `std::future`\n\n## 0.1.4\n\n- Multipart handling now parses requests which do not end in CRLF #1038\n\n## 0.1.3\n\n- Fix ring dependency from actix-web default features for #741.\n\n## 0.1.2\n\n- Fix boundary parsing #876\n\n## 0.1.1\n\n- Fix disconnect handling #834\n\n## 0.1.0\n\n- Release\n\n## 0.1.0-beta.4\n\n- Handle cancellation of uploads #736\n\n- Upgrade to actix-web 1.0.0-beta.4\n\n## 0.1.0-beta.1\n\n- Do not support nested multipart\n\n- Split multipart support to separate crate\n\n- Optimize multipart handling #634, #769\n"
  },
  {
    "path": "actix-multipart/Cargo.toml",
    "content": "[package]\nname = \"actix-multipart\"\nversion = \"0.7.2\"\nauthors = [\n  \"Nikolay Kim <fafhrd91@gmail.com>\",\n  \"Jacob Halsey <jacob@jhalsey.com>\",\n  \"Rob Ede <robjtede@icloud.com>\",\n]\ndescription = \"Multipart request & form support for Actix Web\"\nkeywords = [\"http\", \"actix\", \"web\", \"multipart\", \"form\"]\nhomepage.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix_http::*\",\n  \"actix_multipart_derive::*\",\n  \"actix_utils::*\",\n  \"actix_web::*\",\n  \"bytes::*\",\n  \"futures_core::*\",\n  \"mime::*\",\n  \"serde_json::*\",\n  \"serde_plain::*\",\n  \"serde::*\",\n  \"tempfile::*\",\n]\n\n[features]\ndefault = [\"tempfile\", \"derive\"]\nderive = [\"actix-multipart-derive\"]\ntempfile = [\"dep:tempfile\", \"tokio/fs\"]\n\n[dependencies]\nactix-multipart-derive = { version = \"=0.7.0\", optional = true }\nactix-utils = \"3\"\nactix-web = { version = \"4\", default-features = false }\n\nderive_more = { version = \"2\", features = [\"display\", \"error\", \"from\"] }\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nhttparse = \"1.3\"\nlocal-waker = \"0.1\"\nlog = \"0.4\"\nmemchr = \"2.5\"\nmime = \"0.3\"\nrand = \"0.9\"\nserde = \"1\"\nserde_json = \"1\"\nserde_plain = \"1\"\ntempfile = { version = \"3.4\", optional = true }\ntokio = { version = \"1.38.2\", features = [\"sync\", \"io-util\"] }\n\n[dev-dependencies]\nactix-http = \"3\"\nactix-multipart-rfc7578 = \"0.11\"\nactix-rt = \"2.2\"\nactix-test = \"0.1\"\nactix-web = \"4\"\nassert_matches = \"1\"\nawc = \"3\"\nenv_logger = \"0.11\"\nfutures-test = \"0.3\"\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nmulter = \"3\"\ntokio = { version = \"1.38.2\", features = [\"sync\"] }\ntokio-stream = \"0.1\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-multipart/README.md",
    "content": "# `actix-multipart`\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart)\n[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.7.2)](https://docs.rs/actix-multipart/0.7.2)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-multipart/0.7.2/status.svg)](https://deps.rs/crate/actix-multipart/0.7.2)\n[![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n<!-- cargo-rdme start -->\n\nMultipart request & form support for Actix Web.\n\nThe [`Multipart`] extractor aims to support all kinds of `multipart/*` requests, including `multipart/form-data`, `multipart/related` and `multipart/mixed`. This is a lower-level extractor which supports reading [multipart fields](Field), in the order they are sent by the client.\n\nDue to additional requirements for `multipart/form-data` requests, the higher level [`MultipartForm`] extractor and derive macro only supports this media type.\n\n## Examples\n\n```rust\nuse actix_multipart::form::{\n    json::Json as MpJson, tempfile::TempFile, MultipartForm, MultipartFormConfig,\n};\nuse actix_web::{middleware::Logger, post, App, HttpServer, Responder};\nuse serde::Deserialize;\n\n#[derive(Debug, Deserialize)]\nstruct Metadata {\n    name: String,\n}\n\n#[derive(Debug, MultipartForm)]\nstruct UploadForm {\n    // Note: the form is also subject to the global limits configured using `MultipartFormConfig`.\n    #[multipart(limit = \"100MB\")]\n    file: TempFile,\n    json: MpJson<Metadata>,\n}\n\n#[post(\"/videos\")]\nasync fn post_video(MultipartForm(form): MultipartForm<UploadForm>) -> impl Responder {\n    format!(\n        \"Uploaded file {}, with size: {}\\ntemporary file ({}) was deleted\\n\",\n        form.json.name,\n        form.file.size,\n        form.file.file.path().display(),\n    )\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    HttpServer::new(move || {\n        App::new()\n            .service(post_video)\n            .wrap(Logger::default())\n            // Also increase the global total limit to 100MiB.\n            .app_data(MultipartFormConfig::default().total_limit(100 * 1024 * 1024))\n    })\n    .workers(2)\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\ncURL request:\n\n```sh\ncurl -v --request POST \\\n  --url http://localhost:8080/videos \\\n  -F 'json={\"name\": \"Cargo.lock\"};type=application/json' \\\n  -F file=@./Cargo.lock\n```\n\n[`MultipartForm`]: struct@form::MultipartForm\n\n<!-- cargo-rdme end -->\n\n[More available in the examples repo &rarr;](https://github.com/actix/examples/tree/main/forms/multipart)\n"
  },
  {
    "path": "actix-multipart/examples/form.rs",
    "content": "use actix_multipart::form::{\n    json::Json as MpJson, tempfile::TempFile, MultipartForm, MultipartFormConfig,\n};\nuse actix_web::{middleware::Logger, post, App, HttpServer, Responder};\nuse serde::Deserialize;\n\n#[derive(Debug, Deserialize)]\nstruct Metadata {\n    name: String,\n}\n\n#[derive(Debug, MultipartForm)]\nstruct UploadForm {\n    // Note: the form is also subject to the global limits configured using `MultipartFormConfig`.\n    #[multipart(limit = \"100MB\")]\n    file: TempFile,\n    json: MpJson<Metadata>,\n}\n\n#[post(\"/videos\")]\nasync fn post_video(MultipartForm(form): MultipartForm<UploadForm>) -> impl Responder {\n    format!(\n        \"Uploaded file {}, with size: {}\\ntemporary file ({}) was deleted\\n\",\n        form.json.name,\n        form.file.size,\n        form.file.file.path().display(),\n    )\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    HttpServer::new(move || {\n        App::new()\n            .service(post_video)\n            .wrap(Logger::default())\n            // Also increase the global total limit to 100MiB.\n            .app_data(MultipartFormConfig::default().total_limit(100 * 1024 * 1024))\n    })\n    .workers(2)\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-multipart/src/error.rs",
    "content": "//! Error and Result module\n\nuse actix_web::{\n    error::{ParseError, PayloadError},\n    http::StatusCode,\n    ResponseError,\n};\nuse derive_more::{Display, Error, From};\n\n/// A set of errors that can occur during parsing multipart streams.\n#[derive(Debug, Display, From, Error)]\n#[non_exhaustive]\npub enum Error {\n    /// Could not find Content-Type header.\n    #[display(\"Could not find Content-Type header\")]\n    ContentTypeMissing,\n\n    /// Could not parse Content-Type header.\n    #[display(\"Could not parse Content-Type header\")]\n    ContentTypeParse,\n\n    /// Parsed Content-Type did not have \"multipart\" top-level media type.\n    ///\n    /// Also raised when extracting a [`MultipartForm`] from a request that does not have the\n    /// \"multipart/form-data\" media type.\n    ///\n    /// [`MultipartForm`]: struct@crate::form::MultipartForm\n    #[display(\"Parsed Content-Type did not have 'multipart' top-level media type\")]\n    ContentTypeIncompatible,\n\n    /// Multipart boundary is not found.\n    #[display(\"Multipart boundary is not found\")]\n    BoundaryMissing,\n\n    /// Content-Disposition header was not found or not of disposition type \"form-data\" when parsing\n    /// a \"form-data\" field.\n    ///\n    /// As per [RFC 7578 §4.2], a \"multipart/form-data\" field's Content-Disposition header must\n    /// always be present and have a disposition type of \"form-data\".\n    ///\n    /// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n    #[display(\"Content-Disposition header was not found when parsing a \\\"form-data\\\" field\")]\n    ContentDispositionMissing,\n\n    /// Content-Disposition name parameter was not found when parsing a \"form-data\" field.\n    ///\n    /// As per [RFC 7578 §4.2], a \"multipart/form-data\" field's Content-Disposition header must\n    /// always include a \"name\" parameter.\n    ///\n    /// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n    #[display(\"Content-Disposition header was not found when parsing a \\\"form-data\\\" field\")]\n    ContentDispositionNameMissing,\n\n    /// Nested multipart is not supported.\n    #[display(\"Nested multipart is not supported\")]\n    Nested,\n\n    /// Multipart stream is incomplete.\n    #[display(\"Multipart stream is incomplete\")]\n    Incomplete,\n\n    /// Field parsing failed.\n    #[display(\"Error during field parsing\")]\n    Parse(ParseError),\n\n    /// HTTP payload error.\n    #[display(\"Payload error\")]\n    Payload(PayloadError),\n\n    /// Stream is not consumed.\n    #[display(\"Stream is not consumed\")]\n    NotConsumed,\n\n    /// Form field handler raised error.\n    #[display(\"An error occurred processing field: {name}\")]\n    Field {\n        name: String,\n        source: actix_web::Error,\n    },\n\n    /// Duplicate field found (for structure that opted-in to denying duplicate fields).\n    #[display(\"Duplicate field found: {_0}\")]\n    #[from(ignore)]\n    DuplicateField(#[error(not(source))] String),\n\n    /// Required field is missing.\n    #[display(\"Required field is missing: {_0}\")]\n    #[from(ignore)]\n    MissingField(#[error(not(source))] String),\n\n    /// Unknown field (for structure that opted-in to denying unknown fields).\n    #[display(\"Unknown field: {_0}\")]\n    #[from(ignore)]\n    UnknownField(#[error(not(source))] String),\n}\n\n/// Return `BadRequest` for `MultipartError`.\nimpl ResponseError for Error {\n    fn status_code(&self) -> StatusCode {\n        match &self {\n            Error::Field { source, .. } => source.as_response_error().status_code(),\n            Error::ContentTypeIncompatible => StatusCode::UNSUPPORTED_MEDIA_TYPE,\n            _ => StatusCode::BAD_REQUEST,\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_multipart_error() {\n        let resp = Error::BoundaryMissing.error_response();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/extractor.rs",
    "content": "use actix_utils::future::{ready, Ready};\nuse actix_web::{dev::Payload, Error, FromRequest, HttpRequest};\n\nuse crate::multipart::Multipart;\n\n/// Extract request's payload as multipart stream.\n///\n/// Content-type: multipart/*;\n///\n/// # Examples\n///\n/// ```\n/// use actix_web::{web, HttpResponse};\n/// use actix_multipart::Multipart;\n/// use futures_util::StreamExt as _;\n///\n/// async fn index(mut payload: Multipart) -> actix_web::Result<HttpResponse> {\n///     // iterate over multipart stream\n///     while let Some(item) = payload.next().await {\n///            let mut field = item?;\n///\n///            // Field in turn is stream of *Bytes* object\n///            while let Some(chunk) = field.next().await {\n///                println!(\"-- CHUNK: \\n{:?}\", std::str::from_utf8(&chunk?));\n///            }\n///     }\n///\n///     Ok(HttpResponse::Ok().finish())\n/// }\n/// ```\nimpl FromRequest for Multipart {\n    type Error = Error;\n    type Future = Ready<Result<Multipart, Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n        ready(Ok(Multipart::from_req(req, payload)))\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/field.rs",
    "content": "use std::{\n    cell::RefCell,\n    cmp, fmt,\n    future::poll_fn,\n    mem,\n    pin::Pin,\n    rc::Rc,\n    task::{ready, Context, Poll},\n};\n\nuse actix_web::{\n    error::PayloadError,\n    http::header::{self, ContentDisposition, HeaderMap},\n    web::{Bytes, BytesMut},\n};\nuse derive_more::{Display, Error};\nuse futures_core::Stream;\nuse mime::Mime;\n\nuse crate::{\n    error::Error,\n    payload::{PayloadBuffer, PayloadRef},\n    safety::Safety,\n};\n\n/// Error type returned from [`Field::bytes()`] when field data is larger than limit.\n#[derive(Debug, Display, Error)]\n#[display(\"size limit exceeded while collecting field data\")]\n#[non_exhaustive]\npub struct LimitExceeded;\n\n/// A single field in a multipart stream.\npub struct Field {\n    /// Field's Content-Type.\n    content_type: Option<Mime>,\n\n    /// Field's Content-Disposition.\n    content_disposition: Option<ContentDisposition>,\n\n    /// Form field name.\n    ///\n    /// A non-optional storage for form field names to avoid unwraps in `form` module. Will be an\n    /// empty string in non-form contexts.\n    ///\n    // INVARIANT: always non-empty when request content-type is multipart/form-data.\n    pub(crate) form_field_name: String,\n\n    /// Field's header map.\n    headers: HeaderMap,\n\n    safety: Safety,\n    inner: Rc<RefCell<InnerField>>,\n}\n\nimpl Field {\n    pub(crate) fn new(\n        content_type: Option<Mime>,\n        content_disposition: Option<ContentDisposition>,\n        form_field_name: Option<String>,\n        headers: HeaderMap,\n        safety: Safety,\n        inner: Rc<RefCell<InnerField>>,\n    ) -> Self {\n        Field {\n            content_type,\n            content_disposition,\n            form_field_name: form_field_name.unwrap_or_default(),\n            headers,\n            inner,\n            safety,\n        }\n    }\n\n    /// Returns a reference to the field's header map.\n    pub fn headers(&self) -> &HeaderMap {\n        &self.headers\n    }\n\n    /// Returns a reference to the field's content (mime) type, if it is supplied by the client.\n    ///\n    /// According to [RFC 7578](https://www.rfc-editor.org/rfc/rfc7578#section-4.4), if it is not\n    /// present, it should default to \"text/plain\". Note it is the responsibility of the client to\n    /// provide the appropriate content type, there is no attempt to validate this by the server.\n    pub fn content_type(&self) -> Option<&Mime> {\n        self.content_type.as_ref()\n    }\n\n    /// Returns this field's parsed Content-Disposition header, if set.\n    ///\n    /// # Validation\n    ///\n    /// Per [RFC 7578 §4.2], the parts of a multipart/form-data payload MUST contain a\n    /// Content-Disposition header field where the disposition type is `form-data` and MUST also\n    /// contain an additional parameter of `name` with its value being the original field name from\n    /// the form. This requirement is enforced during extraction for multipart/form-data requests,\n    /// but not other kinds of multipart requests (such as multipart/related).\n    ///\n    /// As such, it is safe to `.unwrap()` calls `.content_disposition()` if you've verified.\n    ///\n    /// The [`name()`](Self::name) method is also provided as a convenience for obtaining the\n    /// aforementioned name parameter.\n    ///\n    /// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n    pub fn content_disposition(&self) -> Option<&ContentDisposition> {\n        self.content_disposition.as_ref()\n    }\n\n    /// Returns the field's name, if set.\n    ///\n    /// See [`content_disposition()`](Self::content_disposition) regarding guarantees on presence of\n    /// the \"name\" field.\n    pub fn name(&self) -> Option<&str> {\n        self.content_disposition()?.get_name()\n    }\n\n    /// Collects the raw field data, up to `limit` bytes.\n    ///\n    /// # Errors\n    ///\n    /// Any errors produced by the data stream are returned as `Ok(Err(Error))` immediately.\n    ///\n    /// If the buffered data size would exceed `limit`, an `Err(LimitExceeded)` is returned. Note\n    /// that, in this case, the full data stream is exhausted before returning the error so that\n    /// subsequent fields can still be read. To better defend against malicious/infinite requests,\n    /// it is advisable to also put a timeout on this call.\n    pub async fn bytes(&mut self, limit: usize) -> Result<Result<Bytes, Error>, LimitExceeded> {\n        /// Sensible default (2kB) for initial, bounded allocation when collecting body bytes.\n        const INITIAL_ALLOC_BYTES: usize = 2 * 1024;\n\n        let mut exceeded_limit = false;\n        let mut buf = BytesMut::with_capacity(INITIAL_ALLOC_BYTES);\n\n        let mut field = Pin::new(self);\n\n        match poll_fn(|cx| loop {\n            match ready!(field.as_mut().poll_next(cx)) {\n                // if already over limit, discard chunk to advance multipart request\n                Some(Ok(_chunk)) if exceeded_limit => {}\n\n                // if limit is exceeded set flag to true and continue\n                Some(Ok(chunk)) if buf.len() + chunk.len() > limit => {\n                    exceeded_limit = true;\n                    // eagerly de-allocate field data buffer\n                    let _ = mem::take(&mut buf);\n                }\n\n                Some(Ok(chunk)) => buf.extend_from_slice(&chunk),\n\n                None => return Poll::Ready(Ok(())),\n                Some(Err(err)) => return Poll::Ready(Err(err)),\n            }\n        })\n        .await\n        {\n            // propagate error returned from body poll\n            Err(err) => Ok(Err(err)),\n\n            // limit was exceeded while reading body\n            Ok(()) if exceeded_limit => Err(LimitExceeded),\n\n            // otherwise return body buffer\n            Ok(()) => Ok(Ok(buf.freeze())),\n        }\n    }\n}\n\nimpl Stream for Field {\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.get_mut();\n        let mut inner = this.inner.borrow_mut();\n\n        if let Some(mut buffer) = inner\n            .payload\n            .as_ref()\n            .expect(\"Field should not be polled after completion\")\n            .get_mut(&this.safety)\n        {\n            // check safety and poll read payload to buffer.\n            buffer.poll_stream(cx)?;\n        } else if !this.safety.is_clean() {\n            // safety violation\n            return Poll::Ready(Some(Err(Error::NotConsumed)));\n        } else {\n            return Poll::Pending;\n        }\n\n        inner.poll(&this.safety)\n    }\n}\n\nimpl fmt::Debug for Field {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if let Some(ct) = &self.content_type {\n            writeln!(f, \"\\nField: {}\", ct)?;\n        } else {\n            writeln!(f, \"\\nField:\")?;\n        }\n        writeln!(f, \"  boundary: {}\", self.inner.borrow().boundary)?;\n        writeln!(f, \"  headers:\")?;\n        for (key, val) in self.headers.iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n        Ok(())\n    }\n}\n\npub(crate) struct InnerField {\n    /// Payload is initialized as Some and is `take`n when the field stream finishes.\n    payload: Option<PayloadRef>,\n\n    /// Field boundary (without \"--\" prefix).\n    boundary: String,\n\n    /// True if request payload has been exhausted.\n    eof: bool,\n\n    /// Field data's stated size according to it's Content-Length header.\n    length: Option<u64>,\n}\n\nimpl InnerField {\n    pub(crate) fn new_in_rc(\n        payload: PayloadRef,\n        boundary: String,\n        headers: &HeaderMap,\n    ) -> Result<Rc<RefCell<InnerField>>, PayloadError> {\n        Self::new(payload, boundary, headers).map(|this| Rc::new(RefCell::new(this)))\n    }\n\n    pub(crate) fn new(\n        payload: PayloadRef,\n        boundary: String,\n        headers: &HeaderMap,\n    ) -> Result<InnerField, PayloadError> {\n        let len = if let Some(len) = headers.get(&header::CONTENT_LENGTH) {\n            match len.to_str().ok().and_then(|len| len.parse::<u64>().ok()) {\n                Some(len) => Some(len),\n                None => return Err(PayloadError::Incomplete(None)),\n            }\n        } else {\n            None\n        };\n\n        Ok(InnerField {\n            boundary,\n            payload: Some(payload),\n            eof: false,\n            length: len,\n        })\n    }\n\n    /// Reads body part content chunk of the specified size.\n    ///\n    /// The body part must has `Content-Length` header with proper value.\n    pub(crate) fn read_len(\n        payload: &mut PayloadBuffer,\n        size: &mut u64,\n    ) -> Poll<Option<Result<Bytes, Error>>> {\n        if *size == 0 {\n            Poll::Ready(None)\n        } else {\n            match payload.read_max(*size)? {\n                Some(mut chunk) => {\n                    let len = cmp::min(chunk.len() as u64, *size);\n                    *size -= len;\n                    let ch = chunk.split_to(len as usize);\n                    if !chunk.is_empty() {\n                        payload.unprocessed(chunk);\n                    }\n                    Poll::Ready(Some(Ok(ch)))\n                }\n                None => {\n                    if payload.eof && (*size != 0) {\n                        Poll::Ready(Some(Err(Error::Incomplete)))\n                    } else {\n                        Poll::Pending\n                    }\n                }\n            }\n        }\n    }\n\n    /// Reads content chunk of body part with unknown length.\n    ///\n    /// The `Content-Length` header for body part is not necessary.\n    pub(crate) fn read_stream(\n        payload: &mut PayloadBuffer,\n        boundary: &str,\n    ) -> Poll<Option<Result<Bytes, Error>>> {\n        let mut pos = 0;\n\n        let len = payload.buf.len();\n\n        if len == 0 {\n            return if payload.eof {\n                Poll::Ready(Some(Err(Error::Incomplete)))\n            } else {\n                Poll::Pending\n            };\n        }\n\n        // check boundary\n        if len > 4 && payload.buf[0] == b'\\r' {\n            let b_len = if payload.buf.starts_with(b\"\\r\\n\") && &payload.buf[2..4] == b\"--\" {\n                Some(4)\n            } else if &payload.buf[1..3] == b\"--\" {\n                Some(3)\n            } else {\n                None\n            };\n\n            if let Some(b_len) = b_len {\n                let b_size = boundary.len() + b_len;\n                if len < b_size {\n                    return Poll::Pending;\n                } else if &payload.buf[b_len..b_size] == boundary.as_bytes() {\n                    // found boundary\n                    return Poll::Ready(None);\n                }\n            }\n        }\n\n        loop {\n            return if let Some(idx) = memchr::memmem::find(&payload.buf[pos..], b\"\\r\") {\n                let cur = pos + idx;\n\n                // check if we have enough data for boundary detection\n                if cur + 4 > len {\n                    if cur > 0 {\n                        Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))\n                    } else {\n                        Poll::Pending\n                    }\n                } else {\n                    // check boundary\n                    if (&payload.buf[cur..cur + 2] == b\"\\r\\n\"\n                        && &payload.buf[cur + 2..cur + 4] == b\"--\")\n                        || (&payload.buf[cur..=cur] == b\"\\r\"\n                            && &payload.buf[cur + 1..cur + 3] == b\"--\")\n                    {\n                        if cur != 0 {\n                            // return buffer\n                            Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze())))\n                        } else {\n                            pos = cur + 1;\n                            continue;\n                        }\n                    } else {\n                        // not boundary\n                        pos = cur + 1;\n                        continue;\n                    }\n                }\n            } else {\n                Poll::Ready(Some(Ok(payload.buf.split().freeze())))\n            };\n        }\n    }\n\n    pub(crate) fn poll(&mut self, safety: &Safety) -> Poll<Option<Result<Bytes, Error>>> {\n        if self.payload.is_none() {\n            return Poll::Ready(None);\n        }\n\n        let Some(mut payload) = self\n            .payload\n            .as_ref()\n            .expect(\"Field should not be polled after completion\")\n            .get_mut(safety)\n        else {\n            return Poll::Pending;\n        };\n\n        if !self.eof {\n            let res = if let Some(ref mut len) = self.length {\n                Self::read_len(&mut payload, len)\n            } else {\n                Self::read_stream(&mut payload, &self.boundary)\n            };\n\n            match ready!(res) {\n                Some(Ok(bytes)) => return Poll::Ready(Some(Ok(bytes))),\n                Some(Err(err)) => return Poll::Ready(Some(Err(err))),\n                None => self.eof = true,\n            }\n        }\n\n        let result = match payload.readline() {\n            Ok(None) => Poll::Pending,\n            Ok(Some(line)) => {\n                if line.as_ref() != b\"\\r\\n\" {\n                    log::warn!(\"multipart field did not read all the data or it is malformed\");\n                }\n                Poll::Ready(None)\n            }\n            Err(err) => Poll::Ready(Some(Err(err))),\n        };\n\n        drop(payload);\n\n        if let Poll::Ready(None) = result {\n            // drop payload buffer and make future un-poll-able\n            let _ = self.payload.take();\n        }\n\n        result\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use futures_util::{stream, StreamExt as _};\n\n    use super::*;\n    use crate::Multipart;\n\n    // TODO: use test utility when multi-file support is introduced\n    fn create_double_request_with_header() -> (Bytes, HeaderMap) {\n        let bytes = Bytes::from(\n            \"testasdadsad\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Disposition: form-data; name=\\\"file\\\"; filename=\\\"fn.txt\\\"\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\n\\\n             \\r\\n\\\n             one+one+one\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Disposition: form-data; name=\\\"file\\\"; filename=\\\"fn.txt\\\"\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\n\\\n             \\r\\n\\\n             two+two+two\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0--\\r\\n\",\n        );\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/mixed; boundary=\\\"abbc761f78ff4d7cb7573b5a23f96ef0\\\"\",\n            ),\n        );\n        (bytes, headers)\n    }\n\n    #[actix_rt::test]\n    async fn bytes_unlimited() {\n        let (body, headers) = create_double_request_with_header();\n\n        let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));\n\n        let field = multipart\n            .next()\n            .await\n            .expect(\"multipart should have two fields\")\n            .expect(\"multipart body should be well formatted\")\n            .bytes(usize::MAX)\n            .await\n            .expect(\"field data should not be size limited\")\n            .expect(\"reading field data should not error\");\n        assert_eq!(field, \"one+one+one\");\n\n        let field = multipart\n            .next()\n            .await\n            .expect(\"multipart should have two fields\")\n            .expect(\"multipart body should be well formatted\")\n            .bytes(usize::MAX)\n            .await\n            .expect(\"field data should not be size limited\")\n            .expect(\"reading field data should not error\");\n        assert_eq!(field, \"two+two+two\");\n    }\n\n    #[actix_rt::test]\n    async fn bytes_limited() {\n        let (body, headers) = create_double_request_with_header();\n\n        let mut multipart = Multipart::new(&headers, stream::iter([Ok(body)]));\n\n        multipart\n            .next()\n            .await\n            .expect(\"multipart should have two fields\")\n            .expect(\"multipart body should be well formatted\")\n            .bytes(8) // smaller than data size\n            .await\n            .expect_err(\"field data should be size limited\");\n\n        // next field still readable\n        let field = multipart\n            .next()\n            .await\n            .expect(\"multipart should have two fields\")\n            .expect(\"multipart body should be well formatted\")\n            .bytes(usize::MAX)\n            .await\n            .expect(\"field data should not be size limited\")\n            .expect(\"reading field data should not error\");\n        assert_eq!(field, \"two+two+two\");\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/form/bytes.rs",
    "content": "//! Reads a field into memory.\n\nuse actix_web::{web::BytesMut, HttpRequest};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::TryStreamExt as _;\nuse mime::Mime;\n\nuse crate::{\n    form::{FieldReader, Limits},\n    Field, MultipartError,\n};\n\n/// Read the field into memory.\n#[derive(Debug)]\npub struct Bytes {\n    /// The data.\n    pub data: actix_web::web::Bytes,\n\n    /// The value of the `Content-Type` header.\n    pub content_type: Option<Mime>,\n\n    /// The `filename` value in the `Content-Disposition` header.\n    pub file_name: Option<String>,\n}\n\nimpl<'t> FieldReader<'t> for Bytes {\n    type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;\n\n    fn read_field(_: &'t HttpRequest, mut field: Field, limits: &'t mut Limits) -> Self::Future {\n        Box::pin(async move {\n            let mut buf = BytesMut::with_capacity(131_072);\n\n            while let Some(chunk) = field.try_next().await? {\n                limits.try_consume_limits(chunk.len(), true)?;\n                buf.extend(chunk);\n            }\n\n            Ok(Bytes {\n                data: buf.freeze(),\n                content_type: field.content_type().map(ToOwned::to_owned),\n                file_name: field\n                    .content_disposition()\n                    .expect(\"multipart form fields should have a content-disposition header\")\n                    .get_filename()\n                    .map(ToOwned::to_owned),\n            })\n        })\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/form/json.rs",
    "content": "//! Deserializes a field as JSON.\n\nuse std::sync::Arc;\n\nuse actix_web::{http::StatusCode, web, Error, HttpRequest, ResponseError};\nuse derive_more::{Deref, DerefMut, Display, Error};\nuse futures_core::future::LocalBoxFuture;\nuse serde::de::DeserializeOwned;\n\nuse super::FieldErrorHandler;\nuse crate::{\n    form::{bytes::Bytes, FieldReader, Limits},\n    Field, MultipartError,\n};\n\n/// Deserialize from JSON.\n#[derive(Debug, Deref, DerefMut)]\npub struct Json<T: DeserializeOwned>(pub T);\n\nimpl<T: DeserializeOwned> Json<T> {\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<'t, T> FieldReader<'t> for Json<T>\nwhere\n    T: DeserializeOwned + 'static,\n{\n    type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;\n\n    fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future {\n        Box::pin(async move {\n            let config = JsonConfig::from_req(req);\n\n            if config.validate_content_type {\n                let valid = if let Some(mime) = field.content_type() {\n                    mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)\n                } else {\n                    false\n                };\n\n                if !valid {\n                    return Err(MultipartError::Field {\n                        name: field.form_field_name,\n                        source: config.map_error(req, JsonFieldError::ContentType),\n                    });\n                }\n            }\n\n            let form_field_name = field.form_field_name.clone();\n\n            let bytes = Bytes::read_field(req, field, limits).await?;\n\n            Ok(Json(serde_json::from_slice(bytes.data.as_ref()).map_err(\n                |err| MultipartError::Field {\n                    name: form_field_name,\n                    source: config.map_error(req, JsonFieldError::Deserialize(err)),\n                },\n            )?))\n        })\n    }\n}\n\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum JsonFieldError {\n    /// Deserialize error.\n    #[display(\"Json deserialize error: {}\", _0)]\n    Deserialize(serde_json::Error),\n\n    /// Content type error.\n    #[display(\"Content type error\")]\n    ContentType,\n}\n\nimpl ResponseError for JsonFieldError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\n/// Configuration for the [`Json`] field reader.\n#[derive(Clone)]\npub struct JsonConfig {\n    err_handler: FieldErrorHandler<JsonFieldError>,\n    validate_content_type: bool,\n}\n\nconst DEFAULT_CONFIG: JsonConfig = JsonConfig {\n    err_handler: None,\n    validate_content_type: true,\n};\n\nimpl JsonConfig {\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(JsonFieldError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n\n    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n\n    fn map_error(&self, req: &HttpRequest, err: JsonFieldError) -> Error {\n        if let Some(err_handler) = self.err_handler.as_ref() {\n            (*err_handler)(err, req)\n        } else {\n            err.into()\n        }\n    }\n\n    /// Sets whether or not the field must have a valid `Content-Type` header to be parsed.\n    pub fn validate_content_type(mut self, validate_content_type: bool) -> Self {\n        self.validate_content_type = validate_content_type;\n        self\n    }\n}\n\nimpl Default for JsonConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::collections::HashMap;\n\n    use actix_web::{http::StatusCode, web, web::Bytes, App, HttpResponse, Responder};\n\n    use crate::form::{\n        json::{Json, JsonConfig},\n        MultipartForm,\n    };\n\n    #[derive(MultipartForm)]\n    struct JsonForm {\n        json: Json<HashMap<String, String>>,\n    }\n\n    async fn test_json_route(form: MultipartForm<JsonForm>) -> impl Responder {\n        let mut expected = HashMap::new();\n        expected.insert(\"key1\".to_owned(), \"value1\".to_owned());\n        expected.insert(\"key2\".to_owned(), \"value2\".to_owned());\n        assert_eq!(&*form.json, &expected);\n        HttpResponse::Ok().finish()\n    }\n\n    const TEST_JSON: &str = r#\"{\"key1\": \"value1\", \"key2\": \"value2\"}\"#;\n\n    #[actix_rt::test]\n    async fn test_json_without_content_type() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/\", web::post().to(test_json_route))\n                .app_data(JsonConfig::default().validate_content_type(false))\n        });\n\n        let (body, headers) = crate::test::create_form_data_payload_and_headers(\n            \"json\",\n            None,\n            None,\n            Bytes::from_static(TEST_JSON.as_bytes()),\n        );\n        let mut req = srv.post(\"/\");\n        *req.headers_mut() = headers;\n        let res = req.send_body(body).await.unwrap();\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_content_type_validation() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/\", web::post().to(test_json_route))\n                .app_data(JsonConfig::default().validate_content_type(true))\n        });\n\n        // Deny because wrong content type\n        let (body, headers) = crate::test::create_form_data_payload_and_headers(\n            \"json\",\n            None,\n            Some(mime::APPLICATION_OCTET_STREAM),\n            Bytes::from_static(TEST_JSON.as_bytes()),\n        );\n        let mut req = srv.post(\"/\");\n        *req.headers_mut() = headers;\n        let res = req.send_body(body).await.unwrap();\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n\n        // Allow because correct content type\n        let (body, headers) = crate::test::create_form_data_payload_and_headers(\n            \"json\",\n            None,\n            Some(mime::APPLICATION_JSON),\n            Bytes::from_static(TEST_JSON.as_bytes()),\n        );\n        let mut req = srv.post(\"/\");\n        *req.headers_mut() = headers;\n        let res = req.send_body(body).await.unwrap();\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/form/mod.rs",
    "content": "//! Extract and process typed data from fields of a `multipart/form-data` request.\n\nuse std::{\n    any::Any,\n    collections::HashMap,\n    future::{ready, Future},\n    sync::Arc,\n};\n\nuse actix_web::{dev, error::PayloadError, web, Error, FromRequest, HttpRequest};\nuse derive_more::{Deref, DerefMut};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::{TryFutureExt as _, TryStreamExt as _};\n\nuse crate::{Field, Multipart, MultipartError};\n\npub mod bytes;\npub mod json;\n#[cfg(feature = \"tempfile\")]\npub mod tempfile;\npub mod text;\n\n#[cfg(feature = \"derive\")]\npub use actix_multipart_derive::MultipartForm;\n\ntype FieldErrorHandler<T> = Option<Arc<dyn Fn(T, &HttpRequest) -> Error + Send + Sync>>;\n\n/// Trait that data types to be used in a multipart form struct should implement.\n///\n/// It represents an asynchronous handler that processes a multipart field to produce `Self`.\npub trait FieldReader<'t>: Sized + Any {\n    /// Future that resolves to a `Self`.\n    type Future: Future<Output = Result<Self, MultipartError>>;\n\n    /// The form will call this function to handle the field.\n    ///\n    /// # Panics\n    ///\n    /// When reading the `field` payload using its `Stream` implementation, polling (manually or via\n    /// `next()`/`try_next()`) may panic after the payload is exhausted. If this is a problem for\n    /// your implementation of this method, you should [`fuse()`] the `Field` first.\n    ///\n    /// [`fuse()`]: futures_util::stream::StreamExt::fuse()\n    fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future;\n}\n\n/// Used to accumulate the state of the loaded fields.\n#[doc(hidden)]\n#[derive(Default, Deref, DerefMut)]\npub struct State(pub HashMap<String, Box<dyn Any>>);\n\n/// Trait that the field collection types implement, i.e. `Vec<T>`, `Option<T>`, or `T` itself.\n#[doc(hidden)]\npub trait FieldGroupReader<'t>: Sized + Any {\n    type Future: Future<Output = Result<(), MultipartError>>;\n\n    /// The form will call this function for each matching field.\n    fn handle_field(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n        duplicate_field: DuplicateField,\n    ) -> Self::Future;\n\n    /// Construct `Self` from the group of processed fields.\n    fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError>;\n}\n\nimpl<'t, T> FieldGroupReader<'t> for Option<T>\nwhere\n    T: FieldReader<'t>,\n{\n    type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;\n\n    fn handle_field(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n        duplicate_field: DuplicateField,\n    ) -> Self::Future {\n        if state.contains_key(&field.form_field_name) {\n            match duplicate_field {\n                DuplicateField::Ignore => return Box::pin(ready(Ok(()))),\n\n                DuplicateField::Deny => {\n                    return Box::pin(ready(Err(MultipartError::DuplicateField(\n                        field.form_field_name,\n                    ))))\n                }\n\n                DuplicateField::Replace => {}\n            }\n        }\n\n        Box::pin(async move {\n            let field_name = field.form_field_name.clone();\n            let t = T::read_field(req, field, limits).await?;\n            state.insert(field_name, Box::new(t));\n            Ok(())\n        })\n    }\n\n    fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {\n        Ok(state.remove(name).map(|m| *m.downcast::<T>().unwrap()))\n    }\n}\n\nimpl<'t, T> FieldGroupReader<'t> for Vec<T>\nwhere\n    T: FieldReader<'t>,\n{\n    type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;\n\n    fn handle_field(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n        _duplicate_field: DuplicateField,\n    ) -> Self::Future {\n        Box::pin(async move {\n            // Note: Vec GroupReader always allows duplicates\n\n            let vec = state\n                .entry(field.form_field_name.clone())\n                .or_insert_with(|| Box::<Vec<T>>::default())\n                .downcast_mut::<Vec<T>>()\n                .unwrap();\n\n            let item = T::read_field(req, field, limits).await?;\n            vec.push(item);\n\n            Ok(())\n        })\n    }\n\n    fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {\n        Ok(state\n            .remove(name)\n            .map(|m| *m.downcast::<Vec<T>>().unwrap())\n            .unwrap_or_default())\n    }\n}\n\nimpl<'t, T> FieldGroupReader<'t> for T\nwhere\n    T: FieldReader<'t>,\n{\n    type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;\n\n    fn handle_field(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n        duplicate_field: DuplicateField,\n    ) -> Self::Future {\n        if state.contains_key(&field.form_field_name) {\n            match duplicate_field {\n                DuplicateField::Ignore => return Box::pin(ready(Ok(()))),\n\n                DuplicateField::Deny => {\n                    return Box::pin(ready(Err(MultipartError::DuplicateField(\n                        field.form_field_name,\n                    ))))\n                }\n\n                DuplicateField::Replace => {}\n            }\n        }\n\n        Box::pin(async move {\n            let field_name = field.form_field_name.clone();\n            let t = T::read_field(req, field, limits).await?;\n            state.insert(field_name, Box::new(t));\n            Ok(())\n        })\n    }\n\n    fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {\n        state\n            .remove(name)\n            .map(|m| *m.downcast::<T>().unwrap())\n            .ok_or_else(|| MultipartError::MissingField(name.to_owned()))\n    }\n}\n\nimpl<'t, T> FieldGroupReader<'t> for Option<Vec<T>>\nwhere\n    T: FieldReader<'t>,\n{\n    type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;\n\n    fn handle_field(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n        _duplicate_field: DuplicateField,\n    ) -> Self::Future {\n        let field_name = field.name().unwrap().to_string();\n\n        Box::pin(async move {\n            let vec = state\n                .entry(field_name)\n                .or_insert_with(|| Box::<Vec<T>>::default())\n                .downcast_mut::<Vec<T>>()\n                .unwrap();\n\n            let item = T::read_field(req, field, limits).await?;\n            vec.push(item);\n\n            Ok(())\n        })\n    }\n\n    fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {\n        if let Some(boxed_vec) = state.remove(name) {\n            let vec = *boxed_vec.downcast::<Vec<T>>().unwrap();\n            Ok(Some(vec))\n        } else {\n            Ok(None)\n        }\n    }\n}\n\n/// Trait that allows a type to be used in the [`struct@MultipartForm`] extractor.\n///\n/// You should use the [`macro@MultipartForm`] macro to derive this for your struct.\npub trait MultipartCollect: Sized {\n    /// An optional limit in bytes to be applied a given field name. Note this limit will be shared\n    /// across all fields sharing the same name.\n    fn limit(field_name: &str) -> Option<usize>;\n\n    /// The extractor will call this function for each incoming field, the state can be updated\n    /// with the processed field data.\n    fn handle_field<'t>(\n        req: &'t HttpRequest,\n        field: Field,\n        limits: &'t mut Limits,\n        state: &'t mut State,\n    ) -> LocalBoxFuture<'t, Result<(), MultipartError>>;\n\n    /// Once all the fields have been processed and stored in the state, this is called\n    /// to convert into the struct representation.\n    fn from_state(state: State) -> Result<Self, MultipartError>;\n}\n\n#[doc(hidden)]\npub enum DuplicateField {\n    /// Additional fields are not processed.\n    Ignore,\n\n    /// An error will be raised.\n    Deny,\n\n    /// All fields will be processed, the last one will replace all previous.\n    Replace,\n}\n\n/// Used to keep track of the remaining limits for the form and current field.\npub struct Limits {\n    pub total_limit_remaining: usize,\n    pub memory_limit_remaining: usize,\n    pub field_limit_remaining: Option<usize>,\n}\n\nimpl Limits {\n    pub fn new(total_limit: usize, memory_limit: usize) -> Self {\n        Self {\n            total_limit_remaining: total_limit,\n            memory_limit_remaining: memory_limit,\n            field_limit_remaining: None,\n        }\n    }\n\n    /// This function should be called within a [`FieldReader`] when reading each chunk of a field\n    /// to ensure that the form limits are not exceeded.\n    ///\n    /// # Arguments\n    ///\n    /// * `bytes` - The number of bytes being read from this chunk\n    /// * `in_memory` - Whether to consume from the memory limits\n    pub fn try_consume_limits(\n        &mut self,\n        bytes: usize,\n        in_memory: bool,\n    ) -> Result<(), MultipartError> {\n        self.total_limit_remaining = self\n            .total_limit_remaining\n            .checked_sub(bytes)\n            .ok_or(MultipartError::Payload(PayloadError::Overflow))?;\n\n        if in_memory {\n            self.memory_limit_remaining = self\n                .memory_limit_remaining\n                .checked_sub(bytes)\n                .ok_or(MultipartError::Payload(PayloadError::Overflow))?;\n        }\n\n        if let Some(field_limit) = self.field_limit_remaining {\n            self.field_limit_remaining = Some(\n                field_limit\n                    .checked_sub(bytes)\n                    .ok_or(MultipartError::Payload(PayloadError::Overflow))?,\n            );\n        }\n\n        Ok(())\n    }\n}\n\n/// Typed `multipart/form-data` extractor.\n///\n/// To extract typed data from a multipart stream, the inner type `T` must implement the\n/// [`MultipartCollect`] trait. You should use the [`macro@MultipartForm`] macro to derive this\n/// for your struct.\n///\n/// Note that this extractor rejects requests with any other Content-Type such as `multipart/mixed`,\n/// `multipart/related`, or non-multipart media types.\n///\n/// Add a [`MultipartFormConfig`] to your app data to configure extraction.\n#[derive(Deref, DerefMut)]\npub struct MultipartForm<T: MultipartCollect>(pub T);\n\nimpl<T: MultipartCollect> MultipartForm<T> {\n    /// Unwrap into inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T> FromRequest for MultipartForm<T>\nwhere\n    T: MultipartCollect + 'static,\n{\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n        let mut multipart = Multipart::from_req(req, payload);\n\n        let content_type = match multipart.content_type_or_bail() {\n            Ok(content_type) => content_type,\n            Err(err) => return Box::pin(ready(Err(err.into()))),\n        };\n\n        if content_type.subtype() != mime::FORM_DATA {\n            // this extractor only supports multipart/form-data\n            return Box::pin(ready(Err(MultipartError::ContentTypeIncompatible.into())));\n        };\n\n        let config = MultipartFormConfig::from_req(req);\n        let mut limits = Limits::new(config.total_limit, config.memory_limit);\n\n        let req = req.clone();\n        let req2 = req.clone();\n        let err_handler = config.err_handler.clone();\n\n        Box::pin(\n            async move {\n                let mut state = State::default();\n\n                // ensure limits are shared for all fields with this name\n                let mut field_limits = HashMap::<String, Option<usize>>::new();\n\n                while let Some(field) = multipart.try_next().await? {\n                    debug_assert!(\n                        !field.form_field_name.is_empty(),\n                        \"multipart form fields should have names\",\n                    );\n\n                    // Retrieve the limit for this field\n                    let entry = field_limits\n                        .entry(field.form_field_name.clone())\n                        .or_insert_with(|| T::limit(&field.form_field_name));\n\n                    limits.field_limit_remaining.clone_from(entry);\n\n                    T::handle_field(&req, field, &mut limits, &mut state).await?;\n\n                    // Update the stored limit\n                    *entry = limits.field_limit_remaining;\n                }\n\n                let inner = T::from_state(state)?;\n                Ok(MultipartForm(inner))\n            }\n            .map_err(move |err| {\n                if let Some(handler) = err_handler {\n                    (*handler)(err, &req2)\n                } else {\n                    err.into()\n                }\n            }),\n        )\n    }\n}\n\ntype MultipartFormErrorHandler =\n    Option<Arc<dyn Fn(MultipartError, &HttpRequest) -> Error + Send + Sync>>;\n\n/// [`struct@MultipartForm`] extractor configuration.\n///\n/// Add to your app data to have it picked up by [`struct@MultipartForm`] extractors.\n#[derive(Clone)]\npub struct MultipartFormConfig {\n    total_limit: usize,\n    memory_limit: usize,\n    err_handler: MultipartFormErrorHandler,\n}\n\nimpl MultipartFormConfig {\n    /// Sets maximum accepted payload size for the entire form. By default this limit is 50MiB.\n    pub fn total_limit(mut self, total_limit: usize) -> Self {\n        self.total_limit = total_limit;\n        self\n    }\n\n    /// Sets maximum accepted data that will be read into memory. By default this limit is 2MiB.\n    pub fn memory_limit(mut self, memory_limit: usize) -> Self {\n        self.memory_limit = memory_limit;\n        self\n    }\n\n    /// Sets custom error handler.\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(MultipartError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n\n    /// Extracts payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n}\n\nconst DEFAULT_CONFIG: MultipartFormConfig = MultipartFormConfig {\n    total_limit: 52_428_800, // 50 MiB\n    memory_limit: 2_097_152, // 2 MiB\n    err_handler: None,\n};\n\nimpl Default for MultipartFormConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::encoding::Decoder;\n    use actix_multipart_rfc7578::client::multipart;\n    use actix_test::TestServer;\n    use actix_web::{\n        dev::Payload, http::StatusCode, web, App, HttpRequest, HttpResponse, Resource, Responder,\n    };\n    use awc::{Client, ClientResponse};\n    use futures_core::future::LocalBoxFuture;\n    use futures_util::TryStreamExt as _;\n\n    use super::MultipartForm;\n    use crate::{\n        form::{\n            bytes::Bytes, tempfile::TempFile, text::Text, FieldReader, Limits, MultipartFormConfig,\n        },\n        Field, MultipartError,\n    };\n\n    pub async fn send_form(\n        srv: &TestServer,\n        form: multipart::Form<'static>,\n        uri: &'static str,\n    ) -> ClientResponse<Decoder<Payload>> {\n        Client::default()\n            .post(srv.url(uri))\n            .content_type(form.content_type())\n            .send_body(multipart::Body::from(form))\n            .await\n            .unwrap()\n    }\n\n    /// Test `Option` fields.\n    #[derive(MultipartForm)]\n    struct TestOptions {\n        field1: Option<Text<String>>,\n        field2: Option<Text<String>>,\n    }\n\n    async fn test_options_route(form: MultipartForm<TestOptions>) -> impl Responder {\n        assert!(form.field1.is_some());\n        assert!(form.field2.is_none());\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_options() {\n        let srv = actix_test::start(|| App::new().route(\"/\", web::post().to(test_options_route)));\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"field1\", \"value\");\n\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test `Vec` fields.\n    #[derive(MultipartForm)]\n    struct TestVec {\n        list1: Vec<Text<String>>,\n        list2: Vec<Text<String>>,\n    }\n\n    async fn test_vec_route(form: MultipartForm<TestVec>) -> impl Responder {\n        let form = form.into_inner();\n        let strings = form\n            .list1\n            .into_iter()\n            .map(|s| s.into_inner())\n            .collect::<Vec<_>>();\n        assert_eq!(strings, vec![\"value1\", \"value2\", \"value3\"]);\n        assert_eq!(form.list2.len(), 0);\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_vec() {\n        let srv = actix_test::start(|| App::new().route(\"/\", web::post().to(test_vec_route)));\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"list1\", \"value1\");\n        form.add_text(\"list1\", \"value2\");\n        form.add_text(\"list1\", \"value3\");\n\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test `Option<Vec>` fields.\n    #[derive(MultipartForm)]\n    struct TestOptionVec {\n        list1: Option<Vec<Text<String>>>,\n        list2: Option<Vec<Text<String>>>,\n    }\n\n    async fn test_option_vec_route(form: MultipartForm<TestOptionVec>) -> impl Responder {\n        let form = form.into_inner();\n        let strings = form\n            .list1\n            .unwrap()\n            .into_iter()\n            .map(|s| s.into_inner())\n            .collect::<Vec<_>>();\n        assert_eq!(strings, vec![\"value1\", \"value2\", \"value3\"]);\n        assert!(form.list2.is_none());\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_option_vec() {\n        let srv =\n            actix_test::start(|| App::new().route(\"/\", web::post().to(test_option_vec_route)));\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"list1\", \"value1\");\n        form.add_text(\"list1\", \"value2\");\n        form.add_text(\"list1\", \"value3\");\n\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test the `rename` field attribute.\n    #[derive(MultipartForm)]\n    struct TestFieldRenaming {\n        #[multipart(rename = \"renamed\")]\n        field1: Text<String>,\n        #[multipart(rename = \"field1\")]\n        field2: Text<String>,\n        field3: Text<String>,\n    }\n\n    async fn test_field_renaming_route(form: MultipartForm<TestFieldRenaming>) -> impl Responder {\n        assert_eq!(&*form.field1, \"renamed\");\n        assert_eq!(&*form.field2, \"field1\");\n        assert_eq!(&*form.field3, \"field3\");\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_field_renaming() {\n        let srv =\n            actix_test::start(|| App::new().route(\"/\", web::post().to(test_field_renaming_route)));\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"renamed\", \"renamed\");\n        form.add_text(\"field1\", \"field1\");\n        form.add_text(\"field3\", \"field3\");\n\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test the `deny_unknown_fields` struct attribute.\n    #[derive(MultipartForm)]\n    #[multipart(deny_unknown_fields)]\n    struct TestDenyUnknown {}\n\n    #[derive(MultipartForm)]\n    struct TestAllowUnknown {}\n\n    async fn test_deny_unknown_route(_: MultipartForm<TestDenyUnknown>) -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    async fn test_allow_unknown_route(_: MultipartForm<TestAllowUnknown>) -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_deny_unknown() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/deny\", web::post().to(test_deny_unknown_route))\n                .route(\"/allow\", web::post().to(test_allow_unknown_route))\n        });\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"unknown\", \"value\");\n        let response = send_form(&srv, form, \"/deny\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"unknown\", \"value\");\n        let response = send_form(&srv, form, \"/allow\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test the `duplicate_field` struct attribute.\n    #[derive(MultipartForm)]\n    #[multipart(duplicate_field = \"deny\")]\n    struct TestDuplicateDeny {\n        _field: Text<String>,\n    }\n\n    #[derive(MultipartForm)]\n    #[multipart(duplicate_field = \"replace\")]\n    struct TestDuplicateReplace {\n        field: Text<String>,\n    }\n\n    #[derive(MultipartForm)]\n    #[multipart(duplicate_field = \"ignore\")]\n    struct TestDuplicateIgnore {\n        field: Text<String>,\n    }\n\n    async fn test_duplicate_deny_route(_: MultipartForm<TestDuplicateDeny>) -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    async fn test_duplicate_replace_route(\n        form: MultipartForm<TestDuplicateReplace>,\n    ) -> impl Responder {\n        assert_eq!(&*form.field, \"second_value\");\n        HttpResponse::Ok().finish()\n    }\n\n    async fn test_duplicate_ignore_route(\n        form: MultipartForm<TestDuplicateIgnore>,\n    ) -> impl Responder {\n        assert_eq!(&*form.field, \"first_value\");\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_duplicate_field() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/deny\", web::post().to(test_duplicate_deny_route))\n                .route(\"/replace\", web::post().to(test_duplicate_replace_route))\n                .route(\"/ignore\", web::post().to(test_duplicate_ignore_route))\n        });\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"_field\", \"first_value\");\n        form.add_text(\"_field\", \"second_value\");\n        let response = send_form(&srv, form, \"/deny\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"first_value\");\n        form.add_text(\"field\", \"second_value\");\n        let response = send_form(&srv, form, \"/replace\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"first_value\");\n        form.add_text(\"field\", \"second_value\");\n        let response = send_form(&srv, form, \"/ignore\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    /// Test the Limits.\n    #[derive(MultipartForm)]\n    struct TestMemoryUploadLimits {\n        field: Bytes,\n    }\n\n    #[derive(MultipartForm)]\n    struct TestFileUploadLimits {\n        field: TempFile,\n    }\n\n    async fn test_upload_limits_memory(\n        form: MultipartForm<TestMemoryUploadLimits>,\n    ) -> impl Responder {\n        assert!(!form.field.data.is_empty());\n        HttpResponse::Ok().finish()\n    }\n\n    async fn test_upload_limits_file(form: MultipartForm<TestFileUploadLimits>) -> impl Responder {\n        assert!(form.field.size > 0);\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_memory_limits() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/text\", web::post().to(test_upload_limits_memory))\n                .route(\"/file\", web::post().to(test_upload_limits_file))\n                .app_data(\n                    MultipartFormConfig::default()\n                        .memory_limit(20)\n                        .total_limit(usize::MAX),\n                )\n        });\n\n        // Exceeds the 20 byte memory limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/text\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        // Memory limit should not apply when the data is being streamed to disk\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/file\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_total_limit() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/text\", web::post().to(test_upload_limits_memory))\n                .route(\"/file\", web::post().to(test_upload_limits_file))\n                .app_data(\n                    MultipartFormConfig::default()\n                        .memory_limit(usize::MAX)\n                        .total_limit(20),\n                )\n        });\n\n        // Within the 20 byte limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"7 bytes\");\n        let response = send_form(&srv, form, \"/text\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        // Exceeds the 20 byte overall limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/text\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        // Exceeds the 20 byte overall limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/file\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[derive(MultipartForm)]\n    struct TestFieldLevelLimits {\n        #[multipart(limit = \"30B\")]\n        field: Vec<Bytes>,\n    }\n\n    async fn test_field_level_limits_route(\n        form: MultipartForm<TestFieldLevelLimits>,\n    ) -> impl Responder {\n        assert!(!form.field.is_empty());\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_field_level_limits() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/\", web::post().to(test_field_level_limits_route))\n                .app_data(\n                    MultipartFormConfig::default()\n                        .memory_limit(usize::MAX)\n                        .total_limit(usize::MAX),\n                )\n        });\n\n        // Within the 30 byte limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        // Exceeds the the 30 byte limit\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is more than 30 bytes long\");\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        // Total of values (14 bytes) is within 30 byte limit for \"field\"\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"7 bytes\");\n        form.add_text(\"field\", \"7 bytes\");\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n\n        // Total of values exceeds 30 byte limit for \"field\"\n        let mut form = multipart::Form::default();\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        form.add_text(\"field\", \"this string is 28 bytes long\");\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[actix_rt::test]\n    async fn non_multipart_form_data() {\n        #[derive(MultipartForm)]\n        struct TestNonMultipartFormData {\n            #[allow(unused)]\n            #[multipart(limit = \"30B\")]\n            foo: Text<String>,\n        }\n\n        async fn non_multipart_form_data_route(\n            _form: MultipartForm<TestNonMultipartFormData>,\n        ) -> String {\n            unreachable!(\"request is sent with multipart/mixed\");\n        }\n\n        let srv = actix_test::start(|| {\n            App::new().route(\"/\", web::post().to(non_multipart_form_data_route))\n        });\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"foo\", \"foo\");\n\n        // mangle content-type, keeping the boundary\n        let ct = form.content_type().replacen(\"/form-data\", \"/mixed\", 1);\n\n        let res = Client::default()\n            .post(srv.url(\"/\"))\n            .content_type(ct)\n            .send_body(multipart::Body::from(form))\n            .await\n            .unwrap();\n\n        assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);\n    }\n\n    #[should_panic(expected = \"called `Result::unwrap()` on an `Err` value: Connect(Disconnected)\")]\n    #[actix_web::test]\n    async fn field_try_next_panic() {\n        #[derive(Debug)]\n        struct NullSink;\n\n        impl<'t> FieldReader<'t> for NullSink {\n            type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;\n\n            fn read_field(\n                _: &'t HttpRequest,\n                mut field: Field,\n                _limits: &'t mut Limits,\n            ) -> Self::Future {\n                Box::pin(async move {\n                    // exhaust field stream\n                    while let Some(_chunk) = field.try_next().await? {}\n\n                    // poll again, crash\n                    let _post = field.try_next().await;\n\n                    Ok(Self)\n                })\n            }\n        }\n\n        #[allow(dead_code)]\n        #[derive(MultipartForm)]\n        struct NullSinkForm {\n            foo: NullSink,\n        }\n\n        async fn null_sink(_form: MultipartForm<NullSinkForm>) -> impl Responder {\n            \"unreachable\"\n        }\n\n        let srv = actix_test::start(|| App::new().service(Resource::new(\"/\").post(null_sink)));\n\n        let mut form = multipart::Form::default();\n        form.add_text(\"foo\", \"data is not important to this test\");\n\n        // panics with Err(Connect(Disconnected)) due to form NullSink panic\n        let _res = send_form(&srv, form, \"/\").await;\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/form/tempfile.rs",
    "content": "//! Writes a field to a temporary file on disk.\n\nuse std::{\n    io,\n    path::{Path, PathBuf},\n    sync::Arc,\n};\n\nuse actix_web::{http::StatusCode, web, Error, HttpRequest, ResponseError};\nuse derive_more::{Display, Error};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::TryStreamExt as _;\nuse mime::Mime;\nuse tempfile::NamedTempFile;\nuse tokio::io::AsyncWriteExt;\n\nuse super::FieldErrorHandler;\nuse crate::{\n    form::{FieldReader, Limits},\n    Field, MultipartError,\n};\n\n/// Write the field to a temporary file on disk.\n#[derive(Debug)]\npub struct TempFile {\n    /// The temporary file on disk.\n    pub file: NamedTempFile,\n\n    /// The value of the `content-type` header.\n    pub content_type: Option<Mime>,\n\n    /// The `filename` value in the `content-disposition` header.\n    pub file_name: Option<String>,\n\n    /// The size in bytes of the file.\n    pub size: usize,\n}\n\nimpl<'t> FieldReader<'t> for TempFile {\n    type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;\n\n    fn read_field(req: &'t HttpRequest, mut field: Field, limits: &'t mut Limits) -> Self::Future {\n        Box::pin(async move {\n            let config = TempFileConfig::from_req(req);\n            let mut size = 0;\n\n            let file = config.create_tempfile().map_err(|err| {\n                config.map_error(req, &field.form_field_name, TempFileError::FileIo(err))\n            })?;\n\n            let mut file_async = tokio::fs::File::from_std(file.reopen().map_err(|err| {\n                config.map_error(req, &field.form_field_name, TempFileError::FileIo(err))\n            })?);\n\n            while let Some(chunk) = field.try_next().await? {\n                limits.try_consume_limits(chunk.len(), false)?;\n                size += chunk.len();\n                file_async.write_all(chunk.as_ref()).await.map_err(|err| {\n                    config.map_error(req, &field.form_field_name, TempFileError::FileIo(err))\n                })?;\n            }\n\n            file_async.flush().await.map_err(|err| {\n                config.map_error(req, &field.form_field_name, TempFileError::FileIo(err))\n            })?;\n\n            Ok(TempFile {\n                file,\n                content_type: field.content_type().map(ToOwned::to_owned),\n                file_name: field\n                    .content_disposition()\n                    .expect(\"multipart form fields should have a content-disposition header\")\n                    .get_filename()\n                    .map(ToOwned::to_owned),\n                size,\n            })\n        })\n    }\n}\n\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum TempFileError {\n    /// File I/O Error\n    #[display(\"File I/O error: {}\", _0)]\n    FileIo(std::io::Error),\n}\n\nimpl ResponseError for TempFileError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::INTERNAL_SERVER_ERROR\n    }\n}\n\n/// Configuration for the [`TempFile`] field reader.\n#[derive(Clone)]\npub struct TempFileConfig {\n    err_handler: FieldErrorHandler<TempFileError>,\n    directory: Option<PathBuf>,\n}\n\nimpl TempFileConfig {\n    fn create_tempfile(&self) -> io::Result<NamedTempFile> {\n        if let Some(ref dir) = self.directory {\n            NamedTempFile::new_in(dir)\n        } else {\n            NamedTempFile::new()\n        }\n    }\n}\n\nimpl TempFileConfig {\n    /// Sets custom error handler.\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(TempFileError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n\n    /// Extracts payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n\n    fn map_error(&self, req: &HttpRequest, field_name: &str, err: TempFileError) -> MultipartError {\n        let source = if let Some(ref err_handler) = self.err_handler {\n            (err_handler)(err, req)\n        } else {\n            err.into()\n        };\n\n        MultipartError::Field {\n            name: field_name.to_owned(),\n            source,\n        }\n    }\n\n    /// Sets the directory that temp files will be created in.\n    ///\n    /// The default temporary file location is platform dependent.\n    pub fn directory(mut self, dir: impl AsRef<Path>) -> Self {\n        self.directory = Some(dir.as_ref().to_owned());\n        self\n    }\n}\n\nconst DEFAULT_CONFIG: TempFileConfig = TempFileConfig {\n    err_handler: None,\n    directory: None,\n};\n\nimpl Default for TempFileConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io::{Cursor, Read};\n\n    use actix_multipart_rfc7578::client::multipart;\n    use actix_web::{http::StatusCode, web, App, HttpResponse, Responder};\n\n    use crate::form::{tempfile::TempFile, tests::send_form, MultipartForm};\n\n    #[derive(MultipartForm)]\n    struct FileForm {\n        file: TempFile,\n    }\n\n    async fn test_file_route(form: MultipartForm<FileForm>) -> impl Responder {\n        let mut form = form.into_inner();\n        let mut contents = String::new();\n        form.file.file.read_to_string(&mut contents).unwrap();\n        assert_eq!(contents, \"Hello, world!\");\n        assert_eq!(form.file.file_name.unwrap(), \"testfile.txt\");\n        assert_eq!(form.file.content_type.unwrap(), mime::TEXT_PLAIN);\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_file_upload() {\n        let srv = actix_test::start(|| App::new().route(\"/\", web::post().to(test_file_route)));\n\n        let mut form = multipart::Form::default();\n        let bytes = Cursor::new(\"Hello, world!\");\n        form.add_reader_file_with_mime(\"file\", bytes, \"testfile.txt\", mime::TEXT_PLAIN);\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/form/text.rs",
    "content": "//! Deserializes a field from plain text.\n\nuse std::{str, sync::Arc};\n\nuse actix_web::{http::StatusCode, web, Error, HttpRequest, ResponseError};\nuse derive_more::{Deref, DerefMut, Display, Error};\nuse futures_core::future::LocalBoxFuture;\nuse serde::de::DeserializeOwned;\n\nuse super::FieldErrorHandler;\nuse crate::{\n    form::{bytes::Bytes, FieldReader, Limits},\n    Field, MultipartError,\n};\n\n/// Deserialize from plain text.\n///\n/// Internally this uses [`serde_plain`] for deserialization, which supports primitive types\n/// including strings, numbers, and simple enums.\n#[derive(Debug, Deref, DerefMut)]\npub struct Text<T: DeserializeOwned>(pub T);\n\nimpl<T: DeserializeOwned> Text<T> {\n    /// Unwraps into inner value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<'t, T> FieldReader<'t> for Text<T>\nwhere\n    T: DeserializeOwned + 'static,\n{\n    type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;\n\n    fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future {\n        Box::pin(async move {\n            let config = TextConfig::from_req(req);\n\n            if config.validate_content_type {\n                let valid = if let Some(mime) = field.content_type() {\n                    mime.subtype() == mime::PLAIN || mime.suffix() == Some(mime::PLAIN)\n                } else {\n                    // https://datatracker.ietf.org/doc/html/rfc7578#section-4.4\n                    // content type defaults to text/plain, so None should be considered valid\n                    true\n                };\n\n                if !valid {\n                    return Err(MultipartError::Field {\n                        name: field.form_field_name,\n                        source: config.map_error(req, TextError::ContentType),\n                    });\n                }\n            }\n\n            let form_field_name = field.form_field_name.clone();\n\n            let bytes = Bytes::read_field(req, field, limits).await?;\n\n            let text = str::from_utf8(&bytes.data).map_err(|err| MultipartError::Field {\n                name: form_field_name.clone(),\n                source: config.map_error(req, TextError::Utf8Error(err)),\n            })?;\n\n            Ok(Text(serde_plain::from_str(text).map_err(|err| {\n                MultipartError::Field {\n                    name: form_field_name,\n                    source: config.map_error(req, TextError::Deserialize(err)),\n                }\n            })?))\n        })\n    }\n}\n\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum TextError {\n    /// UTF-8 decoding error.\n    #[display(\"UTF-8 decoding error: {}\", _0)]\n    Utf8Error(str::Utf8Error),\n\n    /// Deserialize error.\n    #[display(\"Plain text deserialize error: {}\", _0)]\n    Deserialize(serde_plain::Error),\n\n    /// Content type error.\n    #[display(\"Content type error\")]\n    ContentType,\n}\n\nimpl ResponseError for TextError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\n/// Configuration for the [`Text`] field reader.\n#[derive(Clone)]\npub struct TextConfig {\n    err_handler: FieldErrorHandler<TextError>,\n    validate_content_type: bool,\n}\n\nimpl TextConfig {\n    /// Sets custom error handler.\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(TextError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n\n    /// Extracts payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n\n    fn map_error(&self, req: &HttpRequest, err: TextError) -> Error {\n        if let Some(ref err_handler) = self.err_handler {\n            (err_handler)(err, req)\n        } else {\n            err.into()\n        }\n    }\n\n    /// Sets whether or not the field must have a valid `Content-Type` header to be parsed.\n    ///\n    /// Note that an empty `Content-Type` is also accepted, as the multipart specification defines\n    /// `text/plain` as the default for text fields.\n    pub fn validate_content_type(mut self, validate_content_type: bool) -> Self {\n        self.validate_content_type = validate_content_type;\n        self\n    }\n}\n\nconst DEFAULT_CONFIG: TextConfig = TextConfig {\n    err_handler: None,\n    validate_content_type: true,\n};\n\nimpl Default for TextConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io::Cursor;\n\n    use actix_multipart_rfc7578::client::multipart;\n    use actix_web::{http::StatusCode, web, App, HttpResponse, Responder};\n\n    use crate::form::{\n        tests::send_form,\n        text::{Text, TextConfig},\n        MultipartForm,\n    };\n\n    #[derive(MultipartForm)]\n    struct TextForm {\n        number: Text<i32>,\n    }\n\n    async fn test_text_route(form: MultipartForm<TextForm>) -> impl Responder {\n        assert_eq!(*form.number, 1025);\n        HttpResponse::Ok().finish()\n    }\n\n    #[actix_rt::test]\n    async fn test_content_type_validation() {\n        let srv = actix_test::start(|| {\n            App::new()\n                .route(\"/\", web::post().to(test_text_route))\n                .app_data(TextConfig::default().validate_content_type(true))\n        });\n\n        // Deny because wrong content type\n        let bytes = Cursor::new(\"1025\");\n        let mut form = multipart::Form::default();\n        form.add_reader_file_with_mime(\"number\", bytes, \"\", mime::APPLICATION_OCTET_STREAM);\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n\n        // Allow because correct content type\n        let bytes = Cursor::new(\"1025\");\n        let mut form = multipart::Form::default();\n        form.add_reader_file_with_mime(\"number\", bytes, \"\", mime::TEXT_PLAIN);\n        let response = send_form(&srv, form, \"/\").await;\n        assert_eq!(response.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/lib.rs",
    "content": "//! Multipart request & form support for Actix Web.\n//!\n//! The [`Multipart`] extractor aims to support all kinds of `multipart/*` requests, including\n//! `multipart/form-data`, `multipart/related` and `multipart/mixed`. This is a lower-level\n//! extractor which supports reading [multipart fields](Field), in the order they are sent by the\n//! client.\n//!\n//! Due to additional requirements for `multipart/form-data` requests, the higher level\n//! [`MultipartForm`] extractor and derive macro only supports this media type.\n//!\n//! # Examples\n//!\n//! ```no_run\n//! use actix_web::{post, App, HttpServer, Responder};\n//!\n//! use actix_multipart::form::{json::Json as MpJson, tempfile::TempFile, MultipartForm, MultipartFormConfig};\n//! use serde::Deserialize;\n//!\n//! #[derive(Debug, Deserialize)]\n//! struct Metadata {\n//!     name: String,\n//! }\n//!\n//! #[derive(Debug, MultipartForm)]\n//! struct UploadForm {\n//!     // Note: the form is also subject to the global limits configured using `MultipartFormConfig`.\n//!     #[multipart(limit = \"100MB\")]\n//!     file: TempFile,\n//!     json: MpJson<Metadata>,\n//! }\n//!\n//! #[post(\"/videos\")]\n//! pub async fn post_video(MultipartForm(form): MultipartForm<UploadForm>) -> impl Responder {\n//!     format!(\n//!         \"Uploaded file {}, with size: {}\",\n//!         form.json.name, form.file.size\n//!     )\n//! }\n//!\n//! #[actix_web::main]\n//! async fn main() -> std::io::Result<()> {\n//!     HttpServer::new(move || {\n//!         App::new()\n//!             .service(post_video)\n//!             // Also increase the global total limit to 100MiB.\n//!             .app_data(MultipartFormConfig::default().total_limit(100 * 1024 * 1024))\n//!     })\n//!     .bind((\"127.0.0.1\", 8080))?\n//!     .run()\n//!     .await\n//! }\n//! ```\n//!\n//! cURL request:\n//!\n//! ```sh\n//! curl -v --request POST \\\n//!   --url http://localhost:8080/videos \\\n//!   -F 'json={\"name\": \"Cargo.lock\"};type=application/json' \\\n//!   -F file=@./Cargo.lock\n//! ```\n//!\n//! [`MultipartForm`]: struct@form::MultipartForm\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\n// This allows us to use the actix_multipart_derive within this crate's tests\n#[cfg(test)]\nextern crate self as actix_multipart;\n\nmod error;\nmod extractor;\npub(crate) mod field;\npub mod form;\nmod multipart;\npub(crate) mod payload;\npub(crate) mod safety;\npub mod test;\n\npub use self::{\n    error::Error as MultipartError,\n    field::{Field, LimitExceeded},\n    multipart::Multipart,\n};\n"
  },
  {
    "path": "actix-multipart/src/multipart.rs",
    "content": "//! Multipart response payload support.\n\nuse std::{\n    cell::RefCell,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_web::{\n    dev,\n    error::{ParseError, PayloadError},\n    http::header::{self, ContentDisposition, HeaderMap, HeaderName, HeaderValue},\n    web::Bytes,\n    HttpRequest,\n};\nuse futures_core::stream::Stream;\nuse mime::Mime;\n\nuse crate::{\n    error::Error,\n    field::InnerField,\n    payload::{PayloadBuffer, PayloadRef},\n    safety::Safety,\n    Field,\n};\n\nconst MAX_HEADERS: usize = 32;\n\n/// The server-side implementation of `multipart/form-data` requests.\n///\n/// This will parse the incoming stream into `MultipartItem` instances via its `Stream`\n/// implementation. `MultipartItem::Field` contains multipart field. `MultipartItem::Multipart` is\n/// used for nested multipart streams.\npub struct Multipart {\n    flow: Flow,\n    safety: Safety,\n}\n\nenum Flow {\n    InFlight(Inner),\n\n    /// Error container is Some until an error is returned out of the flow.\n    Error(Option<Error>),\n}\n\nimpl Multipart {\n    /// Creates multipart instance from parts.\n    pub fn new<S>(headers: &HeaderMap, stream: S) -> Self\n    where\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        match Self::find_ct_and_boundary(headers) {\n            Ok((ct, boundary)) => Self::from_ct_and_boundary(ct, boundary, stream),\n            Err(err) => Self::from_error(err),\n        }\n    }\n\n    /// Creates multipart instance from parts.\n    pub(crate) fn from_req(req: &HttpRequest, payload: &mut dev::Payload) -> Self {\n        match Self::find_ct_and_boundary(req.headers()) {\n            Ok((ct, boundary)) => Self::from_ct_and_boundary(ct, boundary, payload.take()),\n            Err(err) => Self::from_error(err),\n        }\n    }\n\n    /// Extract Content-Type and boundary info from headers.\n    pub(crate) fn find_ct_and_boundary(headers: &HeaderMap) -> Result<(Mime, String), Error> {\n        let content_type = headers\n            .get(&header::CONTENT_TYPE)\n            .ok_or(Error::ContentTypeMissing)?\n            .to_str()\n            .ok()\n            .and_then(|content_type| content_type.parse::<Mime>().ok())\n            .ok_or(Error::ContentTypeParse)?;\n\n        if content_type.type_() != mime::MULTIPART {\n            return Err(Error::ContentTypeIncompatible);\n        }\n\n        let boundary = content_type\n            .get_param(mime::BOUNDARY)\n            .ok_or(Error::BoundaryMissing)?\n            .as_str()\n            .to_owned();\n\n        Ok((content_type, boundary))\n    }\n\n    /// Constructs a new multipart reader from given Content-Type, boundary, and stream.\n    pub(crate) fn from_ct_and_boundary<S>(ct: Mime, boundary: String, stream: S) -> Multipart\n    where\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        Multipart {\n            safety: Safety::new(),\n            flow: Flow::InFlight(Inner {\n                payload: PayloadRef::new(PayloadBuffer::new(stream)),\n                content_type: ct,\n                boundary,\n                state: State::FirstBoundary,\n                item: Item::None,\n            }),\n        }\n    }\n\n    /// Constructs a new multipart reader from given `MultipartError`.\n    pub(crate) fn from_error(err: Error) -> Multipart {\n        Multipart {\n            flow: Flow::Error(Some(err)),\n            safety: Safety::new(),\n        }\n    }\n\n    /// Return requests parsed Content-Type or raise the stored error.\n    pub(crate) fn content_type_or_bail(&mut self) -> Result<mime::Mime, Error> {\n        match self.flow {\n            Flow::InFlight(ref inner) => Ok(inner.content_type.clone()),\n            Flow::Error(ref mut err) => Err(err\n                .take()\n                .expect(\"error should not be taken after it was returned\")),\n        }\n    }\n}\n\nimpl Stream for Multipart {\n    type Item = Result<Field, Error>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.get_mut();\n\n        match this.flow {\n            Flow::InFlight(ref mut inner) => {\n                if let Some(mut buffer) = inner.payload.get_mut(&this.safety) {\n                    // check safety and poll read payload to buffer.\n                    buffer.poll_stream(cx)?;\n                } else if !this.safety.is_clean() {\n                    // safety violation\n                    return Poll::Ready(Some(Err(Error::NotConsumed)));\n                } else {\n                    return Poll::Pending;\n                }\n\n                inner.poll(&this.safety, cx)\n            }\n\n            Flow::Error(ref mut err) => Poll::Ready(Some(Err(err\n                .take()\n                .expect(\"Multipart polled after finish\")))),\n        }\n    }\n}\n\n#[derive(PartialEq, Debug)]\nenum State {\n    /// Skip data until first boundary.\n    FirstBoundary,\n\n    /// Reading boundary.\n    Boundary,\n\n    /// Reading Headers.\n    Headers,\n\n    /// Stream EOF.\n    Eof,\n}\n\nenum Item {\n    None,\n    Field(Rc<RefCell<InnerField>>),\n}\n\nstruct Inner {\n    /// Request's payload stream & buffer.\n    payload: PayloadRef,\n\n    /// Request's Content-Type.\n    ///\n    /// Guaranteed to have \"multipart\" top-level media type, i.e., `multipart/*`.\n    content_type: Mime,\n\n    /// Field boundary.\n    boundary: String,\n\n    state: State,\n    item: Item,\n}\n\nimpl Inner {\n    fn read_field_headers(payload: &mut PayloadBuffer) -> Result<Option<HeaderMap>, Error> {\n        match payload.read_until(b\"\\r\\n\\r\\n\")? {\n            None => {\n                if payload.eof {\n                    Err(Error::Incomplete)\n                } else {\n                    Ok(None)\n                }\n            }\n\n            Some(bytes) => {\n                let mut hdrs = [httparse::EMPTY_HEADER; MAX_HEADERS];\n\n                match httparse::parse_headers(&bytes, &mut hdrs).map_err(ParseError::from)? {\n                    httparse::Status::Complete((_, hdrs)) => {\n                        // convert headers\n                        let mut headers = HeaderMap::with_capacity(hdrs.len());\n\n                        for h in hdrs {\n                            let name =\n                                HeaderName::try_from(h.name).map_err(|_| ParseError::Header)?;\n                            let value =\n                                HeaderValue::try_from(h.value).map_err(|_| ParseError::Header)?;\n                            headers.append(name, value);\n                        }\n\n                        Ok(Some(headers))\n                    }\n\n                    httparse::Status::Partial => Err(ParseError::Header.into()),\n                }\n            }\n        }\n    }\n\n    /// Reads a field boundary from the payload buffer (and discards it).\n    ///\n    /// Reads \"in-between\" and \"final\" boundaries. E.g. for boundary = \"foo\":\n    ///\n    /// ```plain\n    /// --foo    <-- in-between fields\n    /// --foo--  <-- end of request body, should be followed by EOF\n    /// ```\n    ///\n    /// Returns:\n    ///\n    /// - `Ok(Some(true))` - final field boundary read (EOF)\n    /// - `Ok(Some(false))` - field boundary read\n    /// - `Ok(None)` - boundary not found, more data needs reading\n    /// - `Err(BoundaryMissing)` - multipart boundary is missing\n    fn read_boundary(payload: &mut PayloadBuffer, boundary: &str) -> Result<Option<bool>, Error> {\n        // TODO: need to read epilogue\n        let chunk = match payload.readline_or_eof()? {\n            // TODO: this might be okay as a let Some() else return Ok(None)\n            None => return Ok(payload.eof.then_some(true)),\n            Some(chunk) => chunk,\n        };\n\n        const BOUNDARY_MARKER: &[u8] = b\"--\";\n        const LINE_BREAK: &[u8] = b\"\\r\\n\";\n\n        let boundary_len = boundary.len();\n\n        if chunk.len() < boundary_len + 2 + 2\n            || !chunk.starts_with(BOUNDARY_MARKER)\n            || &chunk[2..boundary_len + 2] != boundary.as_bytes()\n        {\n            return Err(Error::BoundaryMissing);\n        }\n\n        // chunk facts:\n        // - long enough to contain boundary + 2 markers or 1 marker and line-break\n        // - starts with boundary marker\n        // - chunk contains correct boundary\n\n        if &chunk[boundary_len + 2..] == LINE_BREAK {\n            // boundary is followed by line-break, indicating more fields to come\n            return Ok(Some(false));\n        }\n\n        // boundary is followed by marker\n        if &chunk[boundary_len + 2..boundary_len + 4] == BOUNDARY_MARKER\n            && (\n                // chunk is exactly boundary len + 2 markers\n                chunk.len() == boundary_len + 2 + 2\n                // final boundary is allowed to end with a line-break\n                || &chunk[boundary_len + 4..] == LINE_BREAK\n            )\n        {\n            return Ok(Some(true));\n        }\n\n        Err(Error::BoundaryMissing)\n    }\n\n    fn skip_until_boundary(\n        payload: &mut PayloadBuffer,\n        boundary: &str,\n    ) -> Result<Option<bool>, Error> {\n        let mut eof = false;\n\n        loop {\n            match payload.readline()? {\n                Some(chunk) => {\n                    if chunk.is_empty() {\n                        return Err(Error::BoundaryMissing);\n                    }\n                    if chunk.len() < boundary.len() {\n                        continue;\n                    }\n                    if &chunk[..2] == b\"--\" && &chunk[2..chunk.len() - 2] == boundary.as_bytes() {\n                        break;\n                    } else {\n                        if chunk.len() < boundary.len() + 2 {\n                            continue;\n                        }\n                        let b: &[u8] = boundary.as_ref();\n                        if &chunk[..boundary.len()] == b\n                            && &chunk[boundary.len()..boundary.len() + 2] == b\"--\"\n                        {\n                            eof = true;\n                            break;\n                        }\n                    }\n                }\n                None => {\n                    return if payload.eof {\n                        Err(Error::Incomplete)\n                    } else {\n                        Ok(None)\n                    };\n                }\n            }\n        }\n        Ok(Some(eof))\n    }\n\n    fn poll(&mut self, safety: &Safety, cx: &Context<'_>) -> Poll<Option<Result<Field, Error>>> {\n        if self.state == State::Eof {\n            Poll::Ready(None)\n        } else {\n            // release field\n            loop {\n                // Nested multipart streams of fields has to be consumed\n                // before switching to next\n                if safety.current() {\n                    let stop = match self.item {\n                        Item::Field(ref mut field) => match field.borrow_mut().poll(safety) {\n                            Poll::Pending => return Poll::Pending,\n                            Poll::Ready(Some(Ok(_))) => continue,\n                            Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err))),\n                            Poll::Ready(None) => true,\n                        },\n                        Item::None => false,\n                    };\n                    if stop {\n                        self.item = Item::None;\n                    }\n                    if let Item::None = self.item {\n                        break;\n                    }\n                }\n            }\n\n            let field_headers = if let Some(mut payload) = self.payload.get_mut(safety) {\n                match self.state {\n                    // read until first boundary\n                    State::FirstBoundary => {\n                        match Inner::skip_until_boundary(&mut payload, &self.boundary)? {\n                            None => return Poll::Pending,\n                            Some(eof) => {\n                                if eof {\n                                    self.state = State::Eof;\n                                    return Poll::Ready(None);\n                                } else {\n                                    self.state = State::Headers;\n                                }\n                            }\n                        }\n                    }\n\n                    // read boundary\n                    State::Boundary => match Inner::read_boundary(&mut payload, &self.boundary)? {\n                        None => return Poll::Pending,\n                        Some(eof) => {\n                            if eof {\n                                self.state = State::Eof;\n                                return Poll::Ready(None);\n                            } else {\n                                self.state = State::Headers;\n                            }\n                        }\n                    },\n\n                    _ => {}\n                }\n\n                // read field headers for next field\n                if self.state == State::Headers {\n                    if let Some(headers) = Inner::read_field_headers(&mut payload)? {\n                        self.state = State::Boundary;\n                        headers\n                    } else {\n                        return Poll::Pending;\n                    }\n                } else {\n                    unreachable!()\n                }\n            } else {\n                log::debug!(\"NotReady: field is in flight\");\n                return Poll::Pending;\n            };\n\n            let field_content_disposition = field_headers\n                .get(&header::CONTENT_DISPOSITION)\n                .and_then(|cd| ContentDisposition::from_raw(cd).ok())\n                .filter(|content_disposition| {\n                    matches!(\n                        content_disposition.disposition,\n                        header::DispositionType::FormData,\n                    )\n                });\n\n            let form_field_name = if self.content_type.subtype() == mime::FORM_DATA {\n                // According to RFC 7578 §4.2, which relates to \"multipart/form-data\" requests\n                // specifically, fields must have a Content-Disposition header, its disposition\n                // type must be set as \"form-data\", and it must have a name parameter.\n\n                let Some(cd) = &field_content_disposition else {\n                    return Poll::Ready(Some(Err(Error::ContentDispositionMissing)));\n                };\n\n                let Some(field_name) = cd.get_name() else {\n                    return Poll::Ready(Some(Err(Error::ContentDispositionNameMissing)));\n                };\n\n                Some(field_name.to_owned())\n            } else {\n                None\n            };\n\n            // TODO: check out other multipart/* RFCs for specific requirements\n\n            let field_content_type: Option<Mime> = field_headers\n                .get(&header::CONTENT_TYPE)\n                .and_then(|ct| ct.to_str().ok())\n                .and_then(|ct| ct.parse().ok());\n\n            self.state = State::Boundary;\n\n            // nested multipart stream is not supported\n            if let Some(mime) = &field_content_type {\n                if mime.type_() == mime::MULTIPART {\n                    return Poll::Ready(Some(Err(Error::Nested)));\n                }\n            }\n\n            let field_inner =\n                InnerField::new_in_rc(self.payload.clone(), self.boundary.clone(), &field_headers)?;\n\n            self.item = Item::Field(Rc::clone(&field_inner));\n\n            Poll::Ready(Some(Ok(Field::new(\n                field_content_type,\n                field_content_disposition,\n                form_field_name,\n                field_headers,\n                safety.clone(cx),\n                field_inner,\n            ))))\n        }\n    }\n}\n\nimpl Drop for Inner {\n    fn drop(&mut self) {\n        // InnerMultipartItem::Field has to be dropped first because of Safety.\n        self.item = Item::None;\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use actix_http::h1;\n    use actix_web::{\n        http::header::{DispositionParam, DispositionType},\n        rt,\n        test::TestRequest,\n        web::{BufMut as _, BytesMut},\n        FromRequest,\n    };\n    use assert_matches::assert_matches;\n    use futures_test::stream::StreamTestExt as _;\n    use futures_util::{stream, StreamExt as _};\n    use tokio::sync::mpsc;\n    use tokio_stream::wrappers::UnboundedReceiverStream;\n\n    use super::*;\n\n    const BOUNDARY: &str = \"abbc761f78ff4d7cb7573b5a23f96ef0\";\n\n    #[actix_rt::test]\n    async fn test_boundary() {\n        let headers = HeaderMap::new();\n        match Multipart::find_ct_and_boundary(&headers) {\n            Err(Error::ContentTypeMissing) => {}\n            _ => unreachable!(\"should not happen\"),\n        }\n\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\"test\"),\n        );\n\n        match Multipart::find_ct_and_boundary(&headers) {\n            Err(Error::ContentTypeParse) => {}\n            _ => unreachable!(\"should not happen\"),\n        }\n\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\"multipart/mixed\"),\n        );\n        match Multipart::find_ct_and_boundary(&headers) {\n            Err(Error::BoundaryMissing) => {}\n            _ => unreachable!(\"should not happen\"),\n        }\n\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/mixed; boundary=\\\"5c02368e880e436dab70ed54e1c58209\\\"\",\n            ),\n        );\n\n        assert_eq!(\n            Multipart::find_ct_and_boundary(&headers).unwrap().1,\n            \"5c02368e880e436dab70ed54e1c58209\",\n        );\n    }\n\n    fn create_stream() -> (\n        mpsc::UnboundedSender<Result<Bytes, PayloadError>>,\n        impl Stream<Item = Result<Bytes, PayloadError>>,\n    ) {\n        let (tx, rx) = mpsc::unbounded_channel();\n\n        (\n            tx,\n            UnboundedReceiverStream::new(rx).map(|res| res.map_err(|_| panic!())),\n        )\n    }\n\n    fn create_simple_request_with_header() -> (Bytes, HeaderMap) {\n        let (body, headers) = crate::test::create_form_data_payload_and_headers_with_boundary(\n            BOUNDARY,\n            \"file\",\n            Some(\"fn.txt\".to_owned()),\n            Some(mime::TEXT_PLAIN_UTF_8),\n            Bytes::from_static(b\"data\"),\n        );\n\n        let mut buf = BytesMut::with_capacity(body.len() + 14);\n\n        // add junk before form to test pre-boundary data rejection\n        buf.put(\"testasdadsad\\r\\n\".as_bytes());\n\n        buf.put(body);\n\n        (buf.freeze(), headers)\n    }\n\n    // TODO: use test utility when multi-file support is introduced\n    fn create_double_request_with_header() -> (Bytes, HeaderMap) {\n        let bytes = Bytes::from(\n            \"testasdadsad\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Disposition: form-data; name=\\\"file\\\"; filename=\\\"fn.txt\\\"\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\nContent-Length: 4\\r\\n\\r\\n\\\n             test\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Disposition: form-data; name=\\\"file\\\"; filename=\\\"fn.txt\\\"\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\nContent-Length: 4\\r\\n\\r\\n\\\n             data\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0--\\r\\n\",\n        );\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/mixed; boundary=\\\"abbc761f78ff4d7cb7573b5a23f96ef0\\\"\",\n            ),\n        );\n        (bytes, headers)\n    }\n\n    #[actix_rt::test]\n    async fn test_multipart_no_end_crlf() {\n        let (sender, payload) = create_stream();\n        let (mut bytes, headers) = create_double_request_with_header();\n        let bytes_stripped = bytes.split_to(bytes.len()); // strip crlf\n\n        sender.send(Ok(bytes_stripped)).unwrap();\n        drop(sender); // eof\n\n        let mut multipart = Multipart::new(&headers, payload);\n\n        match multipart.next().await.unwrap() {\n            Ok(_) => {}\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await.unwrap() {\n            Ok(_) => {}\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await {\n            None => {}\n            _ => unreachable!(),\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_multipart() {\n        let (sender, payload) = create_stream();\n        let (bytes, headers) = create_double_request_with_header();\n\n        sender.send(Ok(bytes)).unwrap();\n\n        let mut multipart = Multipart::new(&headers, payload);\n        match multipart.next().await {\n            Some(Ok(mut field)) => {\n                let cd = field.content_disposition().unwrap();\n                assert_eq!(cd.disposition, DispositionType::FormData);\n                assert_eq!(cd.parameters[0], DispositionParam::Name(\"file\".into()));\n\n                assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);\n                assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);\n\n                match field.next().await.unwrap() {\n                    Ok(chunk) => assert_eq!(chunk, \"test\"),\n                    _ => unreachable!(),\n                }\n                match field.next().await {\n                    None => {}\n                    _ => unreachable!(),\n                }\n            }\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await.unwrap() {\n            Ok(mut field) => {\n                assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);\n                assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);\n\n                match field.next().await {\n                    Some(Ok(chunk)) => assert_eq!(chunk, \"data\"),\n                    _ => unreachable!(),\n                }\n                match field.next().await {\n                    None => {}\n                    _ => unreachable!(),\n                }\n            }\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await {\n            None => {}\n            _ => unreachable!(),\n        }\n    }\n\n    // Loops, collecting all bytes until end-of-field\n    async fn get_whole_field(field: &mut Field) -> BytesMut {\n        let mut b = BytesMut::new();\n        loop {\n            match field.next().await {\n                Some(Ok(chunk)) => b.extend_from_slice(&chunk),\n                None => return b,\n                _ => unreachable!(),\n            }\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_stream() {\n        let (bytes, headers) = create_double_request_with_header();\n        let payload = stream::iter(bytes)\n            .map(|byte| Ok(Bytes::copy_from_slice(&[byte])))\n            .interleave_pending();\n\n        let mut multipart = Multipart::new(&headers, payload);\n        match multipart.next().await.unwrap() {\n            Ok(mut field) => {\n                let cd = field.content_disposition().unwrap();\n                assert_eq!(cd.disposition, DispositionType::FormData);\n                assert_eq!(cd.parameters[0], DispositionParam::Name(\"file\".into()));\n\n                assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);\n                assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);\n\n                assert_eq!(get_whole_field(&mut field).await, \"test\");\n            }\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await {\n            Some(Ok(mut field)) => {\n                assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);\n                assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);\n\n                assert_eq!(get_whole_field(&mut field).await, \"data\");\n            }\n            _ => unreachable!(),\n        }\n\n        match multipart.next().await {\n            None => {}\n            _ => unreachable!(),\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_multipart_from_error() {\n        let err = Error::ContentTypeMissing;\n        let mut multipart = Multipart::from_error(err);\n        assert!(multipart.next().await.unwrap().is_err())\n    }\n\n    #[actix_rt::test]\n    async fn test_multipart_from_boundary() {\n        let (_, payload) = create_stream();\n        let (_, headers) = create_simple_request_with_header();\n        let (ct, boundary) = Multipart::find_ct_and_boundary(&headers).unwrap();\n        let _ = Multipart::from_ct_and_boundary(ct, boundary, payload);\n    }\n\n    #[actix_rt::test]\n    async fn test_multipart_payload_consumption() {\n        // with sample payload and HttpRequest with no headers\n        let (_, inner_payload) = h1::Payload::create(false);\n        let mut payload = actix_web::dev::Payload::from(inner_payload);\n        let req = TestRequest::default().to_http_request();\n\n        // multipart should generate an error\n        let mut mp = Multipart::from_request(&req, &mut payload).await.unwrap();\n        assert!(mp.next().await.unwrap().is_err());\n\n        // and should not consume the payload\n        match payload {\n            actix_web::dev::Payload::H1 { .. } => {} //expected\n            _ => unreachable!(),\n        }\n    }\n\n    #[actix_rt::test]\n    async fn no_content_disposition_form_data() {\n        let bytes = Bytes::from(\n            \"testasdadsad\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\n\\\n             Content-Length: 4\\r\\n\\\n             \\r\\n\\\n             test\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\",\n        );\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/form-data; boundary=\\\"abbc761f78ff4d7cb7573b5a23f96ef0\\\"\",\n            ),\n        );\n        let payload = stream::iter(bytes)\n            .map(|byte| Ok(Bytes::copy_from_slice(&[byte])))\n            .interleave_pending();\n\n        let mut multipart = Multipart::new(&headers, payload);\n        let res = multipart.next().await.unwrap();\n        assert_matches!(\n            res.expect_err(\n                \"according to RFC 7578, form-data fields require a content-disposition header\"\n            ),\n            Error::ContentDispositionMissing\n        );\n    }\n\n    #[actix_rt::test]\n    async fn no_content_disposition_non_form_data() {\n        let bytes = Bytes::from(\n            \"testasdadsad\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\n\\\n             Content-Length: 4\\r\\n\\\n             \\r\\n\\\n             test\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\",\n        );\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/mixed; boundary=\\\"abbc761f78ff4d7cb7573b5a23f96ef0\\\"\",\n            ),\n        );\n        let payload = stream::iter(bytes)\n            .map(|byte| Ok(Bytes::copy_from_slice(&[byte])))\n            .interleave_pending();\n\n        let mut multipart = Multipart::new(&headers, payload);\n        let res = multipart.next().await.unwrap();\n        res.unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn no_name_in_form_data_content_disposition() {\n        let bytes = Bytes::from(\n            \"testasdadsad\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\\\n             Content-Disposition: form-data; filename=\\\"fn.txt\\\"\\r\\n\\\n             Content-Type: text/plain; charset=utf-8\\r\\n\\\n             Content-Length: 4\\r\\n\\\n             \\r\\n\\\n             test\\r\\n\\\n             --abbc761f78ff4d7cb7573b5a23f96ef0\\r\\n\",\n        );\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            header::CONTENT_TYPE,\n            header::HeaderValue::from_static(\n                \"multipart/form-data; boundary=\\\"abbc761f78ff4d7cb7573b5a23f96ef0\\\"\",\n            ),\n        );\n        let payload = stream::iter(bytes)\n            .map(|byte| Ok(Bytes::copy_from_slice(&[byte])))\n            .interleave_pending();\n\n        let mut multipart = Multipart::new(&headers, payload);\n        let res = multipart.next().await.unwrap();\n        assert_matches!(\n            res.expect_err(\"according to RFC 7578, form-data fields require a name attribute\"),\n            Error::ContentDispositionNameMissing\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_drop_multipart_dont_hang() {\n        let (sender, payload) = create_stream();\n        let (bytes, headers) = create_simple_request_with_header();\n        sender.send(Ok(bytes)).unwrap();\n        drop(sender); // eof\n\n        let mut multipart = Multipart::new(&headers, payload);\n        let mut field = multipart.next().await.unwrap().unwrap();\n\n        drop(multipart);\n\n        // should fail immediately\n        match field.next().await {\n            Some(Err(Error::NotConsumed)) => {}\n            _ => panic!(),\n        };\n    }\n\n    #[actix_rt::test]\n    async fn test_drop_field_awaken_multipart() {\n        let (sender, payload) = create_stream();\n        let (bytes, headers) = create_double_request_with_header();\n        sender.send(Ok(bytes)).unwrap();\n        drop(sender); // eof\n\n        let mut multipart = Multipart::new(&headers, payload);\n        let mut field = multipart.next().await.unwrap().unwrap();\n\n        let task = rt::spawn(async move {\n            rt::time::sleep(Duration::from_millis(500)).await;\n            assert_eq!(field.next().await.unwrap().unwrap(), \"test\");\n            drop(field);\n        });\n\n        // dropping field should awaken current task\n        let _ = multipart.next().await.unwrap().unwrap();\n        task.await.unwrap();\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/payload.rs",
    "content": "use std::{\n    cell::{RefCell, RefMut},\n    cmp, mem,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_web::{\n    error::PayloadError,\n    web::{Bytes, BytesMut},\n};\nuse futures_core::stream::{LocalBoxStream, Stream};\n\nuse crate::{error::Error, safety::Safety};\n\npub(crate) struct PayloadRef {\n    payload: Rc<RefCell<PayloadBuffer>>,\n}\n\nimpl PayloadRef {\n    pub(crate) fn new(payload: PayloadBuffer) -> PayloadRef {\n        PayloadRef {\n            payload: Rc::new(RefCell::new(payload)),\n        }\n    }\n\n    pub(crate) fn get_mut(&self, safety: &Safety) -> Option<RefMut<'_, PayloadBuffer>> {\n        if safety.current() {\n            Some(self.payload.borrow_mut())\n        } else {\n            None\n        }\n    }\n}\n\nimpl Clone for PayloadRef {\n    fn clone(&self) -> PayloadRef {\n        PayloadRef {\n            payload: Rc::clone(&self.payload),\n        }\n    }\n}\n\n/// Payload buffer.\npub(crate) struct PayloadBuffer {\n    pub(crate) stream: LocalBoxStream<'static, Result<Bytes, PayloadError>>,\n    pub(crate) buf: BytesMut,\n    /// EOF flag. If true, no more payload reads will be attempted.\n    pub(crate) eof: bool,\n}\n\nimpl PayloadBuffer {\n    /// Constructs new payload buffer.\n    pub(crate) fn new<S>(stream: S) -> Self\n    where\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        PayloadBuffer {\n            stream: Box::pin(stream),\n            buf: BytesMut::with_capacity(1_024), // pre-allocate 1KiB\n            eof: false,\n        }\n    }\n\n    pub(crate) fn poll_stream(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {\n        loop {\n            match Pin::new(&mut self.stream).poll_next(cx) {\n                Poll::Ready(Some(Ok(data))) => {\n                    self.buf.extend_from_slice(&data);\n                    // try to read more data\n                    continue;\n                }\n                Poll::Ready(Some(Err(err))) => return Err(err),\n                Poll::Ready(None) => {\n                    self.eof = true;\n                    return Ok(());\n                }\n                Poll::Pending => return Ok(()),\n            }\n        }\n    }\n\n    /// Reads exact number of bytes.\n    #[cfg(test)]\n    pub(crate) fn read_exact(&mut self, size: usize) -> Option<Bytes> {\n        if size <= self.buf.len() {\n            Some(self.buf.split_to(size).freeze())\n        } else {\n            None\n        }\n    }\n\n    pub(crate) fn read_max(&mut self, size: u64) -> Result<Option<Bytes>, Error> {\n        if !self.buf.is_empty() {\n            let size = cmp::min(self.buf.len() as u64, size) as usize;\n            Ok(Some(self.buf.split_to(size).freeze()))\n        } else if self.eof {\n            Err(Error::Incomplete)\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Reads until specified ending.\n    ///\n    /// Returns:\n    ///\n    /// - `Ok(Some(chunk))` - `needle` is found, with chunk ending after needle\n    /// - `Err(Incomplete)` - `needle` is not found and we're at EOF\n    /// - `Ok(None)` - `needle` is not found otherwise\n    pub(crate) fn read_until(&mut self, needle: &[u8]) -> Result<Option<Bytes>, Error> {\n        match memchr::memmem::find(&self.buf, needle) {\n            // buffer exhausted and EOF without finding needle\n            None if self.eof => Err(Error::Incomplete),\n\n            // needle not yet found\n            None => Ok(None),\n\n            // needle found, split chunk out of buf\n            Some(idx) => Ok(Some(self.buf.split_to(idx + needle.len()).freeze())),\n        }\n    }\n\n    /// Reads bytes until new line delimiter (`\\n`, `0x0A`).\n    ///\n    /// Returns:\n    ///\n    /// - `Ok(Some(chunk))` - `needle` is found, with chunk ending after needle\n    /// - `Err(Incomplete)` - `needle` is not found and we're at EOF\n    /// - `Ok(None)` - `needle` is not found otherwise\n    #[inline]\n    pub(crate) fn readline(&mut self) -> Result<Option<Bytes>, Error> {\n        self.read_until(b\"\\n\")\n    }\n\n    /// Reads bytes until new line delimiter or until EOF.\n    #[inline]\n    pub(crate) fn readline_or_eof(&mut self) -> Result<Option<Bytes>, Error> {\n        match self.readline() {\n            Err(Error::Incomplete) if self.eof => Ok(Some(self.buf.split().freeze())),\n            line => line,\n        }\n    }\n\n    /// Puts unprocessed data back to the buffer.\n    pub(crate) fn unprocessed(&mut self, data: Bytes) {\n        // TODO: use BytesMut::from when it's released, see https://github.com/tokio-rs/bytes/pull/710\n        let buf = BytesMut::from(&data[..]);\n        let buf = mem::replace(&mut self.buf, buf);\n        self.buf.extend_from_slice(&buf);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::h1;\n    use futures_util::future::lazy;\n\n    use super::*;\n\n    #[actix_rt::test]\n    async fn basic() {\n        let (_, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n\n        assert_eq!(payload.buf.len(), 0);\n        lazy(|cx| payload.poll_stream(cx)).await.unwrap();\n        assert_eq!(None, payload.read_max(1).unwrap());\n    }\n\n    #[actix_rt::test]\n    async fn eof() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n\n        assert_eq!(None, payload.read_max(4).unwrap());\n        sender.feed_data(Bytes::from(\"data\"));\n        sender.feed_eof();\n        lazy(|cx| payload.poll_stream(cx)).await.unwrap();\n\n        assert_eq!(Some(Bytes::from(\"data\")), payload.read_max(4).unwrap());\n        assert_eq!(payload.buf.len(), 0);\n        assert!(payload.read_max(1).is_err());\n        assert!(payload.eof);\n    }\n\n    #[actix_rt::test]\n    async fn err() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n        assert_eq!(None, payload.read_max(1).unwrap());\n        sender.set_error(PayloadError::Incomplete(None));\n        lazy(|cx| payload.poll_stream(cx)).await.err().unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn read_max() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n\n        sender.feed_data(Bytes::from(\"line1\"));\n        sender.feed_data(Bytes::from(\"line2\"));\n        lazy(|cx| payload.poll_stream(cx)).await.unwrap();\n        assert_eq!(payload.buf.len(), 10);\n\n        assert_eq!(Some(Bytes::from(\"line1\")), payload.read_max(5).unwrap());\n        assert_eq!(payload.buf.len(), 5);\n\n        assert_eq!(Some(Bytes::from(\"line2\")), payload.read_max(5).unwrap());\n        assert_eq!(payload.buf.len(), 0);\n    }\n\n    #[actix_rt::test]\n    async fn read_exactly() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n\n        assert_eq!(None, payload.read_exact(2));\n\n        sender.feed_data(Bytes::from(\"line1\"));\n        sender.feed_data(Bytes::from(\"line2\"));\n        lazy(|cx| payload.poll_stream(cx)).await.unwrap();\n\n        assert_eq!(Some(Bytes::from_static(b\"li\")), payload.read_exact(2));\n        assert_eq!(payload.buf.len(), 8);\n\n        assert_eq!(Some(Bytes::from_static(b\"ne1l\")), payload.read_exact(4));\n        assert_eq!(payload.buf.len(), 4);\n    }\n\n    #[actix_rt::test]\n    async fn read_until() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let mut payload = PayloadBuffer::new(payload);\n\n        assert_eq!(None, payload.read_until(b\"ne\").unwrap());\n\n        sender.feed_data(Bytes::from(\"line1\"));\n        sender.feed_data(Bytes::from(\"line2\"));\n        lazy(|cx| payload.poll_stream(cx)).await.unwrap();\n\n        assert_eq!(\n            Some(Bytes::from(\"line\")),\n            payload.read_until(b\"ne\").unwrap()\n        );\n        assert_eq!(payload.buf.len(), 6);\n\n        assert_eq!(\n            Some(Bytes::from(\"1line2\")),\n            payload.read_until(b\"2\").unwrap()\n        );\n        assert_eq!(payload.buf.len(), 0);\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/safety.rs",
    "content": "use std::{cell::Cell, marker::PhantomData, rc::Rc, task};\n\nuse local_waker::LocalWaker;\n\n/// Counter. It tracks of number of clones of payloads and give access to payload only to top most.\n///\n/// - When dropped, parent task is awakened. This is to support the case where `Field` is dropped in\n///   a separate task than `Multipart`.\n/// - Assumes that parent owners don't move to different tasks; only the top-most is allowed to.\n/// - If dropped and is not top most owner, is_clean flag is set to false.\n#[derive(Debug)]\npub(crate) struct Safety {\n    task: LocalWaker,\n    level: usize,\n    payload: Rc<PhantomData<bool>>,\n    clean: Rc<Cell<bool>>,\n}\n\nimpl Safety {\n    pub(crate) fn new() -> Safety {\n        let payload = Rc::new(PhantomData);\n        Safety {\n            task: LocalWaker::new(),\n            level: Rc::strong_count(&payload),\n            clean: Rc::new(Cell::new(true)),\n            payload,\n        }\n    }\n\n    pub(crate) fn current(&self) -> bool {\n        Rc::strong_count(&self.payload) == self.level && self.clean.get()\n    }\n\n    pub(crate) fn is_clean(&self) -> bool {\n        self.clean.get()\n    }\n\n    pub(crate) fn clone(&self, cx: &task::Context<'_>) -> Safety {\n        let payload = Rc::clone(&self.payload);\n        let s = Safety {\n            task: LocalWaker::new(),\n            level: Rc::strong_count(&payload),\n            clean: self.clean.clone(),\n            payload,\n        };\n        s.task.register(cx.waker());\n        s\n    }\n}\n\nimpl Drop for Safety {\n    fn drop(&mut self) {\n        if Rc::strong_count(&self.payload) != self.level {\n            // Multipart dropped leaving a Field\n            self.clean.set(false);\n        }\n\n        self.task.wake();\n    }\n}\n"
  },
  {
    "path": "actix-multipart/src/test.rs",
    "content": "//! Multipart testing utilities.\n\nuse actix_web::{\n    http::header::{self, HeaderMap},\n    web::{BufMut as _, Bytes, BytesMut},\n};\nuse mime::Mime;\nuse rand::distr::{Alphanumeric, SampleString as _};\n\nconst CRLF: &[u8] = b\"\\r\\n\";\nconst CRLF_CRLF: &[u8] = b\"\\r\\n\\r\\n\";\nconst HYPHENS: &[u8] = b\"--\";\nconst BOUNDARY_PREFIX: &str = \"------------------------\";\n\n/// Constructs a `multipart/form-data` payload from bytes and metadata.\n///\n/// Returned header map can be extended or merged with existing headers.\n///\n/// Multipart boundary used is a random alphanumeric string.\n///\n/// # Examples\n///\n/// ```\n/// use actix_multipart::test::create_form_data_payload_and_headers;\n/// use actix_web::{test::TestRequest, web::Bytes};\n/// use memchr::memmem::find;\n///\n/// let (body, headers) = create_form_data_payload_and_headers(\n///     \"foo\",\n///     Some(\"lorem.txt\".to_owned()),\n///     Some(mime::TEXT_PLAIN_UTF_8),\n///     Bytes::from_static(b\"Lorem ipsum.\"),\n/// );\n///\n/// assert!(find(&body, b\"foo\").is_some());\n/// assert!(find(&body, b\"lorem.txt\").is_some());\n/// assert!(find(&body, b\"text/plain; charset=utf-8\").is_some());\n/// assert!(find(&body, b\"Lorem ipsum.\").is_some());\n///\n/// let req = TestRequest::default();\n///\n/// // merge header map into existing test request and set multipart body\n/// let req = headers\n///     .into_iter()\n///     .fold(req, |req, hdr| req.insert_header(hdr))\n///     .set_payload(body)\n///     .to_http_request();\n///\n/// assert!(\n///     req.headers()\n///         .get(\"content-type\")\n///         .unwrap()\n///         .to_str()\n///         .unwrap()\n///         .starts_with(\"multipart/form-data; boundary=\\\"\")\n/// );\n/// ```\npub fn create_form_data_payload_and_headers(\n    name: &str,\n    filename: Option<String>,\n    content_type: Option<Mime>,\n    file: Bytes,\n) -> (Bytes, HeaderMap) {\n    let boundary = Alphanumeric.sample_string(&mut rand::rng(), 32);\n\n    create_form_data_payload_and_headers_with_boundary(\n        &boundary,\n        name,\n        filename,\n        content_type,\n        file,\n    )\n}\n\n/// Constructs a `multipart/form-data` payload from bytes and metadata with a fixed boundary.\n///\n/// See [`create_form_data_payload_and_headers`] for more details.\npub fn create_form_data_payload_and_headers_with_boundary(\n    boundary: &str,\n    name: &str,\n    filename: Option<String>,\n    content_type: Option<Mime>,\n    file: Bytes,\n) -> (Bytes, HeaderMap) {\n    let mut buf = BytesMut::with_capacity(file.len() + 128);\n\n    let boundary_str = [BOUNDARY_PREFIX, boundary].concat();\n    let boundary = boundary_str.as_bytes();\n\n    buf.put(HYPHENS);\n    buf.put(boundary);\n    buf.put(CRLF);\n\n    buf.put(format!(\"Content-Disposition: form-data; name=\\\"{name}\\\"\").as_bytes());\n    if let Some(filename) = filename {\n        buf.put(format!(\"; filename=\\\"{filename}\\\"\").as_bytes());\n    }\n    buf.put(CRLF);\n\n    if let Some(ct) = content_type {\n        buf.put(format!(\"Content-Type: {ct}\").as_bytes());\n        buf.put(CRLF);\n    }\n\n    buf.put(format!(\"Content-Length: {}\", file.len()).as_bytes());\n    buf.put(CRLF_CRLF);\n\n    buf.put(file);\n    buf.put(CRLF);\n\n    buf.put(HYPHENS);\n    buf.put(boundary);\n    buf.put(HYPHENS);\n    buf.put(CRLF);\n\n    let mut headers = HeaderMap::new();\n    headers.insert(\n        header::CONTENT_TYPE,\n        format!(\"multipart/form-data; boundary=\\\"{boundary_str}\\\"\")\n            .parse()\n            .unwrap(),\n    );\n\n    (buf.freeze(), headers)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::convert::Infallible;\n\n    use futures_util::stream;\n\n    use super::*;\n\n    fn find_boundary(headers: &HeaderMap) -> String {\n        headers\n            .get(\"content-type\")\n            .unwrap()\n            .to_str()\n            .unwrap()\n            .parse::<mime::Mime>()\n            .unwrap()\n            .get_param(mime::BOUNDARY)\n            .unwrap()\n            .as_str()\n            .to_owned()\n    }\n\n    #[test]\n    fn wire_format() {\n        let (pl, headers) = create_form_data_payload_and_headers_with_boundary(\n            \"qWeRtYuIoP\",\n            \"foo\",\n            None,\n            None,\n            Bytes::from_static(b\"Lorem ipsum dolor\\nsit ame.\"),\n        );\n\n        assert_eq!(\n            find_boundary(&headers),\n            \"------------------------qWeRtYuIoP\",\n        );\n\n        assert_eq!(\n            std::str::from_utf8(&pl).unwrap(),\n            \"--------------------------qWeRtYuIoP\\r\\n\\\n            Content-Disposition: form-data; name=\\\"foo\\\"\\r\\n\\\n            Content-Length: 26\\r\\n\\\n            \\r\\n\\\n            Lorem ipsum dolor\\n\\\n            sit ame.\\r\\n\\\n            --------------------------qWeRtYuIoP--\\r\\n\",\n        );\n\n        let (pl, _headers) = create_form_data_payload_and_headers_with_boundary(\n            \"qWeRtYuIoP\",\n            \"foo\",\n            Some(\"Lorem.txt\".to_owned()),\n            Some(mime::TEXT_PLAIN_UTF_8),\n            Bytes::from_static(b\"Lorem ipsum dolor\\nsit ame.\"),\n        );\n\n        assert_eq!(\n            std::str::from_utf8(&pl).unwrap(),\n            \"--------------------------qWeRtYuIoP\\r\\n\\\n            Content-Disposition: form-data; name=\\\"foo\\\"; filename=\\\"Lorem.txt\\\"\\r\\n\\\n            Content-Type: text/plain; charset=utf-8\\r\\n\\\n            Content-Length: 26\\r\\n\\\n            \\r\\n\\\n            Lorem ipsum dolor\\n\\\n            sit ame.\\r\\n\\\n            --------------------------qWeRtYuIoP--\\r\\n\",\n        );\n    }\n\n    /// Test using an external library to prevent the two-wrongs-make-a-right class of errors.\n    #[actix_web::test]\n    async fn ecosystem_compat() {\n        let (pl, headers) = create_form_data_payload_and_headers(\n            \"foo\",\n            None,\n            None,\n            Bytes::from_static(b\"Lorem ipsum dolor\\nsit ame.\"),\n        );\n\n        let boundary = find_boundary(&headers);\n\n        let pl = stream::once(async { Ok::<_, Infallible>(pl) });\n\n        let mut form = multer::Multipart::new(pl, boundary);\n        let field = form.next_field().await.unwrap().unwrap();\n        assert_eq!(field.name().unwrap(), \"foo\");\n        assert_eq!(field.file_name(), None);\n        assert_eq!(field.content_type(), None);\n        assert!(field.bytes().await.unwrap().starts_with(b\"Lorem\"));\n    }\n}\n"
  },
  {
    "path": "actix-multipart-derive/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n## 0.7.0\n\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 0.6.1\n\n- Update `syn` dependency to `2`.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 0.6.0\n\n- Add `MultipartForm` derive macro.\n"
  },
  {
    "path": "actix-multipart-derive/Cargo.toml",
    "content": "[package]\nname = \"actix-multipart-derive\"\nversion = \"0.7.0\"\nauthors = [\"Jacob Halsey <jacob@jhalsey.com>\"]\ndescription = \"Multipart form derive macro for Actix Web\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\", \"futures\"]\nhomepage.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\nbytesize = \"2\"\ndarling = \"0.20\"\nproc-macro2 = \"1\"\nquote = \"1\"\nsyn = \"2\"\n\n[dev-dependencies]\nactix-multipart = \"0.7\"\nactix-web = \"4\"\nrustversion-msrv = \"0.100\"\ntrybuild = \"1\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-multipart-derive/README.md",
    "content": "# `actix-multipart-derive`\n\n> The derive macro implementation for actix-multipart-derive.\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-multipart-derive?label=latest)](https://crates.io/crates/actix-multipart-derive)\n[![Documentation](https://docs.rs/actix-multipart-derive/badge.svg?version=0.7.0)](https://docs.rs/actix-multipart-derive/0.7.0)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart-derive.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-multipart-derive/0.7.0/status.svg)](https://deps.rs/crate/actix-multipart-derive/0.7.0)\n[![Download](https://img.shields.io/crates/d/actix-multipart-derive.svg)](https://crates.io/crates/actix-multipart-derive)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": "actix-multipart-derive/src/lib.rs",
    "content": "//! Multipart form derive macro for Actix Web.\n//!\n//! See [`macro@MultipartForm`] for usage examples.\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n#![allow(clippy::disallowed_names)] // false positives in some macro expansions\n\nuse std::collections::HashSet;\n\nuse bytesize::ByteSize;\nuse darling::{FromDeriveInput, FromField, FromMeta};\nuse proc_macro::TokenStream;\nuse proc_macro2::Ident;\nuse quote::quote;\nuse syn::{parse_macro_input, Type};\n\n#[derive(Default, FromMeta)]\nenum DuplicateField {\n    #[default]\n    Ignore,\n    Deny,\n    Replace,\n}\n\n#[derive(FromDeriveInput, Default)]\n#[darling(attributes(multipart), default)]\nstruct MultipartFormAttrs {\n    deny_unknown_fields: bool,\n    duplicate_field: DuplicateField,\n}\n\n#[allow(clippy::disallowed_names)] // false positive in macro expansion\n#[derive(FromField, Default)]\n#[darling(attributes(multipart), default)]\nstruct FieldAttrs {\n    rename: Option<String>,\n    limit: Option<String>,\n}\n\nstruct ParsedField<'t> {\n    serialization_name: String,\n    rust_name: &'t Ident,\n    limit: Option<usize>,\n    ty: &'t Type,\n}\n\n/// Implements `MultipartCollect` for a struct so that it can be used with the `MultipartForm`\n/// extractor.\n///\n/// # Basic Use\n///\n/// Each field type should implement the `FieldReader` trait:\n///\n/// ```\n/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n///\n/// #[derive(MultipartForm)]\n/// struct ImageUpload {\n///     description: Text<String>,\n///     timestamp: Text<i64>,\n///     image: TempFile,\n/// }\n/// ```\n///\n/// # Optional and List Fields\n///\n/// You can also use `Vec<T>` and `Option<T>` provided that `T: FieldReader`.\n///\n/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field\n/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3).\n///\n/// ```\n/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n///\n/// #[derive(MultipartForm)]\n/// struct Form {\n///     category: Option<Text<String>>,\n///     files: Vec<TempFile>,\n/// }\n/// ```\n///\n/// # Field Renaming\n///\n/// You can use the `#[multipart(rename = \"foo\")]` attribute to receive a field by a different name.\n///\n/// ```\n/// use actix_multipart::form::{tempfile::TempFile, MultipartForm};\n///\n/// #[derive(MultipartForm)]\n/// struct Form {\n///     #[multipart(rename = \"files[]\")]\n///     files: Vec<TempFile>,\n/// }\n/// ```\n///\n/// # Field Limits\n///\n/// You can use the `#[multipart(limit = \"<size>\")]` attribute to set field level limits. The limit\n/// string is parsed using [`bytesize`].\n///\n/// Note: the form is also subject to the global limits configured using `MultipartFormConfig`.\n///\n/// ```\n/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n///\n/// #[derive(MultipartForm)]\n/// struct Form {\n///     #[multipart(limit = \"2 KiB\")]\n///     description: Text<String>,\n///\n///     #[multipart(limit = \"512 MiB\")]\n///     files: Vec<TempFile>,\n/// }\n/// ```\n///\n/// # Unknown Fields\n///\n/// By default fields with an unknown name are ignored. They can be rejected using the\n/// `#[multipart(deny_unknown_fields)]` attribute:\n///\n/// ```\n/// # use actix_multipart::form::MultipartForm;\n/// #[derive(MultipartForm)]\n/// #[multipart(deny_unknown_fields)]\n/// struct Form { }\n/// ```\n///\n/// # Duplicate Fields\n///\n/// The behaviour for when multiple fields with the same name are received can be changed using the\n/// `#[multipart(duplicate_field = \"<behavior>\")]` attribute:\n///\n/// - \"ignore\": (default) Extra fields are ignored. I.e., the first one is persisted.\n/// - \"deny\": A `MultipartError::UnknownField` error response is returned.\n/// - \"replace\": Each field is processed, but only the last one is persisted.\n///\n/// Note that `Vec` fields will ignore this option.\n///\n/// ```\n/// # use actix_multipart::form::MultipartForm;\n/// #[derive(MultipartForm)]\n/// #[multipart(duplicate_field = \"deny\")]\n/// struct Form { }\n/// ```\n///\n/// [`bytesize`]: https://docs.rs/bytesize/2\n#[proc_macro_derive(MultipartForm, attributes(multipart))]\npub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {\n    let input: syn::DeriveInput = parse_macro_input!(input);\n\n    let name = &input.ident;\n\n    let data_struct = match &input.data {\n        syn::Data::Struct(data_struct) => data_struct,\n        _ => {\n            return compile_err(syn::Error::new(\n                input.ident.span(),\n                \"`MultipartForm` can only be derived for structs\",\n            ))\n        }\n    };\n\n    let fields = match &data_struct.fields {\n        syn::Fields::Named(fields_named) => fields_named,\n        _ => {\n            return compile_err(syn::Error::new(\n                input.ident.span(),\n                \"`MultipartForm` can only be derived for a struct with named fields\",\n            ))\n        }\n    };\n\n    let attrs = match MultipartFormAttrs::from_derive_input(&input) {\n        Ok(attrs) => attrs,\n        Err(err) => return err.write_errors().into(),\n    };\n\n    // Parse the field attributes\n    let parsed = match fields\n        .named\n        .iter()\n        .map(|field| {\n            let rust_name = field.ident.as_ref().unwrap();\n            let attrs = FieldAttrs::from_field(field).map_err(|err| err.write_errors())?;\n            let serialization_name = attrs.rename.unwrap_or_else(|| rust_name.to_string());\n\n            let limit = match attrs.limit.map(|limit| match limit.parse::<ByteSize>() {\n                Ok(ByteSize(size)) => Ok(usize::try_from(size).unwrap()),\n                Err(err) => Err(syn::Error::new(\n                    field.ident.as_ref().unwrap().span(),\n                    format!(\"Could not parse size limit `{}`: {}\", limit, err),\n                )),\n            }) {\n                Some(Err(err)) => return Err(compile_err(err)),\n                limit => limit.map(Result::unwrap),\n            };\n\n            Ok(ParsedField {\n                serialization_name,\n                rust_name,\n                limit,\n                ty: &field.ty,\n            })\n        })\n        .collect::<Result<Vec<_>, TokenStream>>()\n    {\n        Ok(attrs) => attrs,\n        Err(err) => return err,\n    };\n\n    // Check that field names are unique\n    let mut set = HashSet::new();\n    for field in &parsed {\n        if !set.insert(field.serialization_name.clone()) {\n            return compile_err(syn::Error::new(\n                field.rust_name.span(),\n                format!(\"Multiple fields named: `{}`\", field.serialization_name),\n            ));\n        }\n    }\n\n    // Return value when a field name is not supported by the form\n    let unknown_field_result = if attrs.deny_unknown_fields {\n        quote!(::std::result::Result::Err(\n            ::actix_multipart::MultipartError::UnknownField(field.name().unwrap().to_string())\n        ))\n    } else {\n        quote!(::std::result::Result::Ok(()))\n    };\n\n    // Value for duplicate action\n    let duplicate_field = match attrs.duplicate_field {\n        DuplicateField::Ignore => quote!(::actix_multipart::form::DuplicateField::Ignore),\n        DuplicateField::Deny => quote!(::actix_multipart::form::DuplicateField::Deny),\n        DuplicateField::Replace => quote!(::actix_multipart::form::DuplicateField::Replace),\n    };\n\n    // limit() implementation\n    let mut limit_impl = quote!();\n    for field in &parsed {\n        let name = &field.serialization_name;\n        if let Some(value) = field.limit {\n            limit_impl.extend(quote!(\n                #name => ::std::option::Option::Some(#value),\n            ));\n        }\n    }\n\n    // handle_field() implementation\n    let mut handle_field_impl = quote!();\n    for field in &parsed {\n        let name = &field.serialization_name;\n        let ty = &field.ty;\n\n        handle_field_impl.extend(quote!(\n            #name => ::std::boxed::Box::pin(\n                <#ty as ::actix_multipart::form::FieldGroupReader>::handle_field(req, field, limits, state, #duplicate_field)\n            ),\n        ));\n    }\n\n    // from_state() implementation\n    let mut from_state_impl = quote!();\n    for field in &parsed {\n        let name = &field.serialization_name;\n        let rust_name = &field.rust_name;\n        let ty = &field.ty;\n        from_state_impl.extend(quote!(\n            #rust_name: <#ty as ::actix_multipart::form::FieldGroupReader>::from_state(#name, &mut state)?,\n        ));\n    }\n\n    let gen = quote! {\n        impl ::actix_multipart::form::MultipartCollect for #name {\n            fn limit(field_name: &str) -> ::std::option::Option<usize> {\n                match field_name {\n                    #limit_impl\n                    _ => None,\n                }\n            }\n\n            fn handle_field<'t>(\n                req: &'t ::actix_web::HttpRequest,\n                field: ::actix_multipart::Field,\n                limits: &'t mut ::actix_multipart::form::Limits,\n                state: &'t mut ::actix_multipart::form::State,\n            ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<(), ::actix_multipart::MultipartError>> + 't>> {\n                match field.name().unwrap() {\n                    #handle_field_impl\n                    _ => return ::std::boxed::Box::pin(::std::future::ready(#unknown_field_result)),\n                }\n            }\n\n            fn from_state(mut state: ::actix_multipart::form::State) -> ::std::result::Result<Self, ::actix_multipart::MultipartError> {\n                Ok(Self {\n                    #from_state_impl\n                })\n            }\n\n        }\n    };\n    gen.into()\n}\n\n/// Transform a syn error into a token stream for returning.\nfn compile_err(err: syn::Error) -> TokenStream {\n    TokenStream::from(err.to_compile_error())\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/all-required.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n\n#[derive(Debug, MultipartForm)]\nstruct ImageUpload {\n    description: Text<String>,\n    timestamp: Text<i64>,\n    image: TempFile,\n}\n\nasync fn handler(_form: MultipartForm<ImageUpload>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/deny-duplicates.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::MultipartForm;\n\n#[derive(MultipartForm)]\n#[multipart(duplicate_field = \"deny\")]\nstruct Form {}\n\nasync fn handler(_form: MultipartForm<Form>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/deny-parse-fail.rs",
    "content": "use actix_multipart::form::MultipartForm;\n\n#[derive(MultipartForm)]\n#[multipart(duplicate_field = \"no\")]\nstruct Form {}\n\nfn main() {}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/deny-parse-fail.stderr",
    "content": "error: Unknown literal value `no`\n --> tests/trybuild/deny-parse-fail.rs:4:31\n  |\n4 | #[multipart(duplicate_field = \"no\")]\n  |                               ^^^^\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/deny-unknown.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::MultipartForm;\n\n#[derive(MultipartForm)]\n#[multipart(deny_unknown_fields)]\nstruct Form {}\n\nasync fn handler(_form: MultipartForm<Form>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/optional-and-list.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n\n#[derive(MultipartForm)]\nstruct Form {\n    category: Option<Text<String>>,\n    files: Vec<TempFile>,\n}\n\nasync fn handler(_form: MultipartForm<Form>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/rename.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::{tempfile::TempFile, MultipartForm};\n\n#[derive(MultipartForm)]\nstruct Form {\n    #[multipart(rename = \"files[]\")]\n    files: Vec<TempFile>,\n}\n\nasync fn handler(_form: MultipartForm<Form>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/size-limit-parse-fail.rs",
    "content": "use actix_multipart::form::{text::Text, MultipartForm};\n\n#[derive(MultipartForm)]\nstruct Form {\n    #[multipart(limit = \"2 bytes\")]\n    description: Text<String>,\n}\n\n#[derive(MultipartForm)]\nstruct Form2 {\n    #[multipart(limit = \"2 megabytes\")]\n    description: Text<String>,\n}\n\n#[derive(MultipartForm)]\nstruct Form3 {\n    #[multipart(limit = \"four meters\")]\n    description: Text<String>,\n}\n\nfn main() {}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/size-limit-parse-fail.stderr",
    "content": "error: Could not parse size limit `2 bytes`: couldn't parse \"bytes\" into a known SI unit, Failed to parse unit \"byt...\"\n --> tests/trybuild/size-limit-parse-fail.rs:6:5\n  |\n6 |     description: Text<String>,\n  |     ^^^^^^^^^^^\n\nerror: Could not parse size limit `2 megabytes`: couldn't parse \"megabytes\" into a known SI unit, Failed to parse unit \"meg...\"\n  --> tests/trybuild/size-limit-parse-fail.rs:12:5\n   |\n12 |     description: Text<String>,\n   |     ^^^^^^^^^^^\n\nerror: Could not parse size limit `four meters`: couldn't parse \"four meters\" into a ByteSize, cannot parse float from empty string\n  --> tests/trybuild/size-limit-parse-fail.rs:18:5\n   |\n18 |     description: Text<String>,\n   |     ^^^^^^^^^^^\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild/size-limits.rs",
    "content": "use actix_web::{web, App, Responder};\n\nuse actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};\n\n#[derive(MultipartForm)]\nstruct Form {\n    #[multipart(limit = \"2 KiB\")]\n    description: Text<String>,\n\n    #[multipart(limit = \"512 MiB\")]\n    files: Vec<TempFile>,\n}\n\nasync fn handler(_form: MultipartForm<Form>) -> impl Responder {\n    \"Hello World!\"\n}\n\n#[actix_web::main]\nasync fn main() {\n    App::new().default_service(web::to(handler));\n}\n"
  },
  {
    "path": "actix-multipart-derive/tests/trybuild.rs",
    "content": "#[rustversion_msrv::msrv]\n#[test]\nfn compile_macros() {\n    let t = trybuild::TestCases::new();\n\n    t.pass(\"tests/trybuild/all-required.rs\");\n    t.pass(\"tests/trybuild/optional-and-list.rs\");\n    t.pass(\"tests/trybuild/rename.rs\");\n    t.pass(\"tests/trybuild/deny-unknown.rs\");\n\n    t.pass(\"tests/trybuild/deny-duplicates.rs\");\n    t.compile_fail(\"tests/trybuild/deny-parse-fail.rs\");\n\n    t.pass(\"tests/trybuild/size-limits.rs\");\n    t.compile_fail(\"tests/trybuild/size-limit-parse-fail.rs\");\n}\n"
  },
  {
    "path": "actix-router/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n## 0.5.4\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n- Support `deserialize_any` in `PathDeserializer` (enables derived `#[serde(untagged)]` enums in path segments). [#2881]\n- Fix stale path segment indices after path rewrites, preventing out-of-bounds access during extraction. [#3562]\n\n[#2881]: https://github.com/actix/actix-web/pull/2881\n[#3562]: https://github.com/actix/actix-web/issues/3562\n\n## 0.5.3\n\n- Add `unicode` crate feature (on-by-default) to switch between `regex` and `regex-lite` as a trade-off between full unicode support and binary size.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 0.5.2\n\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 0.5.1\n\n- Correct typo in error string for `i32` deserialization. [#2876]\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n[#2876]: https://github.com/actix/actix-web/pull/2876\n\n## 0.5.0\n\n### Added\n\n- Add `Path::as_str`. [#2590]\n- Add `ResourceDef::set_name`. [#373][net#373]\n- Add `RouterBuilder::push`. [#2612]\n- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]\n- Introduce `ResourceDef::join`. [#380][net#380]\n- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]\n- `Resource` is now implemented for `&mut Path<_>` and `RefMut<Path<_>>`. [#2568]\n- Support `build_resource_path` on multi-pattern resources. [#2356]\n- Support multi-pattern prefixes and joins. [#2356]\n\n### Changed\n\n- Change signature of `ResourceDef::capture_match_info_fn` to remove `user_data` parameter. [#2612]\n- Deprecate `Path::path`. [#2590]\n- Disallow prefix routes with tail segments. [#379][net#379]\n- Enforce path separators on dynamic prefixes. [#378][net#378]\n- Minimum supported Rust version (MSRV) is now 1.54.\n- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]\n- Prefix segments with trailing slashes define a trailing empty segment. [#2355]\n- `Quoter::requote` now returns `Option<Vec<u8>>`. [#2613]\n- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]\n- Rename `Path::{len => segment_count}` to be more descriptive of its purpose. [#370][net#370]\n- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]\n- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]\n- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]\n- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]\n- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]\n- Rename `Router::{*_checked => *_fn}`. [#373][net#373]\n- Replace `Option<U>` with `U` in `Router` API. [#2612]\n- `Resource` trait now uses an associated type, `Path`, instead of a generic parameter. [#2568]\n- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]\n- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]\n- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]\n- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]\n\n### Fixed\n\n- Fix `ResourceDef`'s `PartialEq` implementation. [#373][net#373]\n- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]\n- Improve malformed path error message. [#384][net#384]\n- `PathDeserializer` now decodes all percent encoded characters in dynamic segments. [#2566]\n- Relax bounds on `Router::recognize*` and `ResourceDef::capture_match_info`. [#2612]\n- Static patterns in multi-patterns are no longer interpreted as regex. [#366][net#366]\n\n### Removed\n\n- `ResourceDef::name_mut`. [#373][net#373]\n- Unused `ResourceInfo`. [#2612]\n\n[#2355]: https://github.com/actix/actix-web/pull/2355\n[#2356]: https://github.com/actix/actix-web/pull/2356\n[#2566]: https://github.com/actix/actix-net/pull/2566\n[#2568]: https://github.com/actix/actix-web/pull/2568\n[#2590]: https://github.com/actix/actix-web/pull/2590\n[#2612]: https://github.com/actix/actix-web/pull/2612\n[#2613]: https://github.com/actix/actix-web/pull/2613\n[net#366]: https://github.com/actix/actix-net/pull/366\n[net#368]: https://github.com/actix/actix-net/pull/368\n[net#368]: https://github.com/actix/actix-net/pull/368\n[net#370]: https://github.com/actix/actix-net/pull/370\n[net#371]: https://github.com/actix/actix-net/pull/371\n[net#372]: https://github.com/actix/actix-net/pull/372\n[net#373]: https://github.com/actix/actix-net/pull/373\n[net#378]: https://github.com/actix/actix-net/pull/378\n[net#379]: https://github.com/actix/actix-net/pull/379\n[net#380]: https://github.com/actix/actix-net/pull/380\n[net#384]: https://github.com/actix/actix-net/pull/384\n\n<details>\n<summary>0.5.0 Pre-Releases</summary>\n\n## 0.5.0-rc.3\n\n- Remove unused `ResourceInfo`. [#2612]\n- Add `RouterBuilder::push`. [#2612]\n- Change signature of `ResourceDef::capture_match_info_fn` to remove `user_data` parameter. [#2612]\n- Replace `Option<U>` with `U` in `Router` API. [#2612]\n- Relax bounds on `Router::recognize*` and `ResourceDef::capture_match_info`. [#2612]\n- `Quoter::requote` now returns `Option<Vec<u8>>`. [#2613]\n\n[#2612]: https://github.com/actix/actix-web/pull/2612\n[#2613]: https://github.com/actix/actix-web/pull/2613\n\n## 0.5.0-rc.2\n\n- Add `Path::as_str`. [#2590]\n- Deprecate `Path::path`. [#2590]\n\n[#2590]: https://github.com/actix/actix-web/pull/2590\n\n## 0.5.0-rc.1\n\n- `Resource` trait now have an associated type, `Path`, instead of the generic parameter. [#2568]\n- `Resource` is now implemented for `&mut Path<_>` and `RefMut<Path<_>>`. [#2568]\n\n[#2568]: https://github.com/actix/actix-web/pull/2568\n\n## 0.5.0-beta.4\n\n- `PathDeserializer` now decodes all percent encoded characters in dynamic segments. [#2566]\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n[#2566]: https://github.com/actix/actix-net/pull/2566\n\n## 0.5.0-beta.3\n\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n## 0.5.0-beta.2\n\n- Introduce `ResourceDef::join`. [#380][net#380]\n- Disallow prefix routes with tail segments. [#379][net#379]\n- Enforce path separators on dynamic prefixes. [#378][net#378]\n- Improve malformed path error message. [#384][net#384]\n- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]\n- Prefix segments with trailing slashes define a trailing empty segment. [#2355]\n- Support multi-pattern prefixes and joins. [#2356]\n- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]\n- Support `build_resource_path` on multi-pattern resources. [#2356]\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n[net#378]: https://github.com/actix/actix-net/pull/378\n[net#379]: https://github.com/actix/actix-net/pull/379\n[net#380]: https://github.com/actix/actix-net/pull/380\n[net#384]: https://github.com/actix/actix-net/pull/384\n[#2355]: https://github.com/actix/actix-web/pull/2355\n[#2356]: https://github.com/actix/actix-web/pull/2356\n\n## 0.5.0-beta.1\n\n- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366][net#366]\n- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]\n- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]\n- Fix `ResourceDef` `PartialEq` implementation. [#373][net#373]\n- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]\n- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]\n- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370][net#370]\n- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]\n- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]\n- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]\n- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]\n- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]\n- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]\n- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373][net#373]\n- Rename `Router::{*_checked => *_fn}`. [#373][net#373]\n- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]\n- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]\n\n[net#368]: https://github.com/actix/actix-net/pull/368\n[net#366]: https://github.com/actix/actix-net/pull/366\n[net#368]: https://github.com/actix/actix-net/pull/368\n[net#370]: https://github.com/actix/actix-net/pull/370\n[net#371]: https://github.com/actix/actix-net/pull/371\n[net#372]: https://github.com/actix/actix-net/pull/372\n[net#373]: https://github.com/actix/actix-net/pull/373\n\n</details>\n\n## 0.4.0\n\n- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357][net#357]\n- Path tail patterns now match new lines (`\\n`) in request URL. [#360][net#360]\n- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359][net#359]\n- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345][net#345]\n\n[net#345]: https://github.com/actix/actix-net/pull/345\n[net#357]: https://github.com/actix/actix-net/pull/357\n[net#359]: https://github.com/actix/actix-net/pull/359\n[net#360]: https://github.com/actix/actix-net/pull/360\n\n## 0.3.0\n\n- Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0\n\n## 0.2.7\n\n- Add `Router::recognize_checked` [#247][net#247]\n\n[net#247]: https://github.com/actix/actix-net/pull/247\n\n## 0.2.6\n\n- Use `bytestring` version range compatible with Bytes v1.0. [#246][net#246]\n\n[net#246]: https://github.com/actix/actix-net/pull/246\n\n## 0.2.5\n\n- Fix `from_hex()` method\n\n## 0.2.4\n\n- Add `ResourceDef::resource_path_named()` path generation method\n\n## 0.2.3\n\n- Add impl `IntoPattern` for `&String`\n\n## 0.2.2\n\n- Use `IntoPattern` for `RouterBuilder::path()`\n\n## 0.2.1\n\n- Add `IntoPattern` trait\n- Add multi-pattern resources\n\n## 0.2.0\n\n- Update http to 0.2\n- Update regex to 1.3\n- Use bytestring instead of string\n\n## 0.1.5\n\n- Remove debug prints\n\n## 0.1.4\n\n- Fix checked resource match\n\n## 0.1.3\n\n- Added support for `remainder match` (i.e \"/path/{tail}\\*\")\n\n## 0.1.2\n\n- Export `Quoter` type\n- Allow to reset `Path` instance\n\n## 0.1.1\n\n- Get dynamic segment by name instead of iterator.\n\n## 0.1.0\n\n- Initial release\n"
  },
  {
    "path": "actix-router/Cargo.toml",
    "content": "[package]\nname = \"actix-router\"\nversion = \"0.5.4\"\nauthors = [\n  \"Nikolay Kim <fafhrd91@gmail.com>\",\n  \"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>\",\n  \"Rob Ede <robjtede@icloud.com>\",\n]\ndescription = \"Resource path matching and router\"\nkeywords = [\"actix\", \"router\", \"routing\"]\nrepository = \"https://github.com/actix/actix-web\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\"http::*\", \"serde::*\"]\n\n[features]\ndefault = [\"http\", \"unicode\"]\nhttp = [\"dep:http\"]\nunicode = [\"dep:regex\"]\n\n[dependencies]\nbytestring = \">=0.1.5, <2\"\ncfg-if = \"1\"\nhttp = { version = \"0.2.7\", optional = true }\nregex = { version = \"1.5\", optional = true }\nregex-lite = \"0.1\"\nserde = \"1\"\ntracing = { version = \"0.1.30\", default-features = false, features = [\"log\"] }\n\n[dev-dependencies]\ncriterion = { version = \"0.5\", features = [\"html_reports\"] }\nhttp = \"0.2.7\"\npercent-encoding = \"2.1\"\nserde = { version = \"1\", features = [\"derive\"] }\n\n[lints]\nworkspace = true\n\n[[bench]]\nname = \"router\"\nharness = false\nrequired-features = [\"unicode\"]\n\n[[bench]]\nname = \"quoter\"\nharness = false\n"
  },
  {
    "path": "actix-router/README.md",
    "content": "# `actix-router`\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-router?label=latest)](https://crates.io/crates/actix-router)\n[![Documentation](https://docs.rs/actix-router/badge.svg?version=0.5.3)](https://docs.rs/actix-router/0.5.3)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-router.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-router/0.5.3/status.svg)](https://deps.rs/crate/actix-router/0.5.3)\n[![Download](https://img.shields.io/crates/d/actix-router.svg)](https://crates.io/crates/actix-router)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n<!-- cargo-rdme start -->\n\nResource path matching and router.\n\n<!-- cargo-rdme end -->\n"
  },
  {
    "path": "actix-router/benches/quoter.rs",
    "content": "use std::{borrow::Cow, fmt::Write as _};\n\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nfn compare_quoters(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"Compare Quoters\");\n\n    let quoter = actix_router::Quoter::new(b\"\", b\"\");\n    let path_quoted = (0..=0x7f).fold(String::new(), |mut buf, c| {\n        write!(&mut buf, \"%{:02X}\", c).unwrap();\n        buf\n    });\n    let path_unquoted = ('\\u{00}'..='\\u{7f}').collect::<String>();\n\n    group.bench_function(\"quoter_unquoted\", |b| {\n        b.iter(|| {\n            for _ in 0..10 {\n                black_box(quoter.requote(path_unquoted.as_bytes()));\n            }\n        });\n    });\n\n    group.bench_function(\"percent_encode_unquoted\", |b| {\n        b.iter(|| {\n            for _ in 0..10 {\n                let decode = percent_encoding::percent_decode(path_unquoted.as_bytes());\n                black_box(Into::<Cow<'_, [u8]>>::into(decode));\n            }\n        });\n    });\n\n    group.bench_function(\"quoter_quoted\", |b| {\n        b.iter(|| {\n            for _ in 0..10 {\n                black_box(quoter.requote(path_quoted.as_bytes()));\n            }\n        });\n    });\n\n    group.bench_function(\"percent_encode_quoted\", |b| {\n        b.iter(|| {\n            for _ in 0..10 {\n                let decode = percent_encoding::percent_decode(path_quoted.as_bytes());\n                black_box(Into::<Cow<'_, [u8]>>::into(decode));\n            }\n        });\n    });\n\n    group.finish();\n}\n\ncriterion_group!(benches, compare_quoters);\ncriterion_main!(benches);\n"
  },
  {
    "path": "actix-router/benches/router.rs",
    "content": "//! Based on https://github.com/ibraheemdev/matchit/blob/master/benches/bench.rs\n\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nmacro_rules! register {\n    (colon) => {{\n        register!(finish => \":p1\", \":p2\", \":p3\", \":p4\")\n    }};\n    (brackets) => {{\n        register!(finish => \"{p1}\", \"{p2}\", \"{p3}\", \"{p4}\")\n    }};\n    (regex) => {{\n        register!(finish => \"(.*)\", \"(.*)\", \"(.*)\", \"(.*)\")\n    }};\n    (finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{\n        #[expect(clippy::useless_concat)]\n        let arr = [\n            concat!(\"/authorizations\"),\n            concat!(\"/authorizations/\", $p1),\n            concat!(\"/applications/\", $p1, \"/tokens/\", $p2),\n            concat!(\"/events\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/events\"),\n            concat!(\"/networks/\", $p1, \"/\", $p2, \"/events\"),\n            concat!(\"/orgs/\", $p1, \"/events\"),\n            concat!(\"/users/\", $p1, \"/received_events\"),\n            concat!(\"/users/\", $p1, \"/received_events/public\"),\n            concat!(\"/users/\", $p1, \"/events\"),\n            concat!(\"/users/\", $p1, \"/events/public\"),\n            concat!(\"/users/\", $p1, \"/events/orgs/\", $p2),\n            concat!(\"/feeds\"),\n            concat!(\"/notifications\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/notifications\"),\n            concat!(\"/notifications/threads/\", $p1),\n            concat!(\"/notifications/threads/\", $p1, \"/subscription\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stargazers\"),\n            concat!(\"/users/\", $p1, \"/starred\"),\n            concat!(\"/user/starred\"),\n            concat!(\"/user/starred/\", $p1, \"/\", $p2),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/subscribers\"),\n            concat!(\"/users/\", $p1, \"/subscriptions\"),\n            concat!(\"/user/subscriptions\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/subscription\"),\n            concat!(\"/user/subscriptions/\", $p1, \"/\", $p2),\n            concat!(\"/users/\", $p1, \"/gists\"),\n            concat!(\"/gists\"),\n            concat!(\"/gists/\", $p1),\n            concat!(\"/gists/\", $p1, \"/star\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/git/blobs/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/git/commits/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/git/refs\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/git/tags/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/git/trees/\", $p3),\n            concat!(\"/issues\"),\n            concat!(\"/user/issues\"),\n            concat!(\"/orgs/\", $p1, \"/issues\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/issues\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/issues/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/assignees\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/assignees/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/issues/\", $p3, \"/comments\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/issues/\", $p3, \"/events\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/labels\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/labels/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/issues/\", $p3, \"/labels\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/milestones/\", $p3, \"/labels\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/milestones/\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/milestones/\", $p3),\n            concat!(\"/emojis\"),\n            concat!(\"/gitignore/templates\"),\n            concat!(\"/gitignore/templates/\", $p1),\n            concat!(\"/meta\"),\n            concat!(\"/rate_limit\"),\n            concat!(\"/users/\", $p1, \"/orgs\"),\n            concat!(\"/user/orgs\"),\n            concat!(\"/orgs/\", $p1),\n            concat!(\"/orgs/\", $p1, \"/members\"),\n            concat!(\"/orgs/\", $p1, \"/members\", $p2),\n            concat!(\"/orgs/\", $p1, \"/public_members\"),\n            concat!(\"/orgs/\", $p1, \"/public_members/\", $p2),\n            concat!(\"/orgs/\", $p1, \"/teams\"),\n            concat!(\"/teams/\", $p1),\n            concat!(\"/teams/\", $p1, \"/members\"),\n            concat!(\"/teams/\", $p1, \"/members\", $p2),\n            concat!(\"/teams/\", $p1, \"/repos\"),\n            concat!(\"/teams/\", $p1, \"/repos/\", $p2, \"/\", $p3),\n            concat!(\"/user/teams\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls/\", $p3, \"/commits\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls/\", $p3, \"/files\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls/\", $p3, \"/merge\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/pulls/\", $p3, \"/comments\"),\n            concat!(\"/user/repos\"),\n            concat!(\"/users/\", $p1, \"/repos\"),\n            concat!(\"/orgs/\", $p1, \"/repos\"),\n            concat!(\"/repositories\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/contributors\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/languages\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/teams\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/tags\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/branches\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/branches/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/collaborators\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/collaborators/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/comments\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/commits/\", $p3, \"/comments\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/commits\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/commits/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/readme\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/keys\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/keys\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/downloads\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/downloads\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/forks\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/hooks\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/hooks\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/releases\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/releases/\", $p3),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/releases/\", $p3, \"/assets\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stats/contributors\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stats/commit_activity\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stats/code_frequency\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stats/participation\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/stats/punch_card\"),\n            concat!(\"/repos/\", $p1, \"/\", $p2, \"/statuses/\", $p3),\n            concat!(\"/search/repositories\"),\n            concat!(\"/search/code\"),\n            concat!(\"/search/issues\"),\n            concat!(\"/search/users\"),\n            concat!(\"/legacy/issues/search/\", $p1, \"/\", $p2, \"/\", $p3, \"/\", $p4),\n            concat!(\"/legacy/repos/search/\", $p1),\n            concat!(\"/legacy/user/search/\", $p1),\n            concat!(\"/legacy/user/email/\", $p1),\n            concat!(\"/users/\", $p1),\n            concat!(\"/user\"),\n            concat!(\"/users\"),\n            concat!(\"/user/emails\"),\n            concat!(\"/users/\", $p1, \"/followers\"),\n            concat!(\"/user/followers\"),\n            concat!(\"/users/\", $p1, \"/following\"),\n            concat!(\"/user/following\"),\n            concat!(\"/user/following/\", $p1),\n            concat!(\"/users/\", $p1, \"/following\", $p2),\n            concat!(\"/users/\", $p1, \"/keys\"),\n            concat!(\"/user/keys\"),\n            concat!(\"/user/keys/\", $p1),\n        ];\n\n        IntoIterator::into_iter(arr)\n    }};\n}\n\nfn call() -> impl Iterator<Item = &'static str> {\n    let arr = [\n        \"/authorizations\",\n        \"/user/repos\",\n        \"/repos/rust-lang/rust/stargazers\",\n        \"/orgs/rust-lang/public_members/nikomatsakis\",\n        \"/repos/rust-lang/rust/releases/1.51.0\",\n    ];\n\n    IntoIterator::into_iter(arr)\n}\n\nfn compare_routers(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"Compare Routers\");\n\n    let mut actix = actix_router::Router::<bool>::build();\n    for route in register!(brackets) {\n        actix.path(route, true);\n    }\n    let actix = actix.finish();\n    group.bench_function(\"actix\", |b| {\n        b.iter(|| {\n            for route in call() {\n                let mut path = actix_router::Path::new(route);\n                black_box(actix.recognize(&mut path).unwrap());\n            }\n        });\n    });\n\n    let regex_set = regex::RegexSet::new(register!(regex)).unwrap();\n    group.bench_function(\"regex\", |b| {\n        b.iter(|| {\n            for route in call() {\n                black_box(regex_set.matches(route));\n            }\n        });\n    });\n\n    group.finish();\n}\n\ncriterion_group!(benches, compare_routers);\ncriterion_main!(benches);\n"
  },
  {
    "path": "actix-router/src/de.rs",
    "content": "use std::borrow::Cow;\n\nuse serde::{\n    de::{self, Deserializer, Error as DeError, Visitor},\n    forward_to_deserialize_any,\n};\n\nuse crate::{\n    path::{Path, PathIter},\n    Quoter, ResourcePath,\n};\n\nthread_local! {\n    static FULL_QUOTER: Quoter = Quoter::new(b\"\", b\"\");\n}\n\nmacro_rules! unsupported_type {\n    ($trait_fn:ident, $name:expr) => {\n        fn $trait_fn<V>(self, _: V) -> Result<V::Value, Self::Error>\n        where\n            V: Visitor<'de>,\n        {\n            Err(de::Error::custom(concat!(\"unsupported type: \", $name)))\n        }\n    };\n}\n\nmacro_rules! parse_single_value {\n    ($trait_fn:ident) => {\n        parse_single_value!($trait_fn, $trait_fn);\n    };\n    ($trait_fn:ident, $visit_fn:ident) => {\n        fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n        where\n            V: Visitor<'de>,\n        {\n            if self.path.segment_count() != 1 {\n                Err(de::value::Error::custom(\n                    format!(\n                        \"wrong number of parameters: {} expected 1\",\n                        self.path.segment_count()\n                    )\n                    .as_str(),\n                ))\n            } else {\n                Value {\n                    value: &self.path[0],\n                }\n                .$visit_fn(visitor)\n            }\n        }\n    };\n}\n\nmacro_rules! parse_value {\n    ($trait_fn:ident, $visit_fn:ident, $tp:tt) => {\n        fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n        where\n            V: Visitor<'de>,\n        {\n            let decoded = FULL_QUOTER\n                .with(|q| q.requote_str_lossy(self.value))\n                .map(Cow::Owned)\n                .unwrap_or(Cow::Borrowed(self.value));\n\n            let v = decoded.parse().map_err(|_| {\n                de::value::Error::custom(format!(\"can not parse {:?} to a {}\", self.value, $tp))\n            })?;\n\n            visitor.$visit_fn(v)\n        }\n    };\n}\n\npub struct PathDeserializer<'de, T: ResourcePath> {\n    path: &'de Path<T>,\n}\n\nimpl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> {\n    pub fn new(path: &'de Path<T>) -> Self {\n        PathDeserializer { path }\n    }\n}\n\nimpl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> {\n    type Error = de::value::Error;\n\n    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_map(ParamsDeserializer {\n            params: self.path.iter(),\n            current: None,\n        })\n    }\n\n    fn deserialize_struct<V>(\n        self,\n        _: &'static str,\n        _: &'static [&'static str],\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        self.deserialize_map(visitor)\n    }\n\n    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_unit()\n    }\n\n    fn deserialize_unit_struct<V>(\n        self,\n        _: &'static str,\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        self.deserialize_unit(visitor)\n    }\n\n    fn deserialize_newtype_struct<V>(\n        self,\n        _: &'static str,\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_newtype_struct(self)\n    }\n\n    fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        if self.path.segment_count() < len {\n            Err(de::value::Error::custom(\n                format!(\n                    \"wrong number of parameters: {} expected {}\",\n                    self.path.segment_count(),\n                    len\n                )\n                .as_str(),\n            ))\n        } else {\n            visitor.visit_seq(ParamsSeq {\n                params: self.path.iter(),\n            })\n        }\n    }\n\n    fn deserialize_tuple_struct<V>(\n        self,\n        _: &'static str,\n        len: usize,\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        if self.path.segment_count() < len {\n            Err(de::value::Error::custom(\n                format!(\n                    \"wrong number of parameters: {} expected {}\",\n                    self.path.segment_count(),\n                    len\n                )\n                .as_str(),\n            ))\n        } else {\n            visitor.visit_seq(ParamsSeq {\n                params: self.path.iter(),\n            })\n        }\n    }\n\n    fn deserialize_enum<V>(\n        self,\n        _: &'static str,\n        _: &'static [&'static str],\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        if self.path.is_empty() {\n            Err(de::value::Error::custom(\"expected at least one parameters\"))\n        } else {\n            visitor.visit_enum(ValueEnum {\n                value: &self.path[0],\n            })\n        }\n    }\n\n    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_seq(ParamsSeq {\n            params: self.path.iter(),\n        })\n    }\n\n    unsupported_type!(deserialize_option, \"Option<T>\");\n    unsupported_type!(deserialize_identifier, \"identifier\");\n    unsupported_type!(deserialize_ignored_any, \"ignored_any\");\n\n    parse_single_value!(deserialize_any);\n    parse_single_value!(deserialize_bool);\n    parse_single_value!(deserialize_i8);\n    parse_single_value!(deserialize_i16);\n    parse_single_value!(deserialize_i32);\n    parse_single_value!(deserialize_i64);\n    parse_single_value!(deserialize_u8);\n    parse_single_value!(deserialize_u16);\n    parse_single_value!(deserialize_u32);\n    parse_single_value!(deserialize_u64);\n    parse_single_value!(deserialize_f32);\n    parse_single_value!(deserialize_f64);\n    parse_single_value!(deserialize_str);\n    parse_single_value!(deserialize_string);\n    parse_single_value!(deserialize_bytes);\n    parse_single_value!(deserialize_byte_buf);\n    parse_single_value!(deserialize_char);\n}\n\nstruct ParamsDeserializer<'de, T: ResourcePath> {\n    params: PathIter<'de, T>,\n    current: Option<(&'de str, &'de str)>,\n}\n\nimpl<'de, T: ResourcePath> de::MapAccess<'de> for ParamsDeserializer<'de, T> {\n    type Error = de::value::Error;\n\n    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>\n    where\n        K: de::DeserializeSeed<'de>,\n    {\n        self.current = self.params.next().map(|ref item| (item.0, item.1));\n        match self.current {\n            Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)),\n            None => Ok(None),\n        }\n    }\n\n    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>\n    where\n        V: de::DeserializeSeed<'de>,\n    {\n        if let Some((_, value)) = self.current.take() {\n            seed.deserialize(Value { value })\n        } else {\n            Err(de::value::Error::custom(\"unexpected item\"))\n        }\n    }\n}\n\nstruct Key<'de> {\n    key: &'de str,\n}\n\nimpl<'de> Deserializer<'de> for Key<'de> {\n    type Error = de::value::Error;\n\n    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_str(self.key)\n    }\n\n    fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"Unexpected\"))\n    }\n\n    forward_to_deserialize_any! {\n        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes\n            byte_buf option unit unit_struct newtype_struct seq tuple\n            tuple_struct map struct enum ignored_any\n    }\n}\n\nstruct Value<'de> {\n    value: &'de str,\n}\n\nimpl<'de> Deserializer<'de> for Value<'de> {\n    type Error = de::value::Error;\n\n    parse_value!(deserialize_bool, visit_bool, \"bool\");\n    parse_value!(deserialize_i8, visit_i8, \"i8\");\n    parse_value!(deserialize_i16, visit_i16, \"i16\");\n    parse_value!(deserialize_i32, visit_i32, \"i32\");\n    parse_value!(deserialize_i64, visit_i64, \"i64\");\n    parse_value!(deserialize_u8, visit_u8, \"u8\");\n    parse_value!(deserialize_u16, visit_u16, \"u16\");\n    parse_value!(deserialize_u32, visit_u32, \"u32\");\n    parse_value!(deserialize_u64, visit_u64, \"u64\");\n    parse_value!(deserialize_f32, visit_f32, \"f32\");\n    parse_value!(deserialize_f64, visit_f64, \"f64\");\n    parse_value!(deserialize_char, visit_char, \"char\");\n\n    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_unit()\n    }\n\n    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_unit()\n    }\n\n    fn deserialize_unit_struct<V>(\n        self,\n        _: &'static str,\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_unit()\n    }\n\n    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        match FULL_QUOTER.with(|q| q.requote_str_lossy(self.value)) {\n            Some(s) => visitor.visit_string(s),\n            None => visitor.visit_borrowed_str(self.value),\n        }\n    }\n\n    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        match FULL_QUOTER.with(|q| q.requote_str_lossy(self.value)) {\n            Some(s) => visitor.visit_byte_buf(s.into()),\n            None => visitor.visit_borrowed_bytes(self.value.as_bytes()),\n        }\n    }\n\n    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        self.deserialize_bytes(visitor)\n    }\n\n    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        self.deserialize_str(visitor)\n    }\n\n    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_some(self)\n    }\n\n    fn deserialize_enum<V>(\n        self,\n        _: &'static str,\n        _: &'static [&'static str],\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_enum(ValueEnum { value: self.value })\n    }\n\n    fn deserialize_newtype_struct<V>(\n        self,\n        _: &'static str,\n        visitor: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        visitor.visit_newtype_struct(self)\n    }\n\n    fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"unsupported type: tuple\"))\n    }\n\n    fn deserialize_struct<V>(\n        self,\n        _: &'static str,\n        _: &'static [&'static str],\n        _: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"unsupported type: struct\"))\n    }\n\n    fn deserialize_tuple_struct<V>(\n        self,\n        _: &'static str,\n        _: usize,\n        _: V,\n    ) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"unsupported type: tuple struct\"))\n    }\n\n    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        let decoded = FULL_QUOTER\n            .with(|q| q.requote_str_lossy(self.value))\n            .map(Cow::Owned)\n            .unwrap_or(Cow::Borrowed(self.value));\n\n        let s = decoded.as_ref();\n        // We have to do it manually here on behalf of serde.\n        if let Ok(v) = s.parse::<u64>() {\n            if let Ok(v) = u32::try_from(v) {\n                return visitor.visit_u32(v);\n            }\n\n            return visitor.visit_u64(v);\n        }\n\n        if let Ok(v) = s.parse::<i64>() {\n            if let Ok(v) = i32::try_from(v) {\n                return visitor.visit_i32(v);\n            }\n\n            return visitor.visit_i64(v);\n        }\n\n        match decoded {\n            Cow::Borrowed(value) => visitor.visit_borrowed_str(value),\n            Cow::Owned(value) => visitor.visit_string(value),\n        }\n    }\n\n    unsupported_type!(deserialize_seq, \"seq\");\n    unsupported_type!(deserialize_map, \"map\");\n    unsupported_type!(deserialize_identifier, \"identifier\");\n}\n\nstruct ParamsSeq<'de, T: ResourcePath> {\n    params: PathIter<'de, T>,\n}\n\nimpl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> {\n    type Error = de::value::Error;\n\n    fn next_element_seed<U>(&mut self, seed: U) -> Result<Option<U::Value>, Self::Error>\n    where\n        U: de::DeserializeSeed<'de>,\n    {\n        match self.params.next() {\n            Some(item) => Ok(Some(seed.deserialize(Value { value: item.1 })?)),\n            None => Ok(None),\n        }\n    }\n}\n\nstruct ValueEnum<'de> {\n    value: &'de str,\n}\n\nimpl<'de> de::EnumAccess<'de> for ValueEnum<'de> {\n    type Error = de::value::Error;\n    type Variant = UnitVariant;\n\n    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>\n    where\n        V: de::DeserializeSeed<'de>,\n    {\n        Ok((seed.deserialize(Key { key: self.value })?, UnitVariant))\n    }\n}\n\nstruct UnitVariant;\n\nimpl<'de> de::VariantAccess<'de> for UnitVariant {\n    type Error = de::value::Error;\n\n    fn unit_variant(self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>\n    where\n        T: de::DeserializeSeed<'de>,\n    {\n        Err(de::value::Error::custom(\"not supported\"))\n    }\n\n    fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"not supported\"))\n    }\n\n    fn struct_variant<V>(self, _: &'static [&'static str], _: V) -> Result<V::Value, Self::Error>\n    where\n        V: Visitor<'de>,\n    {\n        Err(de::value::Error::custom(\"not supported\"))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use serde::Deserialize;\n\n    use super::*;\n    use crate::{router::Router, ResourceDef};\n\n    #[derive(Deserialize)]\n    struct MyStruct {\n        key: String,\n        value: String,\n    }\n\n    #[derive(Debug, Deserialize)]\n    struct Test1(String, u32);\n\n    #[derive(Debug, Deserialize)]\n    struct Test2 {\n        key: String,\n        value: u32,\n    }\n\n    #[derive(Debug, Deserialize, PartialEq)]\n    #[serde(rename_all = \"lowercase\")]\n    enum TestEnum {\n        Val1,\n        Val2,\n    }\n\n    #[derive(Debug, Deserialize)]\n    struct Test3 {\n        val: TestEnum,\n    }\n\n    #[test]\n    fn test_request_extract() {\n        let mut router = Router::<()>::build();\n        router.path(\"/{key}/{value}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/name/user1/\");\n        assert!(router.recognize(&mut path).is_some());\n\n        let s: MyStruct = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(s.key, \"name\");\n        assert_eq!(s.value, \"user1\");\n\n        let s: (String, String) =\n            de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(s.0, \"name\");\n        assert_eq!(s.1, \"user1\");\n\n        let mut router = Router::<()>::build();\n        router.path(\"/{key}/{value}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/name/32/\");\n        assert!(router.recognize(&mut path).is_some());\n\n        let s: Test1 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(s.0, \"name\");\n        assert_eq!(s.1, 32);\n\n        let s: Test2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(s.key, \"name\");\n        assert_eq!(s.value, 32);\n\n        let s: (String, u8) = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(s.0, \"name\");\n        assert_eq!(s.1, 32);\n\n        let res: Vec<String> = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(res[0], \"name\".to_owned());\n        assert_eq!(res[1], \"32\".to_owned());\n    }\n\n    #[test]\n    fn test_extract_path_single() {\n        let mut router = Router::<()>::build();\n        router.path(\"/{value}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/32/\");\n        assert!(router.recognize(&mut path).is_some());\n        let i: i8 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(i, 32);\n    }\n\n    #[test]\n    fn test_extract_enum() {\n        let mut router = Router::<()>::build();\n        router.path(\"/{val}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/val1/\");\n        assert!(router.recognize(&mut path).is_some());\n        let i: TestEnum = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(i, TestEnum::Val1);\n\n        let mut router = Router::<()>::build();\n        router.path(\"/{val1}/{val2}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/val1/val2/\");\n        assert!(router.recognize(&mut path).is_some());\n        let i: (TestEnum, TestEnum) =\n            de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(i, (TestEnum::Val1, TestEnum::Val2));\n    }\n\n    #[test]\n    fn test_extract_enum_value() {\n        let mut router = Router::<()>::build();\n        router.path(\"/{val}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/val1/\");\n        assert!(router.recognize(&mut path).is_some());\n        let i: Test3 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();\n        assert_eq!(i.val, TestEnum::Val1);\n\n        let mut path = Path::new(\"/val3/\");\n        assert!(router.recognize(&mut path).is_some());\n        let i: Result<Test3, de::value::Error> =\n            de::Deserialize::deserialize(PathDeserializer::new(&path));\n        assert!(i.is_err());\n        assert!(format!(\"{:?}\", i).contains(\"unknown variant\"));\n    }\n\n    #[test]\n    fn test_extract_errors() {\n        let mut router = Router::<()>::build();\n        router.path(\"/{value}/\", ());\n        let router = router.finish();\n\n        let mut path = Path::new(\"/name/\");\n        assert!(router.recognize(&mut path).is_some());\n\n        let s: Result<Test1, de::value::Error> =\n            de::Deserialize::deserialize(PathDeserializer::new(&path));\n        assert!(s.is_err());\n        assert!(format!(\"{:?}\", s).contains(\"wrong number of parameters\"));\n\n        let s: Result<Test2, de::value::Error> =\n            de::Deserialize::deserialize(PathDeserializer::new(&path));\n        assert!(s.is_err());\n        assert!(format!(\"{:?}\", s).contains(\"can not parse\"));\n\n        let s: Result<(String, String), de::value::Error> =\n            de::Deserialize::deserialize(PathDeserializer::new(&path));\n        assert!(s.is_err());\n        assert!(format!(\"{:?}\", s).contains(\"wrong number of parameters\"));\n\n        let s: Result<u32, de::value::Error> =\n            de::Deserialize::deserialize(PathDeserializer::new(&path));\n        assert!(s.is_err());\n        assert!(format!(\"{:?}\", s).contains(\"can not parse\"));\n    }\n\n    #[test]\n    fn deserialize_path_decode_string() {\n        let rdef = ResourceDef::new(\"/{key}\");\n\n        let mut path = Path::new(\"/%25\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: String = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment, \"%\");\n\n        let mut path = Path::new(\"/%2F\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: String = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment, \"/\")\n    }\n\n    #[test]\n    fn deserialize_path_decode_seq() {\n        let rdef = ResourceDef::new(\"/{key}/{value}\");\n\n        let mut path = Path::new(\"/%30%25/%30%2F\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: (String, String) = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment.0, \"0%\");\n        assert_eq!(segment.1, \"0/\");\n    }\n\n    #[test]\n    fn deserialize_path_decode_map() {\n        #[derive(Deserialize)]\n        struct Vals {\n            key: String,\n            value: String,\n        }\n\n        let rdef = ResourceDef::new(\"/{key}/{value}\");\n\n        let mut path = Path::new(\"/%25/%2F\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let vals: Vals = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(vals.key, \"%\");\n        assert_eq!(vals.value, \"/\");\n    }\n\n    #[test]\n    fn deserialize_path_decode_any() {\n        #[derive(Debug, PartialEq)]\n        pub enum AnyEnumCustom {\n            String(String),\n            Int(u32),\n            Other,\n        }\n\n        impl<'de> Deserialize<'de> for AnyEnumCustom {\n            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n            where\n                D: serde::Deserializer<'de>,\n            {\n                struct Vis;\n                impl<'de> Visitor<'de> for Vis {\n                    type Value = AnyEnumCustom;\n\n                    fn expecting<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {\n                        write!(f, \"my thing\")\n                    }\n\n                    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>\n                    where\n                        E: serde::de::Error,\n                    {\n                        Ok(AnyEnumCustom::Int(v))\n                    }\n\n                    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>\n                    where\n                        E: serde::de::Error,\n                    {\n                        match u32::try_from(v) {\n                            Ok(v) => Ok(AnyEnumCustom::Int(v)),\n                            Err(_) => Ok(AnyEnumCustom::String(format!(\"some str: {v}\"))),\n                        }\n                    }\n\n                    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>\n                    where\n                        E: serde::de::Error,\n                    {\n                        match u32::try_from(v) {\n                            Ok(v) => Ok(AnyEnumCustom::Int(v)),\n                            Err(_) => Ok(AnyEnumCustom::String(format!(\"some str: {v}\"))),\n                        }\n                    }\n\n                    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {\n                        v.parse().map(AnyEnumCustom::Int).or_else(|_| {\n                            Ok(match v {\n                                \"other\" => AnyEnumCustom::Other,\n                                _ => AnyEnumCustom::String(format!(\"some str: {v}\")),\n                            })\n                        })\n                    }\n                }\n\n                deserializer.deserialize_any(Vis)\n            }\n        }\n\n        #[derive(Debug, Deserialize, PartialEq)]\n        #[serde(untagged)]\n        pub enum AnyEnumDerive {\n            String(String),\n            Int(u32),\n            Other,\n        }\n\n        // single\n        let rdef = ResourceDef::new(\"/{key}\");\n\n        let mut path = Path::new(\"/%25\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: AnyEnumCustom = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment, AnyEnumCustom::String(\"some str: %\".to_string()));\n\n        let mut path = Path::new(\"/%25\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: AnyEnumDerive = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment, AnyEnumDerive::String(\"%\".to_string()));\n\n        // seq\n        let rdef = ResourceDef::new(\"/{key}/{value}\");\n\n        let mut path = Path::new(\"/other/123\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let segment: (AnyEnumCustom, AnyEnumDerive) = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(segment.0, AnyEnumCustom::Other);\n        assert_eq!(segment.1, AnyEnumDerive::Int(123));\n\n        // map\n        #[derive(Deserialize)]\n        struct Vals {\n            key: AnyEnumCustom,\n            value: AnyEnumDerive,\n        }\n\n        let rdef = ResourceDef::new(\"/{key}/{value}\");\n\n        let mut path = Path::new(\"/123/%2F\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let vals: Vals = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(vals.key, AnyEnumCustom::Int(123));\n        assert_eq!(vals.value, AnyEnumDerive::String(\"/\".to_string()));\n    }\n\n    #[test]\n    fn deserialize_borrowed() {\n        #[derive(Debug, Deserialize)]\n        struct Params<'a> {\n            val: &'a str,\n        }\n\n        let rdef = ResourceDef::new(\"/{val}\");\n\n        let mut path = Path::new(\"/X\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        let params: Params<'_> = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(params.val, \"X\");\n        let de = PathDeserializer::new(&path);\n        let params: &str = serde::Deserialize::deserialize(de).unwrap();\n        assert_eq!(params, \"X\");\n\n        let mut path = Path::new(\"/%2F\");\n        rdef.capture_match_info(&mut path);\n        let de = PathDeserializer::new(&path);\n        assert!(<Params<'_> as serde::Deserialize>::deserialize(de).is_err());\n        let de = PathDeserializer::new(&path);\n        assert!(<&str as serde::Deserialize>::deserialize(de).is_err());\n    }\n\n    // #[test]\n    // fn test_extract_path_decode() {\n    //     let mut router = Router::<()>::default();\n    //     router.register_resource(Resource::new(ResourceDef::new(\"/{value}/\")));\n\n    //     macro_rules! test_single_value {\n    //         ($value:expr, $expected:expr) => {{\n    //             let req = TestRequest::with_uri($value).finish();\n    //             let info = router.recognize(&req, &(), 0);\n    //             let req = req.with_route_info(info);\n    //             assert_eq!(\n    //                 *Path::<String>::from_request(&req, &PathConfig::default()).unwrap(),\n    //                 $expected\n    //             );\n    //         }};\n    //     }\n\n    //     test_single_value!(\"/%25/\", \"%\");\n    //     test_single_value!(\"/%40%C2%A3%24%25%5E%26%2B%3D/\", \"@£$%^&+=\");\n    //     test_single_value!(\"/%2B/\", \"+\");\n    //     test_single_value!(\"/%252B/\", \"%2B\");\n    //     test_single_value!(\"/%2F/\", \"/\");\n    //     test_single_value!(\"/%252F/\", \"%2F\");\n    //     test_single_value!(\n    //         \"/http%3A%2F%2Flocalhost%3A80%2Ffoo/\",\n    //         \"http://localhost:80/foo\"\n    //     );\n    //     test_single_value!(\"/%2Fvar%2Flog%2Fsyslog/\", \"/var/log/syslog\");\n    //     test_single_value!(\n    //         \"/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%252Fvar%252Flog%252Fsyslog/\",\n    //         \"http://localhost:80/file/%2Fvar%2Flog%2Fsyslog\"\n    //     );\n\n    //     let req = TestRequest::with_uri(\"/%25/7/?id=test\").finish();\n\n    //     let mut router = Router::<()>::default();\n    //     router.register_resource(Resource::new(ResourceDef::new(\"/{key}/{value}/\")));\n    //     let info = router.recognize(&req, &(), 0);\n    //     let req = req.with_route_info(info);\n\n    //     let s = Path::<Test2>::from_request(&req, &PathConfig::default()).unwrap();\n    //     assert_eq!(s.key, \"%\");\n    //     assert_eq!(s.value, 7);\n\n    //     let s = Path::<(String, String)>::from_request(&req, &PathConfig::default()).unwrap();\n    //     assert_eq!(s.0, \"%\");\n    //     assert_eq!(s.1, \"7\");\n    // }\n\n    // #[test]\n    // fn test_extract_path_no_decode() {\n    //     let mut router = Router::<()>::default();\n    //     router.register_resource(Resource::new(ResourceDef::new(\"/{value}/\")));\n\n    //     let req = TestRequest::with_uri(\"/%25/\").finish();\n    //     let info = router.recognize(&req, &(), 0);\n    //     let req = req.with_route_info(info);\n    //     assert_eq!(\n    //         *Path::<String>::from_request(&req, &&PathConfig::default().disable_decoding())\n    //             .unwrap(),\n    //         \"%25\"\n    //     );\n    // }\n}\n"
  },
  {
    "path": "actix-router/src/lib.rs",
    "content": "//! Resource path matching and router.\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\nmod de;\nmod path;\nmod pattern;\nmod quoter;\nmod regex_set;\nmod resource;\nmod resource_path;\nmod router;\n\n#[cfg(feature = \"http\")]\nmod url;\n\n#[cfg(feature = \"http\")]\npub use self::url::Url;\npub use self::{\n    de::PathDeserializer,\n    path::Path,\n    pattern::{IntoPatterns, Patterns},\n    quoter::Quoter,\n    resource::ResourceDef,\n    resource_path::{Resource, ResourcePath},\n    router::{ResourceId, Router, RouterBuilder},\n};\n"
  },
  {
    "path": "actix-router/src/path.rs",
    "content": "use std::{\n    borrow::Cow,\n    ops::{DerefMut, Index},\n};\n\nuse serde::{de, Deserialize};\n\nuse crate::{de::PathDeserializer, Resource, ResourcePath};\n\n#[derive(Debug, Clone)]\npub(crate) enum PathItem {\n    Static(Cow<'static, str>),\n    Segment(u16, u16),\n}\n\nimpl Default for PathItem {\n    fn default() -> Self {\n        Self::Static(Cow::Borrowed(\"\"))\n    }\n}\n\n/// Resource path match information.\n///\n/// If resource path contains variable patterns, `Path` stores them.\n#[derive(Debug, Clone, Default)]\npub struct Path<T> {\n    /// Full path representation.\n    path: T,\n\n    /// Number of characters in `path` that have been processed into `segments`.\n    pub(crate) skip: u16,\n\n    /// List of processed dynamic segments; name->value pairs.\n    pub(crate) segments: Vec<(Cow<'static, str>, PathItem)>,\n}\n\nimpl<T: ResourcePath> Path<T> {\n    pub fn new(path: T) -> Path<T> {\n        Path {\n            path,\n            skip: 0,\n            segments: Vec::new(),\n        }\n    }\n\n    /// Returns reference to inner path instance.\n    #[inline]\n    pub fn get_ref(&self) -> &T {\n        &self.path\n    }\n\n    /// Returns mutable reference to inner path instance.\n    #[inline]\n    pub fn get_mut(&mut self) -> &mut T {\n        &mut self.path\n    }\n\n    /// Returns full path as a string.\n    #[inline]\n    pub fn as_str(&self) -> &str {\n        self.path.path()\n    }\n\n    /// Returns unprocessed part of the path.\n    ///\n    /// Returns empty string if no more is to be processed.\n    #[inline]\n    pub fn unprocessed(&self) -> &str {\n        // clamp skip to path length\n        let skip = (self.skip as usize).min(self.as_str().len());\n        &self.path.path()[skip..]\n    }\n\n    /// Returns unprocessed part of the path.\n    #[doc(hidden)]\n    #[deprecated(since = \"0.6.0\", note = \"Use `.as_str()` or `.unprocessed()`.\")]\n    #[inline]\n    pub fn path(&self) -> &str {\n        let skip = self.skip as usize;\n        let path = self.path.path();\n        if skip <= path.len() {\n            &path[skip..]\n        } else {\n            \"\"\n        }\n    }\n\n    /// Set new path.\n    #[inline]\n    pub fn set(&mut self, path: T) {\n        self.path = path;\n        self.skip = 0;\n        self.segments.clear();\n    }\n\n    /// Set new path while preserving and remapping existing captured segment indices.\n    ///\n    /// The `reindex` closure maps byte indices from the previous path to byte indices in the new\n    /// path.\n    #[doc(hidden)]\n    pub fn update_with_reindex<F>(&mut self, path: T, mut reindex: F)\n    where\n        F: FnMut(u16) -> u16,\n    {\n        self.skip = reindex(self.skip);\n\n        for (_, item) in &mut self.segments {\n            if let PathItem::Segment(start, end) = item {\n                *start = reindex(*start);\n                *end = reindex(*end);\n\n                if *start > *end {\n                    *start = *end;\n                }\n            }\n        }\n\n        self.path = path;\n        let path = self.path.path();\n\n        self.skip = clamp_to_char_boundary(path, self.skip);\n\n        for (_, item) in &mut self.segments {\n            if let PathItem::Segment(start, end) = item {\n                *start = clamp_to_char_boundary(path, *start);\n                *end = clamp_to_char_boundary(path, *end);\n\n                if *start > *end {\n                    *start = *end;\n                }\n            }\n        }\n    }\n\n    /// Reset state.\n    #[inline]\n    pub fn reset(&mut self) {\n        self.skip = 0;\n        self.segments.clear();\n    }\n\n    /// Skip first `n` chars in path.\n    #[inline]\n    pub fn skip(&mut self, n: u16) {\n        self.skip += n;\n    }\n\n    pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) {\n        match value {\n            PathItem::Static(seg) => self.segments.push((name.into(), PathItem::Static(seg))),\n            PathItem::Segment(begin, end) => self.segments.push((\n                name.into(),\n                PathItem::Segment(self.skip + begin, self.skip + end),\n            )),\n        }\n    }\n\n    #[doc(hidden)]\n    pub fn add_static(\n        &mut self,\n        name: impl Into<Cow<'static, str>>,\n        value: impl Into<Cow<'static, str>>,\n    ) {\n        self.segments\n            .push((name.into(), PathItem::Static(value.into())));\n    }\n\n    /// Check if there are any matched patterns.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.segments.is_empty()\n    }\n\n    /// Returns number of interpolated segments.\n    #[inline]\n    pub fn segment_count(&self) -> usize {\n        self.segments.len()\n    }\n\n    /// Get matched parameter by name without type conversion\n    pub fn get(&self, name: &str) -> Option<&str> {\n        for (seg_name, val) in self.segments.iter() {\n            if name == seg_name {\n                return match val {\n                    PathItem::Static(ref seg) => Some(seg),\n                    PathItem::Segment(start, end) => {\n                        Some(&self.path.path()[(*start as usize)..(*end as usize)])\n                    }\n                };\n            }\n        }\n\n        None\n    }\n\n    /// Returns matched parameter by name.\n    ///\n    /// If keyed parameter is not available empty string is used as default value.\n    pub fn query(&self, key: &str) -> &str {\n        self.get(key).unwrap_or_default()\n    }\n\n    /// Return iterator to items in parameter container.\n    pub fn iter(&self) -> PathIter<'_, T> {\n        PathIter {\n            idx: 0,\n            params: self,\n        }\n    }\n\n    /// Deserializes matching parameters to a specified type `U`.\n    ///\n    /// # Errors\n    ///\n    /// Returns error when dynamic path segments cannot be deserialized into a `U` type.\n    pub fn load<'de, U: Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {\n        Deserialize::deserialize(PathDeserializer::new(self))\n    }\n}\n\nfn clamp_to_char_boundary(path: &str, idx: u16) -> u16 {\n    let mut idx = usize::from(idx).min(path.len());\n\n    while idx > 0 && !path.is_char_boundary(idx) {\n        idx -= 1;\n    }\n\n    idx as u16\n}\n\n#[derive(Debug)]\npub struct PathIter<'a, T> {\n    idx: usize,\n    params: &'a Path<T>,\n}\n\nimpl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {\n    type Item = (&'a str, &'a str);\n\n    #[inline]\n    fn next(&mut self) -> Option<(&'a str, &'a str)> {\n        if self.idx < self.params.segment_count() {\n            let idx = self.idx;\n            let res = match self.params.segments[idx].1 {\n                PathItem::Static(ref seg) => seg,\n                PathItem::Segment(start, end) => {\n                    &self.params.path.path()[(start as usize)..(end as usize)]\n                }\n            };\n            self.idx += 1;\n            return Some((&self.params.segments[idx].0, res));\n        }\n        None\n    }\n}\n\nimpl<'a, T: ResourcePath> Index<&'a str> for Path<T> {\n    type Output = str;\n\n    fn index(&self, name: &'a str) -> &str {\n        self.get(name)\n            .expect(\"Value for parameter is not available\")\n    }\n}\n\nimpl<T: ResourcePath> Index<usize> for Path<T> {\n    type Output = str;\n\n    fn index(&self, idx: usize) -> &str {\n        match self.segments[idx].1 {\n            PathItem::Static(ref seg) => seg,\n            PathItem::Segment(start, end) => &self.path.path()[(start as usize)..(end as usize)],\n        }\n    }\n}\n\nimpl<T: ResourcePath> Resource for Path<T> {\n    type Path = T;\n\n    fn resource_path(&mut self) -> &mut Path<Self::Path> {\n        self\n    }\n}\n\nimpl<T, P> Resource for T\nwhere\n    T: DerefMut<Target = Path<P>>,\n    P: ResourcePath,\n{\n    type Path = P;\n\n    fn resource_path(&mut self) -> &mut Path<Self::Path> {\n        &mut *self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::cell::RefCell;\n\n    use super::*;\n\n    #[allow(clippy::needless_borrow)]\n    #[test]\n    fn deref_impls() {\n        let mut foo = Path::new(\"/foo\");\n        let _ = (&mut foo).resource_path();\n\n        let foo = RefCell::new(foo);\n        let _ = foo.borrow_mut().resource_path();\n    }\n}\n"
  },
  {
    "path": "actix-router/src/pattern.rs",
    "content": "/// One or many patterns.\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub enum Patterns {\n    Single(String),\n    List(Vec<String>),\n}\n\nimpl Patterns {\n    pub fn is_empty(&self) -> bool {\n        match self {\n            Patterns::Single(_) => false,\n            Patterns::List(pats) => pats.is_empty(),\n        }\n    }\n}\n\n/// Helper trait for type that could be converted to one or more path patterns.\npub trait IntoPatterns {\n    fn patterns(&self) -> Patterns;\n}\n\nimpl IntoPatterns for String {\n    fn patterns(&self) -> Patterns {\n        Patterns::Single(self.clone())\n    }\n}\n\nimpl IntoPatterns for &String {\n    fn patterns(&self) -> Patterns {\n        (*self).patterns()\n    }\n}\n\nimpl IntoPatterns for str {\n    fn patterns(&self) -> Patterns {\n        Patterns::Single(self.to_owned())\n    }\n}\n\nimpl IntoPatterns for &str {\n    fn patterns(&self) -> Patterns {\n        (*self).patterns()\n    }\n}\n\nimpl IntoPatterns for bytestring::ByteString {\n    fn patterns(&self) -> Patterns {\n        Patterns::Single(self.to_string())\n    }\n}\n\nimpl IntoPatterns for Patterns {\n    fn patterns(&self) -> Patterns {\n        self.clone()\n    }\n}\n\nimpl<T: AsRef<str>> IntoPatterns for Vec<T> {\n    fn patterns(&self) -> Patterns {\n        let mut patterns = self.iter().map(|v| v.as_ref().to_owned());\n\n        match patterns.size_hint() {\n            (1, _) => Patterns::Single(patterns.next().unwrap()),\n            _ => Patterns::List(patterns.collect()),\n        }\n    }\n}\n\nmacro_rules! array_patterns_single (($tp:ty) => {\n    impl IntoPatterns for [$tp; 1] {\n        fn patterns(&self) -> Patterns {\n            Patterns::Single(self[0].to_owned())\n        }\n    }\n});\n\nmacro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => {\n    // for each array length specified in space-separated $num\n    $(\n        impl IntoPatterns for [$tp; $num] {\n            fn patterns(&self) -> Patterns {\n                Patterns::List(self.iter().map($str_fn).collect())\n            }\n        }\n    )+\n});\n\narray_patterns_single!(&str);\narray_patterns_multiple!(&str, |&v| v.to_owned(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);\n\narray_patterns_single!(String);\narray_patterns_multiple!(String, |v| v.clone(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);\n"
  },
  {
    "path": "actix-router/src/quoter.rs",
    "content": "/// Partial percent-decoding.\n///\n/// Performs percent-decoding on a slice but can selectively skip decoding certain sequences.\n///\n/// # Examples\n/// ```\n/// # use actix_router::Quoter;\n/// // + is set as a protected character and will not be decoded...\n/// let q = Quoter::new(&[], b\"+\");\n///\n/// // ...but the other encoded characters (like the hyphen below) will.\n/// assert_eq!(q.requote(b\"/a%2Db%2Bc\").unwrap(), b\"/a-b%2Bc\");\n/// ```\npub struct Quoter {\n    /// Simple bit-map of protected values in the 0-127 ASCII range.\n    protected_table: AsciiBitmap,\n}\n\nimpl Quoter {\n    /// Constructs a new `Quoter` instance given a set of protected ASCII bytes.\n    ///\n    /// The first argument is ignored but is kept for backward compatibility.\n    ///\n    /// # Panics\n    /// Panics if any of the `protected` bytes are not in the 0-127 ASCII range.\n    pub fn new(_: &[u8], protected: &[u8]) -> Quoter {\n        let mut protected_table = AsciiBitmap::default();\n\n        // prepare protected table\n        for &ch in protected {\n            protected_table.set_bit(ch);\n        }\n\n        Quoter { protected_table }\n    }\n\n    /// Decodes the next escape sequence, if any, and advances `val`.\n    #[inline(always)]\n    fn decode_next<'a>(&self, val: &mut &'a [u8]) -> Option<(&'a [u8], u8)> {\n        for i in 0..val.len() {\n            if let (prev, [b'%', p1, p2, rem @ ..]) = val.split_at(i) {\n                if let Some(ch) = hex_pair_to_char(*p1, *p2)\n                    // ignore protected ascii bytes\n                    .filter(|&ch| !(ch < 128 && self.protected_table.bit_at(ch)))\n                {\n                    *val = rem;\n                    return Some((prev, ch));\n                }\n            }\n        }\n\n        None\n    }\n\n    /// Partially percent-decodes the given bytes.\n    ///\n    /// Escape sequences of the protected set are *not* decoded.\n    ///\n    /// Returns `None` when no modification to the original bytes was required.\n    ///\n    /// Invalid/incomplete percent-encoding sequences are passed unmodified.\n    pub fn requote(&self, val: &[u8]) -> Option<Vec<u8>> {\n        let mut remaining = val;\n\n        // early return indicates that no percent-encoded sequences exist and we can skip allocation\n        let (pre, decoded_char) = self.decode_next(&mut remaining)?;\n\n        // decoded output will always be shorter than the input\n        let mut decoded = Vec::<u8>::with_capacity(val.len());\n\n        // push first segment and decoded char\n        decoded.extend_from_slice(pre);\n        decoded.push(decoded_char);\n\n        // decode and push rest of segments and decoded chars\n        while let Some((prev, ch)) = self.decode_next(&mut remaining) {\n            // this ugly conditional achieves +50% perf in cases where this is a tight loop.\n            if !prev.is_empty() {\n                decoded.extend_from_slice(prev);\n            }\n            decoded.push(ch);\n        }\n\n        decoded.extend_from_slice(remaining);\n\n        Some(decoded)\n    }\n\n    pub(crate) fn requote_str_lossy(&self, val: &str) -> Option<String> {\n        self.requote(val.as_bytes())\n            .map(|data| String::from_utf8_lossy(&data).into_owned())\n    }\n}\n\n/// Decode a ASCII hex-encoded pair to an integer.\n///\n/// Returns `None` if either portion of the decoded pair does not evaluate to a valid hex value.\n///\n/// - `0x33 ('3'), 0x30 ('0') => 0x30 ('0')`\n/// - `0x34 ('4'), 0x31 ('1') => 0x41 ('A')`\n/// - `0x36 ('6'), 0x31 ('1') => 0x61 ('a')`\n#[inline(always)]\nfn hex_pair_to_char(d1: u8, d2: u8) -> Option<u8> {\n    let d_high = char::from(d1).to_digit(16)?;\n    let d_low = char::from(d2).to_digit(16)?;\n\n    // left shift high nibble by 4 bits\n    Some(((d_high as u8) << 4) | (d_low as u8))\n}\n\n#[derive(Debug, Default, Clone)]\nstruct AsciiBitmap {\n    array: [u8; 16],\n}\n\nimpl AsciiBitmap {\n    /// Sets bit in given bit-map to 1=true.\n    ///\n    /// # Panics\n    /// Panics if `ch` index is out of bounds.\n    fn set_bit(&mut self, ch: u8) {\n        self.array[(ch >> 3) as usize] |= 0b1 << (ch & 0b111)\n    }\n\n    /// Returns true if bit to true in given bit-map.\n    ///\n    /// # Panics\n    /// Panics if `ch` index is out of bounds.\n    fn bit_at(&self, ch: u8) -> bool {\n        self.array[(ch >> 3) as usize] & (0b1 << (ch & 0b111)) != 0\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn custom_quoter() {\n        let q = Quoter::new(b\"\", b\"+\");\n        assert_eq!(q.requote(b\"/a%25c\").unwrap(), b\"/a%c\");\n        assert_eq!(q.requote(b\"/a%2Bc\"), None);\n\n        let q = Quoter::new(b\"%+\", b\"/\");\n        assert_eq!(q.requote(b\"/a%25b%2Bc\").unwrap(), b\"/a%b+c\");\n        assert_eq!(q.requote(b\"/a%2fb\"), None);\n        assert_eq!(q.requote(b\"/a%2Fb\"), None);\n        assert_eq!(q.requote(b\"/a%0Ab\").unwrap(), b\"/a\\nb\");\n        assert_eq!(q.requote(b\"/a%FE\\xffb\").unwrap(), b\"/a\\xfe\\xffb\");\n        assert_eq!(q.requote(b\"/a\\xfe\\xffb\"), None);\n    }\n\n    #[test]\n    fn non_ascii() {\n        let q = Quoter::new(b\"%+\", b\"/\");\n        assert_eq!(q.requote(b\"/a%FE\\xffb\").unwrap(), b\"/a\\xfe\\xffb\");\n        assert_eq!(q.requote(b\"/a\\xfe\\xffb\"), None);\n    }\n\n    #[test]\n    fn invalid_sequences() {\n        let q = Quoter::new(b\"%+\", b\"/\");\n        assert_eq!(q.requote(b\"/a%2x%2X%%\"), None);\n        assert_eq!(q.requote(b\"/a%20%2X%%\").unwrap(), b\"/a %2X%%\");\n    }\n\n    #[test]\n    fn quoter_no_modification() {\n        let q = Quoter::new(b\"\", b\"\");\n        assert_eq!(q.requote(b\"/abc/../efg\"), None);\n    }\n}\n"
  },
  {
    "path": "actix-router/src/regex_set.rs",
    "content": "//! Abstraction over `regex` and `regex-lite` depending on whether we have `unicode` crate feature\n//! enabled.\n\nuse cfg_if::cfg_if;\n#[cfg(feature = \"unicode\")]\npub(crate) use regex::{escape, Regex};\n#[cfg(not(feature = \"unicode\"))]\npub(crate) use regex_lite::{escape, Regex};\n\n#[cfg(feature = \"unicode\")]\n#[derive(Debug, Clone)]\npub(crate) struct RegexSet(regex::RegexSet);\n\n#[cfg(not(feature = \"unicode\"))]\n#[derive(Debug, Clone)]\npub(crate) struct RegexSet(Vec<regex_lite::Regex>);\n\nimpl RegexSet {\n    /// Create a new regex set.\n    ///\n    /// # Panics\n    ///\n    /// Panics if any path patterns are malformed.\n    pub(crate) fn new(re_set: Vec<String>) -> Self {\n        cfg_if! {\n            if #[cfg(feature = \"unicode\")] {\n                Self(regex::RegexSet::new(re_set).unwrap())\n            } else {\n                Self(re_set.iter().map(|re| Regex::new(re).unwrap()).collect())\n            }\n        }\n    }\n\n    /// Create a new empty regex set.\n    pub(crate) fn empty() -> Self {\n        cfg_if! {\n            if #[cfg(feature = \"unicode\")] {\n                Self(regex::RegexSet::empty())\n            } else {\n                Self(Vec::new())\n            }\n        }\n    }\n\n    /// Returns true if regex set matches `path`.\n    pub(crate) fn is_match(&self, path: &str) -> bool {\n        cfg_if! {\n            if #[cfg(feature = \"unicode\")] {\n                self.0.is_match(path)\n            } else {\n                self.0.iter().any(|re| re.is_match(path))\n            }\n        }\n    }\n\n    /// Returns index within `path` of first match.\n    pub(crate) fn first_match_idx(&self, path: &str) -> Option<usize> {\n        cfg_if! {\n            if #[cfg(feature = \"unicode\")] {\n                self.0.matches(path).into_iter().next()\n            } else {\n                Some(self.0.iter().enumerate().find(|(_, re)| re.is_match(path))?.0)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "actix-router/src/resource.rs",
    "content": "use std::{\n    borrow::{Borrow, Cow},\n    collections::HashMap,\n    hash::{BuildHasher, Hash, Hasher},\n    mem,\n};\n\nuse tracing::error;\n\nuse crate::{\n    path::PathItem,\n    regex_set::{escape, Regex, RegexSet},\n    IntoPatterns, Patterns, Resource, ResourcePath,\n};\n\nconst MAX_DYNAMIC_SEGMENTS: usize = 16;\n\n/// Regex flags to allow '.' in regex to match '\\n'\n///\n/// See the docs under: https://docs.rs/regex/1/regex/#grouping-and-flags\nconst REGEX_FLAGS: &str = \"(?s-m)\";\n\n/// Describes the set of paths that match to a resource.\n///\n/// `ResourceDef`s are effectively a way to transform the a custom resource pattern syntax into\n/// suitable regular expressions from which to check matches with paths and capture portions of a\n/// matched path into variables. Common cases are on a fast path that avoids going through the\n/// regex engine.\n///\n///\n/// # Pattern Format and Matching Behavior\n/// Resource pattern is defined as a string of zero or more _segments_ where each segment is\n/// preceded by a slash `/`.\n///\n/// This means that pattern string __must__ either be empty or begin with a slash (`/`). This also\n/// implies that a trailing slash in pattern defines an empty segment. For example, the pattern\n/// `\"/user/\"` has two segments: `[\"user\", \"\"]`\n///\n/// A key point to understand is that `ResourceDef` matches segments, not strings. Segments are\n/// matched individually. For example, the pattern `/user/` is not considered a prefix for the path\n/// `/user/123/456`, because the second segment doesn't match: `[\"user\", \"\"]`\n/// vs `[\"user\", \"123\", \"456\"]`.\n///\n/// This definition is consistent with the definition of absolute URL path in\n/// [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)\n///\n///\n/// # Static Resources\n/// A static resource is the most basic type of definition. Pass a pattern to [new][Self::new].\n/// Conforming paths must match the pattern exactly.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::ResourceDef;\n/// let resource = ResourceDef::new(\"/home\");\n///\n/// assert!(resource.is_match(\"/home\"));\n///\n/// assert!(!resource.is_match(\"/home/\"));\n/// assert!(!resource.is_match(\"/home/new\"));\n/// assert!(!resource.is_match(\"/homes\"));\n/// assert!(!resource.is_match(\"/search\"));\n/// ```\n///\n/// # Dynamic Segments\n/// Also known as \"path parameters\". Resources can define sections of a pattern that be extracted\n/// from a conforming path, if it conforms to (one of) the resource pattern(s).\n///\n/// The marker for a dynamic segment is curly braces wrapping an identifier. For example,\n/// `/user/{id}` would match paths like `/user/123` or `/user/james` and be able to extract the user\n/// IDs \"123\" and \"james\", respectively.\n///\n/// However, this resource pattern (`/user/{id}`) would, not cover `/user/123/stars` (unless\n/// constructed as a prefix; see next section) since the default pattern for segments matches all\n/// characters until it finds a `/` character (or the end of the path). Custom segment patterns are\n/// covered further down.\n///\n/// Dynamic segments do not need to be delimited by `/` characters, they can be defined within a\n/// path segment. For example, `/rust-is-{opinion}` can match the paths `/rust-is-cool` and\n/// `/rust-is-hard`.\n///\n/// For information on capturing segment values from paths or other custom resource types,\n/// see [`capture_match_info`][Self::capture_match_info]\n/// and [`capture_match_info_fn`][Self::capture_match_info_fn].\n///\n/// A resource definition can contain at most 16 dynamic segments.\n///\n/// ## Examples\n/// ```\n/// use actix_router::{Path, ResourceDef};\n///\n/// let resource = ResourceDef::prefix(\"/user/{id}\");\n///\n/// assert!(resource.is_match(\"/user/123\"));\n/// assert!(!resource.is_match(\"/user\"));\n/// assert!(!resource.is_match(\"/user/\"));\n///\n/// let mut path = Path::new(\"/user/123\");\n/// resource.capture_match_info(&mut path);\n/// assert_eq!(path.get(\"id\").unwrap(), \"123\");\n/// ```\n///\n/// # Prefix Resources\n/// A prefix resource is defined as pattern that can match just the start of a path, up to a\n/// segment boundary.\n///\n/// Prefix patterns with a trailing slash may have an unexpected, though correct, behavior.\n/// They define and therefore require an empty segment in order to match. It is easier to understand\n/// this behavior after reading the [matching behavior section]. Examples are given below.\n///\n/// The empty pattern (`\"\"`), as a prefix, matches any path.\n///\n/// Prefix resources can contain dynamic segments.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::ResourceDef;\n/// let resource = ResourceDef::prefix(\"/home\");\n/// assert!(resource.is_match(\"/home\"));\n/// assert!(resource.is_match(\"/home/new\"));\n/// assert!(!resource.is_match(\"/homes\"));\n///\n/// // prefix pattern with a trailing slash\n/// let resource = ResourceDef::prefix(\"/user/{id}/\");\n/// assert!(resource.is_match(\"/user/123/\"));\n/// assert!(resource.is_match(\"/user/123//stars\"));\n/// assert!(!resource.is_match(\"/user/123/stars\"));\n/// assert!(!resource.is_match(\"/user/123\"));\n/// ```\n///\n/// # Custom Regex Segments\n/// Dynamic segments can be customised to only match a specific regular expression. It can be\n/// helpful to do this if resource definitions would otherwise conflict and cause one to\n/// be inaccessible.\n///\n/// The regex used when capturing segment values can be specified explicitly using this syntax:\n/// `{name:regex}`. For example, `/user/{id:\\d+}` will only match paths where the user ID\n/// is numeric.\n///\n/// The regex could potentially match multiple segments. If this is not wanted, then care must be\n/// taken to avoid matching a slash `/`. It is guaranteed, however, that the match ends at a\n/// segment boundary; the pattern `r\"(/|$)` is always appended to the regex.\n///\n/// By default, dynamic segments use this regex: `[^/]+`. This shows why it is the case, as shown in\n/// the earlier section, that segments capture a slice of the path up to the next `/` character.\n///\n/// Custom regex segments can be used in static and prefix resource definition variants.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::ResourceDef;\n/// let resource = ResourceDef::new(r\"/user/{id:\\d+}\");\n/// assert!(resource.is_match(\"/user/123\"));\n/// assert!(resource.is_match(\"/user/314159\"));\n/// assert!(!resource.is_match(\"/user/abc\"));\n/// ```\n///\n/// # Tail Segments\n/// As a shortcut to defining a custom regex for matching _all_ remaining characters (not just those\n/// up until a `/` character), there is a special pattern to match (and capture) the remaining\n/// path portion.\n///\n/// To do this, use the segment pattern: `{name}*`. Since a tail segment also has a name, values are\n/// extracted in the same way as non-tail dynamic segments.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::{Path, ResourceDef};\n/// let resource = ResourceDef::new(\"/blob/{tail}*\");\n/// assert!(resource.is_match(\"/blob/HEAD/Cargo.toml\"));\n/// assert!(resource.is_match(\"/blob/HEAD/README.md\"));\n///\n/// let mut path = Path::new(\"/blob/main/LICENSE\");\n/// resource.capture_match_info(&mut path);\n/// assert_eq!(path.get(\"tail\").unwrap(), \"main/LICENSE\");\n/// ```\n///\n/// # Multi-Pattern Resources\n/// For resources that can map to multiple distinct paths, it may be suitable to use\n/// multi-pattern resources by passing an array/vec to [`new`][Self::new]. They will be combined\n/// into a regex set which is usually quicker to check matches on than checking each\n/// pattern individually.\n///\n/// Multi-pattern resources can contain dynamic segments just like single pattern ones.\n/// However, take care to use consistent and semantically-equivalent segment names; it could affect\n/// expectations in the router using these definitions and cause runtime panics.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::ResourceDef;\n/// let resource = ResourceDef::new([\"/home\", \"/index\"]);\n/// assert!(resource.is_match(\"/home\"));\n/// assert!(resource.is_match(\"/index\"));\n/// ```\n///\n/// # Trailing Slashes\n/// It should be noted that this library takes no steps to normalize intra-path or trailing slashes.\n/// As such, all resource definitions implicitly expect a pre-processing step to normalize paths if\n/// you wish to accommodate \"recoverable\" path errors. Below are several examples of resource-path\n/// pairs that would not be compatible.\n///\n/// ## Examples\n/// ```\n/// # use actix_router::ResourceDef;\n/// assert!(!ResourceDef::new(\"/root\").is_match(\"/root/\"));\n/// assert!(!ResourceDef::new(\"/root/\").is_match(\"/root\"));\n/// assert!(!ResourceDef::prefix(\"/root/\").is_match(\"/root\"));\n/// ```\n///\n/// [matching behavior section]: #pattern-format-and-matching-behavior\n#[derive(Clone, Debug)]\npub struct ResourceDef {\n    id: u16,\n\n    /// Optional name of resource.\n    name: Option<String>,\n\n    /// Pattern that generated the resource definition.\n    patterns: Patterns,\n\n    is_prefix: bool,\n\n    /// Pattern type.\n    pat_type: PatternType,\n\n    /// List of segments that compose the pattern, in order.\n    segments: Vec<PatternSegment>,\n}\n\n#[derive(Debug, Clone, PartialEq)]\nenum PatternSegment {\n    /// Literal slice of pattern.\n    Const(String),\n\n    /// Name of dynamic segment.\n    Var(String),\n}\n\n#[derive(Debug, Clone)]\n#[allow(clippy::large_enum_variant)]\nenum PatternType {\n    /// Single constant/literal segment.\n    Static(String),\n\n    /// Single regular expression and list of dynamic segment names.\n    Dynamic(Regex, Vec<&'static str>),\n\n    /// Regular expression set and list of component expressions plus dynamic segment names.\n    DynamicSet(RegexSet, Vec<(Regex, Vec<&'static str>)>),\n}\n\nimpl ResourceDef {\n    /// Constructs a new resource definition from patterns.\n    ///\n    /// Multi-pattern resources can be constructed by providing a slice (or vec) of patterns.\n    ///\n    /// # Panics\n    /// Panics if any path patterns are malformed.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::ResourceDef;\n    ///\n    /// let resource = ResourceDef::new(\"/user/{id}\");\n    /// assert!(resource.is_match(\"/user/123\"));\n    /// assert!(!resource.is_match(\"/user/123/stars\"));\n    /// assert!(!resource.is_match(\"user/1234\"));\n    /// assert!(!resource.is_match(\"/foo\"));\n    ///\n    /// let resource = ResourceDef::new([\"/profile\", \"/user/{id}\"]);\n    /// assert!(resource.is_match(\"/profile\"));\n    /// assert!(resource.is_match(\"/user/123\"));\n    /// assert!(!resource.is_match(\"user/123\"));\n    /// assert!(!resource.is_match(\"/foo\"));\n    /// ```\n    pub fn new<T: IntoPatterns>(paths: T) -> Self {\n        Self::construct(paths, false)\n    }\n\n    /// Constructs a new resource definition using a pattern that performs prefix matching.\n    ///\n    /// More specifically, the regular expressions generated for matching are different when using\n    /// this method vs using `new`; they will not be appended with the `$` meta-character that\n    /// matches the end of an input.\n    ///\n    /// Although it will compile and run correctly, it is meaningless to construct a prefix\n    /// resource definition with a tail segment; use [`new`][Self::new] in this case.\n    ///\n    /// # Panics\n    /// Panics if path pattern is malformed.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::ResourceDef;\n    ///\n    /// let resource = ResourceDef::prefix(\"/user/{id}\");\n    /// assert!(resource.is_match(\"/user/123\"));\n    /// assert!(resource.is_match(\"/user/123/stars\"));\n    /// assert!(!resource.is_match(\"user/123\"));\n    /// assert!(!resource.is_match(\"user/123/stars\"));\n    /// assert!(!resource.is_match(\"/foo\"));\n    /// ```\n    pub fn prefix<T: IntoPatterns>(paths: T) -> Self {\n        ResourceDef::construct(paths, true)\n    }\n\n    /// Constructs a new resource definition using a string pattern that performs prefix matching,\n    /// ensuring a leading `/` if pattern is not empty.\n    ///\n    /// # Panics\n    /// Panics if path pattern is malformed.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::ResourceDef;\n    ///\n    /// let resource = ResourceDef::root_prefix(\"user/{id}\");\n    ///\n    /// assert_eq!(&resource, &ResourceDef::prefix(\"/user/{id}\"));\n    /// assert_eq!(&resource, &ResourceDef::root_prefix(\"/user/{id}\"));\n    /// assert_ne!(&resource, &ResourceDef::new(\"user/{id}\"));\n    /// assert_ne!(&resource, &ResourceDef::new(\"/user/{id}\"));\n    ///\n    /// assert!(resource.is_match(\"/user/123\"));\n    /// assert!(!resource.is_match(\"user/123\"));\n    /// ```\n    pub fn root_prefix(path: &str) -> Self {\n        ResourceDef::prefix(insert_slash(path).into_owned())\n    }\n\n    /// Returns a numeric resource ID.\n    ///\n    /// If not explicitly set using [`set_id`][Self::set_id], this will return `0`.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/root\");\n    /// assert_eq!(resource.id(), 0);\n    ///\n    /// resource.set_id(42);\n    /// assert_eq!(resource.id(), 42);\n    /// ```\n    pub fn id(&self) -> u16 {\n        self.id\n    }\n\n    /// Set numeric resource ID.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/root\");\n    /// resource.set_id(42);\n    /// assert_eq!(resource.id(), 42);\n    /// ```\n    pub fn set_id(&mut self, id: u16) {\n        self.id = id;\n    }\n\n    /// Returns resource definition name, if set.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/root\");\n    /// assert!(resource.name().is_none());\n    ///\n    /// resource.set_name(\"root\");\n    /// assert_eq!(resource.name().unwrap(), \"root\");\n    pub fn name(&self) -> Option<&str> {\n        self.name.as_deref()\n    }\n\n    /// Assigns a new name to the resource.\n    ///\n    /// # Panics\n    /// Panics if `name` is an empty string.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/root\");\n    /// resource.set_name(\"root\");\n    /// assert_eq!(resource.name().unwrap(), \"root\");\n    /// ```\n    pub fn set_name(&mut self, name: impl Into<String>) {\n        let name = name.into();\n\n        assert!(!name.is_empty(), \"resource name should not be empty\");\n\n        self.name = Some(name)\n    }\n\n    /// Returns `true` if pattern type is prefix.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// assert!(ResourceDef::prefix(\"/user\").is_prefix());\n    /// assert!(!ResourceDef::new(\"/user\").is_prefix());\n    /// ```\n    pub fn is_prefix(&self) -> bool {\n        self.is_prefix\n    }\n\n    /// Returns the pattern string that generated the resource definition.\n    ///\n    /// If definition is constructed with multiple patterns, the first pattern is returned. To get\n    /// all patterns, use [`patterns_iter`][Self::pattern_iter]. If resource has 0 patterns,\n    /// returns `None`.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/user/{id}\");\n    /// assert_eq!(resource.pattern().unwrap(), \"/user/{id}\");\n    ///\n    /// let mut resource = ResourceDef::new([\"/profile\", \"/user/{id}\"]);\n    /// assert_eq!(resource.pattern(), Some(\"/profile\"));\n    pub fn pattern(&self) -> Option<&str> {\n        match &self.patterns {\n            Patterns::Single(pattern) => Some(pattern.as_str()),\n            Patterns::List(patterns) => patterns.first().map(AsRef::as_ref),\n        }\n    }\n\n    /// Returns iterator of pattern strings that generated the resource definition.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut resource = ResourceDef::new(\"/root\");\n    /// let mut iter = resource.pattern_iter();\n    /// assert_eq!(iter.next().unwrap(), \"/root\");\n    /// assert!(iter.next().is_none());\n    ///\n    /// let mut resource = ResourceDef::new([\"/root\", \"/backup\"]);\n    /// let mut iter = resource.pattern_iter();\n    /// assert_eq!(iter.next().unwrap(), \"/root\");\n    /// assert_eq!(iter.next().unwrap(), \"/backup\");\n    /// assert!(iter.next().is_none());\n    pub fn pattern_iter(&self) -> impl Iterator<Item = &str> {\n        struct PatternIter<'a> {\n            patterns: &'a Patterns,\n            list_idx: usize,\n            done: bool,\n        }\n\n        impl<'a> Iterator for PatternIter<'a> {\n            type Item = &'a str;\n\n            fn next(&mut self) -> Option<Self::Item> {\n                match &self.patterns {\n                    Patterns::Single(pattern) => {\n                        if self.done {\n                            return None;\n                        }\n\n                        self.done = true;\n                        Some(pattern.as_str())\n                    }\n                    Patterns::List(patterns) if patterns.is_empty() => None,\n                    Patterns::List(patterns) => match patterns.get(self.list_idx) {\n                        Some(pattern) => {\n                            self.list_idx += 1;\n                            Some(pattern.as_str())\n                        }\n                        None => {\n                            // fast path future call\n                            self.done = true;\n                            None\n                        }\n                    },\n                }\n            }\n\n            fn size_hint(&self) -> (usize, Option<usize>) {\n                match &self.patterns {\n                    Patterns::Single(_) => (1, Some(1)),\n                    Patterns::List(patterns) => (patterns.len(), Some(patterns.len())),\n                }\n            }\n        }\n\n        PatternIter {\n            patterns: &self.patterns,\n            list_idx: 0,\n            done: false,\n        }\n    }\n\n    /// Joins two resources.\n    ///\n    /// Resulting resource is prefix if `other` is prefix.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let joined = ResourceDef::prefix(\"/root\").join(&ResourceDef::prefix(\"/seg\"));\n    /// assert_eq!(joined, ResourceDef::prefix(\"/root/seg\"));\n    /// ```\n    pub fn join(&self, other: &ResourceDef) -> ResourceDef {\n        let patterns = self\n            .pattern_iter()\n            .flat_map(move |this| other.pattern_iter().map(move |other| (this, other)))\n            .map(|(this, other)| {\n                let mut pattern = String::with_capacity(this.len() + other.len());\n                pattern.push_str(this);\n                pattern.push_str(other);\n                pattern\n            })\n            .collect::<Vec<_>>();\n\n        match patterns.len() {\n            1 => ResourceDef::construct(&patterns[0], other.is_prefix()),\n            _ => ResourceDef::construct(patterns, other.is_prefix()),\n        }\n    }\n\n    /// Returns `true` if `path` matches this resource.\n    ///\n    /// The behavior of this method depends on how the `ResourceDef` was constructed. For example,\n    /// static resources will not be able to match as many paths as dynamic and prefix resources.\n    /// See [`ResourceDef`] struct docs for details on resource definition types.\n    ///\n    /// This method will always agree with [`find_match`][Self::find_match] on whether the path\n    /// matches or not.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::ResourceDef;\n    ///\n    /// // static resource\n    /// let resource = ResourceDef::new(\"/user\");\n    /// assert!(resource.is_match(\"/user\"));\n    /// assert!(!resource.is_match(\"/users\"));\n    /// assert!(!resource.is_match(\"/user/123\"));\n    /// assert!(!resource.is_match(\"/foo\"));\n    ///\n    /// // dynamic resource\n    /// let resource = ResourceDef::new(\"/user/{user_id}\");\n    /// assert!(resource.is_match(\"/user/123\"));\n    /// assert!(!resource.is_match(\"/user/123/stars\"));\n    ///\n    /// // prefix resource\n    /// let resource = ResourceDef::prefix(\"/root\");\n    /// assert!(resource.is_match(\"/root\"));\n    /// assert!(resource.is_match(\"/root/leaf\"));\n    /// assert!(!resource.is_match(\"/roots\"));\n    ///\n    /// // more examples are shown in the `ResourceDef` struct docs\n    /// ```\n    #[inline]\n    pub fn is_match(&self, path: &str) -> bool {\n        // this function could be expressed as:\n        // `self.find_match(path).is_some()`\n        // but this skips some checks and uses potentially faster regex methods\n\n        match &self.pat_type {\n            PatternType::Static(pattern) => self.static_match(pattern, path).is_some(),\n            PatternType::Dynamic(re, _) => re.is_match(path),\n            PatternType::DynamicSet(re, _) => re.is_match(path),\n        }\n    }\n\n    /// Tries to match `path` to this resource, returning the position in the path where the\n    /// match ends.\n    ///\n    /// This method will always agree with [`is_match`][Self::is_match] on whether the path matches\n    /// or not.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::ResourceDef;\n    ///\n    /// // static resource\n    /// let resource = ResourceDef::new(\"/user\");\n    /// assert_eq!(resource.find_match(\"/user\"), Some(5));\n    /// assert!(resource.find_match(\"/user/\").is_none());\n    /// assert!(resource.find_match(\"/user/123\").is_none());\n    /// assert!(resource.find_match(\"/foo\").is_none());\n    ///\n    /// // constant prefix resource\n    /// let resource = ResourceDef::prefix(\"/user\");\n    /// assert_eq!(resource.find_match(\"/user\"), Some(5));\n    /// assert_eq!(resource.find_match(\"/user/\"), Some(5));\n    /// assert_eq!(resource.find_match(\"/user/123\"), Some(5));\n    ///\n    /// // dynamic prefix resource\n    /// let resource = ResourceDef::prefix(\"/user/{id}\");\n    /// assert_eq!(resource.find_match(\"/user/123\"), Some(9));\n    /// assert_eq!(resource.find_match(\"/user/1234/\"), Some(10));\n    /// assert_eq!(resource.find_match(\"/user/12345/stars\"), Some(11));\n    /// assert!(resource.find_match(\"/user/\").is_none());\n    ///\n    /// // multi-pattern resource\n    /// let resource = ResourceDef::new([\"/user/{id}\", \"/profile/{id}\"]);\n    /// assert_eq!(resource.find_match(\"/user/123\"), Some(9));\n    /// assert_eq!(resource.find_match(\"/profile/1234\"), Some(13));\n    /// ```\n    pub fn find_match(&self, path: &str) -> Option<usize> {\n        match &self.pat_type {\n            PatternType::Static(pattern) => self.static_match(pattern, path),\n\n            PatternType::Dynamic(re, _) => Some(re.captures(path)?[1].len()),\n\n            PatternType::DynamicSet(re, params) => {\n                let idx = re.first_match_idx(path)?;\n                let (ref pattern, _) = params[idx];\n                Some(pattern.captures(path)?[1].len())\n            }\n        }\n    }\n\n    /// Collects dynamic segment values into `resource`.\n    ///\n    /// Returns `true` if `path` matches this resource.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::{Path, ResourceDef};\n    ///\n    /// let resource = ResourceDef::prefix(\"/user/{id}\");\n    /// let mut path = Path::new(\"/user/123/stars\");\n    /// assert!(resource.capture_match_info(&mut path));\n    /// assert_eq!(path.get(\"id\").unwrap(), \"123\");\n    /// assert_eq!(path.unprocessed(), \"/stars\");\n    ///\n    /// let resource = ResourceDef::new(\"/blob/{path}*\");\n    /// let mut path = Path::new(\"/blob/HEAD/Cargo.toml\");\n    /// assert!(resource.capture_match_info(&mut path));\n    /// assert_eq!(path.get(\"path\").unwrap(), \"HEAD/Cargo.toml\");\n    /// assert_eq!(path.unprocessed(), \"\");\n    /// ```\n    pub fn capture_match_info<R: Resource>(&self, resource: &mut R) -> bool {\n        self.capture_match_info_fn(resource, |_| true)\n    }\n\n    /// Collects dynamic segment values into `resource` after matching paths and executing\n    /// check function.\n    ///\n    /// The check function is given a reference to the passed resource and optional arbitrary data.\n    /// This is useful if you want to conditionally match on some non-path related aspect of the\n    /// resource type.\n    ///\n    /// Returns `true` if resource path matches this resource definition _and_ satisfies the\n    /// given check function.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_router::{Path, ResourceDef};\n    ///\n    /// fn try_match(resource: &ResourceDef, path: &mut Path<&str>) -> bool {\n    ///     let admin_allowed = std::env::var(\"ADMIN_ALLOWED\").is_ok();\n    ///\n    ///     resource.capture_match_info_fn(\n    ///         path,\n    ///         // when env var is not set, reject when path contains \"admin\"\n    ///         |path| !(!admin_allowed && path.as_str().contains(\"admin\")),\n    ///     )\n    /// }\n    ///\n    /// let resource = ResourceDef::prefix(\"/user/{id}\");\n    ///\n    /// // path matches; segment values are collected into path\n    /// let mut path = Path::new(\"/user/james/stars\");\n    /// assert!(try_match(&resource, &mut path));\n    /// assert_eq!(path.get(\"id\").unwrap(), \"james\");\n    /// assert_eq!(path.unprocessed(), \"/stars\");\n    ///\n    /// // path matches but fails check function; no segments are collected\n    /// let mut path = Path::new(\"/user/admin/stars\");\n    /// assert!(!try_match(&resource, &mut path));\n    /// assert_eq!(path.unprocessed(), \"/user/admin/stars\");\n    /// ```\n    pub fn capture_match_info_fn<R, F>(&self, resource: &mut R, check_fn: F) -> bool\n    where\n        R: Resource,\n        F: FnOnce(&R) -> bool,\n    {\n        let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default();\n        let path = resource.resource_path();\n        let path_str = path.unprocessed();\n\n        let (matched_len, matched_vars) = match &self.pat_type {\n            PatternType::Static(pattern) => match self.static_match(pattern, path_str) {\n                Some(len) => (len, None),\n                None => return false,\n            },\n\n            PatternType::Dynamic(re, names) => {\n                let captures = match re.captures(path.unprocessed()) {\n                    Some(captures) => captures,\n                    _ => return false,\n                };\n\n                for (no, name) in names.iter().enumerate() {\n                    if let Some(m) = captures.name(name) {\n                        segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);\n                    } else {\n                        error!(\"Dynamic path match but not all segments found: {}\", name);\n                        return false;\n                    }\n                }\n\n                (captures[1].len(), Some(names))\n            }\n\n            PatternType::DynamicSet(re, params) => {\n                let path = path.unprocessed();\n                let (pattern, names) = match re.first_match_idx(path) {\n                    Some(idx) => &params[idx],\n                    _ => return false,\n                };\n\n                let captures = match pattern.captures(path.path()) {\n                    Some(captures) => captures,\n                    _ => return false,\n                };\n\n                for (no, name) in names.iter().enumerate() {\n                    if let Some(m) = captures.name(name) {\n                        segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);\n                    } else {\n                        error!(\"Dynamic path match but not all segments found: {}\", name);\n                        return false;\n                    }\n                }\n\n                (captures[1].len(), Some(names))\n            }\n        };\n\n        if !check_fn(resource) {\n            return false;\n        }\n\n        // Modify `path` to skip matched part and store matched segments\n        let path = resource.resource_path();\n\n        if let Some(vars) = matched_vars {\n            for i in 0..vars.len() {\n                path.add(vars[i], mem::take(&mut segments[i]));\n            }\n        }\n\n        path.skip(matched_len as u16);\n\n        true\n    }\n\n    /// Assembles resource path using a closure that maps variable segment names to values.\n    fn build_resource_path<F, I>(&self, path: &mut String, mut vars: F) -> bool\n    where\n        F: FnMut(&str) -> Option<I>,\n        I: AsRef<str>,\n    {\n        for segment in &self.segments {\n            match segment {\n                PatternSegment::Const(val) => path.push_str(val),\n                PatternSegment::Var(name) => match vars(name) {\n                    Some(val) => path.push_str(val.as_ref()),\n                    _ => return false,\n                },\n            }\n        }\n\n        true\n    }\n\n    /// Assembles full resource path from iterator of dynamic segment values.\n    ///\n    /// Returns `true` on success.\n    ///\n    /// For multi-pattern resources, the first pattern is used under the assumption that it would be\n    /// equivalent to any other choice.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_router::ResourceDef;\n    /// let mut s = String::new();\n    /// let resource = ResourceDef::new(\"/user/{id}/post/{title}\");\n    ///\n    /// assert!(resource.resource_path_from_iter(&mut s, &[\"123\", \"my-post\"]));\n    /// assert_eq!(s, \"/user/123/post/my-post\");\n    /// ```\n    pub fn resource_path_from_iter<I>(&self, path: &mut String, values: I) -> bool\n    where\n        I: IntoIterator,\n        I::Item: AsRef<str>,\n    {\n        let mut iter = values.into_iter();\n        self.build_resource_path(path, |_| iter.next())\n    }\n\n    /// Assembles resource path from map of dynamic segment values.\n    ///\n    /// Returns `true` on success.\n    ///\n    /// For multi-pattern resources, the first pattern is used under the assumption that it would be\n    /// equivalent to any other choice.\n    ///\n    /// # Examples\n    /// ```\n    /// # use std::collections::HashMap;\n    /// # use actix_router::ResourceDef;\n    /// let mut s = String::new();\n    /// let resource = ResourceDef::new(\"/user/{id}/post/{title}\");\n    ///\n    /// let mut map = HashMap::new();\n    /// map.insert(\"id\", \"123\");\n    /// map.insert(\"title\", \"my-post\");\n    ///\n    /// assert!(resource.resource_path_from_map(&mut s, &map));\n    /// assert_eq!(s, \"/user/123/post/my-post\");\n    /// ```\n    pub fn resource_path_from_map<K, V, S>(\n        &self,\n        path: &mut String,\n        values: &HashMap<K, V, S>,\n    ) -> bool\n    where\n        K: Borrow<str> + Eq + Hash,\n        V: AsRef<str>,\n        S: BuildHasher,\n    {\n        self.build_resource_path(path, |name| values.get(name))\n    }\n\n    /// Returns true if `prefix` acts as a proper prefix (i.e., separated by a slash) in `path`.\n    fn static_match(&self, pattern: &str, path: &str) -> Option<usize> {\n        let rem = path.strip_prefix(pattern)?;\n\n        match self.is_prefix {\n            // resource is not a prefix so an exact match is needed\n            false if rem.is_empty() => Some(pattern.len()),\n\n            // resource is a prefix so rem should start with a path delimiter\n            true if rem.is_empty() || rem.starts_with('/') => Some(pattern.len()),\n\n            // otherwise, no match\n            _ => None,\n        }\n    }\n\n    fn construct<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self {\n        let patterns = paths.patterns();\n\n        let (pat_type, segments) = match &patterns {\n            Patterns::Single(pattern) => ResourceDef::parse(pattern, is_prefix, false),\n\n            // since zero length pattern sets are possible\n            // just return a useless `ResourceDef`\n            Patterns::List(patterns) if patterns.is_empty() => (\n                PatternType::DynamicSet(RegexSet::empty(), Vec::new()),\n                Vec::new(),\n            ),\n\n            Patterns::List(patterns) => {\n                let mut re_set = Vec::with_capacity(patterns.len());\n                let mut pattern_data = Vec::new();\n                let mut segments = None;\n\n                for pattern in patterns {\n                    match ResourceDef::parse(pattern, is_prefix, true) {\n                        (PatternType::Dynamic(re, names), segs) => {\n                            re_set.push(re.as_str().to_owned());\n                            pattern_data.push((re, names));\n                            segments.get_or_insert(segs);\n                        }\n                        _ => unreachable!(),\n                    }\n                }\n\n                let pattern_re_set = RegexSet::new(re_set);\n                let segments = segments.unwrap_or_default();\n\n                (\n                    PatternType::DynamicSet(pattern_re_set, pattern_data),\n                    segments,\n                )\n            }\n        };\n\n        ResourceDef {\n            id: 0,\n            name: None,\n            patterns,\n            is_prefix,\n            pat_type,\n            segments,\n        }\n    }\n\n    /// Parses a dynamic segment definition from a pattern.\n    ///\n    /// The returned tuple includes:\n    /// - the segment descriptor, either `Var` or `Tail`\n    /// - the segment's regex to check values against\n    /// - the remaining, unprocessed string slice\n    /// - whether the parsed parameter represents a tail pattern\n    ///\n    /// # Panics\n    /// Panics if given patterns does not contain a dynamic segment.\n    fn parse_param(pattern: &str) -> (PatternSegment, String, &str, bool) {\n        const DEFAULT_PATTERN: &str = \"[^/]+\";\n        const DEFAULT_PATTERN_TAIL: &str = \".*\";\n\n        let mut params_nesting = 0usize;\n        let close_idx = pattern\n            .find(|c| match c {\n                '{' => {\n                    params_nesting += 1;\n                    false\n                }\n                '}' => {\n                    params_nesting -= 1;\n                    params_nesting == 0\n                }\n                _ => false,\n            })\n            .unwrap_or_else(|| {\n                panic!(\n                    r#\"pattern \"{}\" contains malformed dynamic segment\"#,\n                    pattern\n                )\n            });\n\n        let (mut param, mut unprocessed) = pattern.split_at(close_idx + 1);\n\n        // remove outer curly brackets\n        param = &param[1..param.len() - 1];\n\n        let tail = unprocessed == \"*\";\n\n        let (name, pattern) = match param.find(':') {\n            Some(idx) => {\n                assert!(!tail, \"custom regex is not supported for tail match\");\n\n                let (name, pattern) = param.split_at(idx);\n                (name, &pattern[1..])\n            }\n            None => (\n                param,\n                if tail {\n                    unprocessed = &unprocessed[1..];\n                    DEFAULT_PATTERN_TAIL\n                } else {\n                    DEFAULT_PATTERN\n                },\n            ),\n        };\n\n        let segment = PatternSegment::Var(name.to_string());\n        let regex = format!(r\"(?P<{}>{})\", &name, &pattern);\n\n        (segment, regex, unprocessed, tail)\n    }\n\n    /// Parse `pattern` using `is_prefix` and `force_dynamic` flags.\n    ///\n    /// Parameters:\n    /// - `is_prefix`: Use `true` if `pattern` should be treated as a prefix; i.e., a conforming\n    ///   path will be a match even if it has parts remaining to process\n    /// - `force_dynamic`: Use `true` to disallow the return of static and prefix segments.\n    ///\n    /// The returned tuple includes:\n    /// - the pattern type detected, either `Static`, `Prefix`, or `Dynamic`\n    /// - a list of segment descriptors from the pattern\n    fn parse(\n        pattern: &str,\n        is_prefix: bool,\n        force_dynamic: bool,\n    ) -> (PatternType, Vec<PatternSegment>) {\n        if !force_dynamic && pattern.find('{').is_none() && !pattern.ends_with('*') {\n            // pattern is static\n            return (\n                PatternType::Static(pattern.to_owned()),\n                vec![PatternSegment::Const(pattern.to_owned())],\n            );\n        }\n\n        let mut unprocessed = pattern;\n        let mut segments = Vec::new();\n        let mut re = format!(\"{}^\", REGEX_FLAGS);\n        let mut dyn_segment_count = 0;\n        let mut has_tail_segment = false;\n\n        while let Some(idx) = unprocessed.find('{') {\n            let (prefix, rem) = unprocessed.split_at(idx);\n\n            segments.push(PatternSegment::Const(prefix.to_owned()));\n            re.push_str(&escape(prefix));\n\n            let (param_pattern, re_part, rem, tail) = Self::parse_param(rem);\n\n            if tail {\n                has_tail_segment = true;\n            }\n\n            segments.push(param_pattern);\n            re.push_str(&re_part);\n\n            unprocessed = rem;\n            dyn_segment_count += 1;\n        }\n\n        if is_prefix && has_tail_segment {\n            // tail segments in prefixes have no defined semantics\n\n            #[cfg(not(test))]\n            tracing::warn!(\n                \"Prefix resources should not have tail segments. \\\n                Use `ResourceDef::new` constructor. \\\n                This may become a panic in the future.\"\n            );\n\n            // panic in tests to make this case detectable\n            #[cfg(test)]\n            panic!(\"prefix resource definitions should not have tail segments\");\n        }\n\n        #[allow(clippy::literal_string_with_formatting_args)]\n        if unprocessed.ends_with('*') {\n            // unnamed tail segment\n\n            #[cfg(not(test))]\n            tracing::warn!(\n                \"Tail segments must have names. \\\n                Consider `.../{{tail}}*`. \\\n                This may become a panic in the future.\"\n            );\n\n            // panic in tests to make this case detectable\n            #[cfg(test)]\n            panic!(\"tail segments must have names\");\n        } else if !has_tail_segment && !unprocessed.is_empty() {\n            // prevent `Const(\"\")` element from being added after last dynamic segment\n\n            segments.push(PatternSegment::Const(unprocessed.to_owned()));\n            re.push_str(&escape(unprocessed));\n        }\n\n        assert!(\n            dyn_segment_count <= MAX_DYNAMIC_SEGMENTS,\n            \"Only {} dynamic segments are allowed, provided: {}\",\n            MAX_DYNAMIC_SEGMENTS,\n            dyn_segment_count\n        );\n\n        // Store the pattern in capture group #1 to have context info outside it\n        let mut re = format!(\"({})\", re);\n\n        // Ensure the match ends at a segment boundary\n        if !has_tail_segment {\n            if is_prefix {\n                re.push_str(r\"(/|$)\");\n            } else {\n                re.push('$');\n            }\n        }\n\n        let re = match Regex::new(&re) {\n            Ok(re) => re,\n            Err(err) => panic!(\"Wrong path pattern: \\\"{}\\\" {}\", pattern, err),\n        };\n\n        // `Bok::leak(Box::new(name))` is an intentional memory leak. In typical applications the\n        // routing table is only constructed once (per worker) so leak is bounded. If you are\n        // constructing `ResourceDef`s more than once in your application's lifecycle you would\n        // expect a linear increase in leaked memory over time.\n        let names = re\n            .capture_names()\n            .filter_map(|name| name.map(|name| Box::leak(Box::new(name.to_owned())).as_str()))\n            .collect();\n\n        (PatternType::Dynamic(re, names), segments)\n    }\n}\n\nimpl Eq for ResourceDef {}\n\nimpl PartialEq for ResourceDef {\n    fn eq(&self, other: &ResourceDef) -> bool {\n        self.patterns == other.patterns && self.is_prefix == other.is_prefix\n    }\n}\n\nimpl Hash for ResourceDef {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.patterns.hash(state);\n    }\n}\n\nimpl<'a> From<&'a str> for ResourceDef {\n    fn from(path: &'a str) -> ResourceDef {\n        ResourceDef::new(path)\n    }\n}\n\nimpl From<String> for ResourceDef {\n    fn from(path: String) -> ResourceDef {\n        ResourceDef::new(path)\n    }\n}\n\npub(crate) fn insert_slash(path: &str) -> Cow<'_, str> {\n    if !path.is_empty() && !path.starts_with('/') {\n        let mut new_path = String::with_capacity(path.len() + 1);\n        new_path.push('/');\n        new_path.push_str(path);\n        Cow::Owned(new_path)\n    } else {\n        Cow::Borrowed(path)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::Path;\n\n    #[test]\n    fn equivalence() {\n        assert_eq!(\n            ResourceDef::root_prefix(\"/root\"),\n            ResourceDef::prefix(\"/root\")\n        );\n        assert_eq!(\n            ResourceDef::root_prefix(\"root\"),\n            ResourceDef::prefix(\"/root\")\n        );\n        assert_eq!(\n            ResourceDef::root_prefix(\"/{id}\"),\n            ResourceDef::prefix(\"/{id}\")\n        );\n        assert_eq!(\n            ResourceDef::root_prefix(\"{id}\"),\n            ResourceDef::prefix(\"/{id}\")\n        );\n\n        assert_eq!(ResourceDef::new(\"/\"), ResourceDef::new([\"/\"]));\n        assert_eq!(ResourceDef::new(\"/\"), ResourceDef::new(vec![\"/\"]));\n\n        assert_ne!(ResourceDef::new(\"\"), ResourceDef::prefix(\"\"));\n        assert_ne!(ResourceDef::new(\"/\"), ResourceDef::prefix(\"/\"));\n        assert_ne!(ResourceDef::new(\"/{id}\"), ResourceDef::prefix(\"/{id}\"));\n    }\n\n    #[test]\n    fn parse_static() {\n        let re = ResourceDef::new(\"\");\n\n        assert!(!re.is_prefix());\n\n        assert!(re.is_match(\"\"));\n        assert!(!re.is_match(\"/\"));\n        assert_eq!(re.find_match(\"\"), Some(0));\n        assert_eq!(re.find_match(\"/\"), None);\n\n        let re = ResourceDef::new(\"/\");\n        assert!(re.is_match(\"/\"));\n        assert!(!re.is_match(\"\"));\n        assert!(!re.is_match(\"/foo\"));\n\n        let re = ResourceDef::new(\"/name\");\n        assert!(re.is_match(\"/name\"));\n        assert!(!re.is_match(\"/name1\"));\n        assert!(!re.is_match(\"/name/\"));\n        assert!(!re.is_match(\"/name~\"));\n\n        let mut path = Path::new(\"/name\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.unprocessed(), \"\");\n\n        assert_eq!(re.find_match(\"/name\"), Some(5));\n        assert_eq!(re.find_match(\"/name1\"), None);\n        assert_eq!(re.find_match(\"/name/\"), None);\n        assert_eq!(re.find_match(\"/name~\"), None);\n\n        let re = ResourceDef::new(\"/name/\");\n        assert!(re.is_match(\"/name/\"));\n        assert!(!re.is_match(\"/name\"));\n        assert!(!re.is_match(\"/name/gs\"));\n\n        let re = ResourceDef::new(\"/user/profile\");\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(!re.is_match(\"/user/profile/profile\"));\n\n        let mut path = Path::new(\"/user/profile\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.unprocessed(), \"\");\n    }\n\n    #[test]\n    fn parse_param() {\n        let re = ResourceDef::new(\"/user/{id}\");\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(!re.is_match(\"/user/2345/\"));\n        assert!(!re.is_match(\"/user/2345/sdg\"));\n\n        let mut path = Path::new(\"/user/profile\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"profile\");\n        assert_eq!(path.unprocessed(), \"\");\n\n        let mut path = Path::new(\"/user/1245125\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"1245125\");\n        assert_eq!(path.unprocessed(), \"\");\n\n        let re = ResourceDef::new(\"/v{version}/resource/{id}\");\n        assert!(re.is_match(\"/v1/resource/320120\"));\n        assert!(!re.is_match(\"/v/resource/1\"));\n        assert!(!re.is_match(\"/resource\"));\n\n        let mut path = Path::new(\"/v151/resource/adage32\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"version\").unwrap(), \"151\");\n        assert_eq!(path.get(\"id\").unwrap(), \"adage32\");\n        assert_eq!(path.unprocessed(), \"\");\n\n        let re = ResourceDef::new(\"/{id:[[:digit:]]{6}}\");\n        assert!(re.is_match(\"/012345\"));\n        assert!(!re.is_match(\"/012\"));\n        assert!(!re.is_match(\"/01234567\"));\n        assert!(!re.is_match(\"/XXXXXX\"));\n\n        let mut path = Path::new(\"/012345\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"012345\");\n        assert_eq!(path.unprocessed(), \"\");\n    }\n\n    #[allow(clippy::cognitive_complexity)]\n    #[test]\n    fn dynamic_set() {\n        let re = ResourceDef::new(vec![\n            \"/user/{id}\",\n            \"/v{version}/resource/{id}\",\n            \"/{id:[[:digit:]]{6}}\",\n            \"/static\",\n        ]);\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(!re.is_match(\"/user/2345/\"));\n        assert!(!re.is_match(\"/user/2345/sdg\"));\n\n        let mut path = Path::new(\"/user/profile\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"profile\");\n        assert_eq!(path.unprocessed(), \"\");\n\n        let mut path = Path::new(\"/user/1245125\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"1245125\");\n        assert_eq!(path.unprocessed(), \"\");\n\n        assert!(re.is_match(\"/v1/resource/320120\"));\n        assert!(!re.is_match(\"/v/resource/1\"));\n        assert!(!re.is_match(\"/resource\"));\n\n        let mut path = Path::new(\"/v151/resource/adage32\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"version\").unwrap(), \"151\");\n        assert_eq!(path.get(\"id\").unwrap(), \"adage32\");\n\n        assert!(re.is_match(\"/012345\"));\n        assert!(!re.is_match(\"/012\"));\n        assert!(!re.is_match(\"/01234567\"));\n        assert!(!re.is_match(\"/XXXXXX\"));\n\n        assert!(re.is_match(\"/static\"));\n        assert!(!re.is_match(\"/a/static\"));\n        assert!(!re.is_match(\"/static/a\"));\n\n        let mut path = Path::new(\"/012345\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"012345\");\n\n        let re = ResourceDef::new([\n            \"/user/{id}\",\n            \"/v{version}/resource/{id}\",\n            \"/{id:[[:digit:]]{6}}\",\n        ]);\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(!re.is_match(\"/user/2345/\"));\n        assert!(!re.is_match(\"/user/2345/sdg\"));\n\n        let re = ResourceDef::new([\n            \"/user/{id}\".to_string(),\n            \"/v{version}/resource/{id}\".to_string(),\n            \"/{id:[[:digit:]]{6}}\".to_string(),\n        ]);\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(!re.is_match(\"/user/2345/\"));\n        assert!(!re.is_match(\"/user/2345/sdg\"));\n    }\n\n    #[test]\n    fn dynamic_set_prefix() {\n        let re = ResourceDef::prefix(vec![\"/u/{id}\", \"/{id:[[:digit:]]{3}}\"]);\n\n        assert_eq!(re.find_match(\"/u/abc\"), Some(6));\n        assert_eq!(re.find_match(\"/u/abc/123\"), Some(6));\n        assert_eq!(re.find_match(\"/s/user/profile\"), None);\n\n        assert_eq!(re.find_match(\"/123\"), Some(4));\n        assert_eq!(re.find_match(\"/123/456\"), Some(4));\n        assert_eq!(re.find_match(\"/12345\"), None);\n\n        let mut path = Path::new(\"/151/res\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"151\");\n        assert_eq!(path.unprocessed(), \"/res\");\n    }\n\n    #[test]\n    fn parse_tail() {\n        let re = ResourceDef::new(\"/user/-{id}*\");\n\n        let mut path = Path::new(\"/user/-profile\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"profile\");\n\n        let mut path = Path::new(\"/user/-2345\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"2345\");\n\n        let mut path = Path::new(\"/user/-2345/\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"2345/\");\n\n        let mut path = Path::new(\"/user/-2345/sdg\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"2345/sdg\");\n    }\n\n    #[test]\n    fn static_tail() {\n        let re = ResourceDef::new(\"/user{tail}*\");\n        assert!(re.is_match(\"/users\"));\n        assert!(re.is_match(\"/user-foo\"));\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(re.is_match(\"/user/2345/\"));\n        assert!(re.is_match(\"/user/2345/sdg\"));\n        assert!(!re.is_match(\"/foo/profile\"));\n\n        let re = ResourceDef::new(\"/user/{tail}*\");\n        assert!(re.is_match(\"/user/profile\"));\n        assert!(re.is_match(\"/user/2345\"));\n        assert!(re.is_match(\"/user/2345/\"));\n        assert!(re.is_match(\"/user/2345/sdg\"));\n        assert!(!re.is_match(\"/foo/profile\"));\n    }\n\n    #[test]\n    fn dynamic_tail() {\n        let re = ResourceDef::new(\"/user/{id}/{tail}*\");\n        assert!(!re.is_match(\"/user/2345\"));\n        let mut path = Path::new(\"/user/2345/sdg\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"2345\");\n        assert_eq!(path.get(\"tail\").unwrap(), \"sdg\");\n        assert_eq!(path.unprocessed(), \"\");\n    }\n\n    #[allow(clippy::literal_string_with_formatting_args)]\n    #[test]\n    fn newline_patterns_and_paths() {\n        let re = ResourceDef::new(\"/user/a\\nb\");\n        assert!(re.is_match(\"/user/a\\nb\"));\n        assert!(!re.is_match(\"/user/a\\nb/profile\"));\n\n        let re = ResourceDef::new(\"/a{x}b/test/a{y}b\");\n        let mut path = Path::new(\"/a\\nb/test/a\\nb\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"x\").unwrap(), \"\\n\");\n        assert_eq!(path.get(\"y\").unwrap(), \"\\n\");\n\n        let re = ResourceDef::new(\"/user/{tail}*\");\n        assert!(re.is_match(\"/user/a\\nb/\"));\n\n        let re = ResourceDef::new(\"/user/{id}*\");\n        let mut path = Path::new(\"/user/a\\nb/a\\nb\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"a\\nb/a\\nb\");\n\n        let re = ResourceDef::new(\"/user/{id:.*}\");\n        let mut path = Path::new(\"/user/a\\nb/a\\nb\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"a\\nb/a\\nb\");\n    }\n\n    #[cfg(feature = \"http\")]\n    #[test]\n    fn parse_urlencoded_param() {\n        let re = ResourceDef::new(\"/user/{id}/test\");\n\n        let mut path = Path::new(\"/user/2345/test\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"2345\");\n\n        let mut path = Path::new(\"/user/qwe%25/test\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"qwe%25\");\n\n        let uri = http::Uri::try_from(\"/user/qwe%25/test\").unwrap();\n        let mut path = Path::new(uri);\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.get(\"id\").unwrap(), \"qwe%25\");\n    }\n\n    #[test]\n    fn prefix_static() {\n        let re = ResourceDef::prefix(\"/name\");\n\n        assert!(re.is_prefix());\n\n        assert!(re.is_match(\"/name\"));\n        assert!(re.is_match(\"/name/\"));\n        assert!(re.is_match(\"/name/test/test\"));\n        assert!(!re.is_match(\"/name1\"));\n        assert!(!re.is_match(\"/name~\"));\n\n        let mut path = Path::new(\"/name\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.unprocessed(), \"\");\n\n        let mut path = Path::new(\"/name/test\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.unprocessed(), \"/test\");\n\n        assert_eq!(re.find_match(\"/name\"), Some(5));\n        assert_eq!(re.find_match(\"/name/\"), Some(5));\n        assert_eq!(re.find_match(\"/name/test/test\"), Some(5));\n        assert_eq!(re.find_match(\"/name1\"), None);\n        assert_eq!(re.find_match(\"/name~\"), None);\n\n        let re = ResourceDef::prefix(\"/name/\");\n        assert!(re.is_match(\"/name/\"));\n        assert!(re.is_match(\"/name//gs\"));\n        assert!(!re.is_match(\"/name/gs\"));\n        assert!(!re.is_match(\"/name\"));\n\n        let mut path = Path::new(\"/name/gs\");\n        assert!(!re.capture_match_info(&mut path));\n\n        let mut path = Path::new(\"/name//gs\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(path.unprocessed(), \"/gs\");\n\n        let re = ResourceDef::root_prefix(\"name/\");\n        assert!(re.is_match(\"/name/\"));\n        assert!(re.is_match(\"/name//gs\"));\n        assert!(!re.is_match(\"/name/gs\"));\n        assert!(!re.is_match(\"/name\"));\n\n        let mut path = Path::new(\"/name/gs\");\n        assert!(!re.capture_match_info(&mut path));\n    }\n\n    #[test]\n    fn prefix_dynamic() {\n        let re = ResourceDef::prefix(\"/{name}\");\n\n        assert!(re.is_prefix());\n\n        assert!(re.is_match(\"/name/\"));\n        assert!(re.is_match(\"/name/gs\"));\n        assert!(re.is_match(\"/name\"));\n\n        assert_eq!(re.find_match(\"/name/\"), Some(5));\n        assert_eq!(re.find_match(\"/name/gs\"), Some(5));\n        assert_eq!(re.find_match(\"/name\"), Some(5));\n        assert_eq!(re.find_match(\"\"), None);\n\n        let mut path = Path::new(\"/test2/\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(&path[\"name\"], \"test2\");\n        assert_eq!(&path[0], \"test2\");\n        assert_eq!(path.unprocessed(), \"/\");\n\n        let mut path = Path::new(\"/test2/subpath1/subpath2/index.html\");\n        assert!(re.capture_match_info(&mut path));\n        assert_eq!(&path[\"name\"], \"test2\");\n        assert_eq!(&path[0], \"test2\");\n        assert_eq!(path.unprocessed(), \"/subpath1/subpath2/index.html\");\n\n        let resource = ResourceDef::prefix(\"/user\");\n        // input string shorter than prefix\n        assert!(resource.find_match(\"/foo\").is_none());\n    }\n\n    #[test]\n    fn prefix_empty() {\n        let re = ResourceDef::prefix(\"\");\n\n        assert!(re.is_prefix());\n\n        assert!(re.is_match(\"\"));\n        assert!(re.is_match(\"/\"));\n        assert!(re.is_match(\"/name/test/test\"));\n    }\n\n    #[test]\n    fn build_path_list() {\n        let mut s = String::new();\n        let resource = ResourceDef::new(\"/user/{item1}/test\");\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"user1\"].iter()));\n        assert_eq!(s, \"/user/user1/test\");\n\n        let mut s = String::new();\n        let resource = ResourceDef::new(\"/user/{item1}/{item2}/test\");\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"item\", \"item2\"].iter()));\n        assert_eq!(s, \"/user/item/item2/test\");\n\n        let mut s = String::new();\n        let resource = ResourceDef::new(\"/user/{item1}/{item2}\");\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"item\", \"item2\"].iter()));\n        assert_eq!(s, \"/user/item/item2\");\n\n        let mut s = String::new();\n        let resource = ResourceDef::new(\"/user/{item1}/{item2}/\");\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"item\", \"item2\"].iter()));\n        assert_eq!(s, \"/user/item/item2/\");\n\n        let mut s = String::new();\n        assert!(!resource.resource_path_from_iter(&mut s, &mut [\"item\"].iter()));\n\n        let mut s = String::new();\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"item\", \"item2\"].iter()));\n        assert_eq!(s, \"/user/item/item2/\");\n        assert!(!resource.resource_path_from_iter(&mut s, &mut [\"item\"].iter()));\n\n        let mut s = String::new();\n\n        assert!(resource.resource_path_from_iter(\n            &mut s,\n            #[allow(clippy::useless_vec)]\n            &mut vec![\"item\", \"item2\"].iter()\n        ));\n        assert_eq!(s, \"/user/item/item2/\");\n    }\n\n    #[test]\n    fn multi_pattern_build_path() {\n        let resource = ResourceDef::new([\"/user/{id}\", \"/profile/{id}\"]);\n        let mut s = String::new();\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"123\"].iter()));\n        assert_eq!(s, \"/user/123\");\n    }\n\n    #[test]\n    fn multi_pattern_capture_segment_values() {\n        let resource = ResourceDef::new([\"/user/{id}\", \"/profile/{id}\"]);\n\n        let mut path = Path::new(\"/user/123\");\n        assert!(resource.capture_match_info(&mut path));\n        assert!(path.get(\"id\").is_some());\n\n        let mut path = Path::new(\"/profile/123\");\n        assert!(resource.capture_match_info(&mut path));\n        assert!(path.get(\"id\").is_some());\n\n        let resource = ResourceDef::new([\"/user/{id}\", \"/profile/{uid}\"]);\n\n        let mut path = Path::new(\"/user/123\");\n        assert!(resource.capture_match_info(&mut path));\n        assert!(path.get(\"id\").is_some());\n        assert!(path.get(\"uid\").is_none());\n\n        let mut path = Path::new(\"/profile/123\");\n        assert!(resource.capture_match_info(&mut path));\n        assert!(path.get(\"id\").is_none());\n        assert!(path.get(\"uid\").is_some());\n    }\n\n    #[test]\n    fn dynamic_prefix_proper_segmentation() {\n        let resource = ResourceDef::prefix(r\"/id/{id:\\d{3}}\");\n\n        assert!(resource.is_match(\"/id/123\"));\n        assert!(resource.is_match(\"/id/123/foo\"));\n        assert!(!resource.is_match(\"/id/1234\"));\n        assert!(!resource.is_match(\"/id/123a\"));\n\n        assert_eq!(resource.find_match(\"/id/123\"), Some(7));\n        assert_eq!(resource.find_match(\"/id/123/foo\"), Some(7));\n        assert_eq!(resource.find_match(\"/id/1234\"), None);\n        assert_eq!(resource.find_match(\"/id/123a\"), None);\n    }\n\n    #[test]\n    fn build_path_map() {\n        let resource = ResourceDef::new(\"/user/{item1}/{item2}/\");\n\n        let mut map = HashMap::new();\n        map.insert(\"item1\", \"item\");\n\n        let mut s = String::new();\n        assert!(!resource.resource_path_from_map(&mut s, &map));\n\n        map.insert(\"item2\", \"item2\");\n\n        let mut s = String::new();\n        assert!(resource.resource_path_from_map(&mut s, &map));\n        assert_eq!(s, \"/user/item/item2/\");\n    }\n\n    #[test]\n    fn build_path_tail() {\n        let resource = ResourceDef::new(\"/user/{item1}*\");\n\n        let mut s = String::new();\n        assert!(!resource.resource_path_from_iter(&mut s, &mut [\"\"; 0].iter()));\n\n        let mut s = String::new();\n        assert!(resource.resource_path_from_iter(&mut s, &mut [\"user1\"].iter()));\n        assert_eq!(s, \"/user/user1\");\n\n        let mut s = String::new();\n        let mut map = HashMap::new();\n        map.insert(\"item1\", \"item\");\n        assert!(resource.resource_path_from_map(&mut s, &map));\n        assert_eq!(s, \"/user/item\");\n    }\n\n    #[test]\n    fn prefix_trailing_slash() {\n        // The prefix \"/abc/\" matches two segments: [\"user\", \"\"]\n\n        // These are not prefixes\n        let re = ResourceDef::prefix(\"/abc/\");\n        assert_eq!(re.find_match(\"/abc/def\"), None);\n        assert_eq!(re.find_match(\"/abc//def\"), Some(5));\n\n        let re = ResourceDef::prefix(\"/{id}/\");\n        assert_eq!(re.find_match(\"/abc/def\"), None);\n        assert_eq!(re.find_match(\"/abc//def\"), Some(5));\n    }\n\n    #[test]\n    fn join() {\n        // test joined defs match the same paths as each component separately\n\n        fn seq_find_match(re1: &ResourceDef, re2: &ResourceDef, path: &str) -> Option<usize> {\n            let len1 = re1.find_match(path)?;\n            let len2 = re2.find_match(&path[len1..])?;\n            Some(len1 + len2)\n        }\n\n        macro_rules! join_test {\n            ($pat1:expr, $pat2:expr => $($test:expr),+) => {{\n                let pat1 = $pat1;\n                let pat2 = $pat2;\n                $({\n                    let _path = $test;\n                    let (re1, re2) = (ResourceDef::prefix(pat1), ResourceDef::new(pat2));\n                    let _seq = seq_find_match(&re1, &re2, _path);\n                    let _join = re1.join(&re2).find_match(_path);\n                    assert_eq!(\n                        _seq, _join,\n                        \"patterns: prefix {:?}, {:?}; mismatch on \\\"{}\\\"; seq={:?}; join={:?}\",\n                        pat1, pat2, _path, _seq, _join\n                    );\n                    assert!(!re1.join(&re2).is_prefix());\n\n                    let (re1, re2) = (ResourceDef::prefix(pat1), ResourceDef::prefix(pat2));\n                    let _seq = seq_find_match(&re1, &re2, _path);\n                    let _join = re1.join(&re2).find_match(_path);\n                    assert_eq!(\n                        _seq, _join,\n                        \"patterns: prefix {:?}, prefix {:?}; mismatch on \\\"{}\\\"; seq={:?}; join={:?}\",\n                        pat1, pat2, _path, _seq, _join\n                    );\n                    assert!(re1.join(&re2).is_prefix());\n                })+\n            }}\n        }\n\n        join_test!(\"\", \"\" => \"\", \"/hello\", \"/\");\n        join_test!(\"/user\", \"\" => \"\", \"/user\", \"/user/123\", \"/user11\", \"user\", \"user/123\");\n        join_test!(\"\",  \"/user\" => \"\", \"/user\", \"foo\", \"/user11\", \"user\", \"user/123\");\n        join_test!(\"/user\",  \"/xx\" => \"\", \"\",  \"/\", \"/user\", \"/xx\", \"/userxx\", \"/user/xx\");\n\n        join_test!([\"/ver/{v}\", \"/v{v}\"], [\"/req/{req}\", \"/{req}\"] => \"/v1/abc\", \n                   \"/ver/1/abc\", \"/v1/req/abc\", \"/ver/1/req/abc\", \"/v1/abc/def\",\n                   \"/ver1/req/abc/def\", \"\", \"/\", \"/v1/\");\n    }\n\n    #[test]\n    fn match_methods_agree() {\n        macro_rules! match_methods_agree {\n            ($pat:expr => $($test:expr),+) => {{\n                match_methods_agree!(finish $pat, ResourceDef::new($pat), $($test),+);\n            }};\n            (prefix $pat:expr => $($test:expr),+) => {{\n                match_methods_agree!(finish $pat, ResourceDef::prefix($pat), $($test),+);\n            }};\n            (finish $pat:expr, $re:expr, $($test:expr),+) => {{\n                let re = $re;\n                $({\n                    let _is = re.is_match($test);\n                    let _find = re.find_match($test).is_some();\n                    assert_eq!(\n                        _is, _find,\n                        \"pattern: {:?}; mismatch on \\\"{}\\\"; is={}; find={}\",\n                        $pat, $test, _is, _find\n                    );\n                })+\n            }}\n        }\n\n        match_methods_agree!(\"\" => \"\", \"/\", \"/foo\");\n        match_methods_agree!(\"/\" => \"\", \"/\", \"/foo\");\n        match_methods_agree!(\"/user\" => \"user\", \"/user\", \"/users\", \"/user/123\", \"/foo\");\n        match_methods_agree!(\"/v{v}\" => \"v\", \"/v\", \"/v1\", \"/v222\", \"/foo\");\n        match_methods_agree!([\"/v{v}\", \"/version/{v}\"] => \"/v\", \"/v1\", \"/version\", \"/version/1\", \"/foo\");\n\n        match_methods_agree!(\"/path{tail}*\" => \"/path\", \"/path1\", \"/path/123\");\n        match_methods_agree!(\"/path/{tail}*\" => \"/path\", \"/path1\", \"/path/123\");\n\n        match_methods_agree!(prefix \"\" => \"\", \"/\", \"/foo\");\n        match_methods_agree!(prefix \"/user\" => \"user\", \"/user\", \"/users\", \"/user/123\", \"/foo\");\n        match_methods_agree!(prefix r\"/id/{id:\\d{3}}\" => \"/id/123\", \"/id/1234\");\n        match_methods_agree!([\"/v{v}\", \"/ver/{v}\"] => \"\", \"s/v\", \"/v1\", \"/v1/xx\", \"/ver/i3/5\", \"/ver/1\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn duplicate_segment_name() {\n        ResourceDef::new(\"/user/{id}/post/{id}\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_dynamic_segment_delimiter() {\n        ResourceDef::new(\"/user/{username\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_dynamic_segment_name() {\n        ResourceDef::new(\"/user/{}\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_too_many_dynamic_segments() {\n        // valid\n        ResourceDef::new(\"/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}\");\n\n        // panics\n        ResourceDef::new(\"/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_custom_regex_for_tail() {\n        ResourceDef::new(r\"/{tail:\\d+}*\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_unnamed_tail_segment() {\n        ResourceDef::new(\"/*\");\n    }\n\n    #[test]\n    #[should_panic]\n    fn prefix_plus_tail_match_disallowed() {\n        ResourceDef::prefix(\"/user/{id}*\");\n    }\n}\n"
  },
  {
    "path": "actix-router/src/resource_path.rs",
    "content": "use crate::Path;\n\n/// Abstraction over types that can provide a mutable [`Path`] for routing.\n///\n/// This trait is used by the router to extract the request path in a uniform way across different\n/// request types (e.g., Actix Web's `ServiceRequest`). Implementors return a mutable [`Path`]\n/// wrapper so routing can read and potentially normalize/parse the path without requiring the\n/// original request type.\npub trait Resource {\n    /// Type of resource's path returned in `resource_path`.\n    type Path: ResourcePath;\n\n    /// Returns a mutable reference to the path wrapper used by the router.\n    fn resource_path(&mut self) -> &mut Path<Self::Path>;\n}\n\npub trait ResourcePath {\n    fn path(&self) -> &str;\n}\n\nimpl ResourcePath for String {\n    fn path(&self) -> &str {\n        self.as_str()\n    }\n}\n\nimpl ResourcePath for &str {\n    fn path(&self) -> &str {\n        self\n    }\n}\n\nimpl ResourcePath for bytestring::ByteString {\n    fn path(&self) -> &str {\n        self\n    }\n}\n\n#[cfg(feature = \"http\")]\nimpl ResourcePath for http::Uri {\n    fn path(&self) -> &str {\n        self.path()\n    }\n}\n"
  },
  {
    "path": "actix-router/src/router.rs",
    "content": "use crate::{IntoPatterns, Resource, ResourceDef};\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub struct ResourceId(pub u16);\n\n/// Resource router.\n///\n/// It matches a [routing resource](Resource) to an ordered list of _routes_. Each is defined by a\n/// single [`ResourceDef`] and contains two types of custom data:\n/// 1. The route _value_, of the generic type `T`.\n/// 1. Some _context_ data, of the generic type `U`, which is only provided to the check function in\n///    [`recognize_fn`](Self::recognize_fn). This parameter defaults to `()` and can be omitted if\n///    not required.\npub struct Router<T, U = ()> {\n    routes: Vec<(ResourceDef, T, U)>,\n}\n\nimpl<T, U> Router<T, U> {\n    /// Constructs new `RouterBuilder` with empty route list.\n    pub fn build() -> RouterBuilder<T, U> {\n        RouterBuilder { routes: Vec::new() }\n    }\n\n    /// Finds the value in the router that matches a given [routing resource](Resource).\n    ///\n    /// The match result, including the captured dynamic segments, in the `resource`.\n    pub fn recognize<R>(&self, resource: &mut R) -> Option<(&T, ResourceId)>\n    where\n        R: Resource,\n    {\n        self.recognize_fn(resource, |_, _| true)\n    }\n\n    /// Same as [`recognize`](Self::recognize) but returns a mutable reference to the matched value.\n    pub fn recognize_mut<R>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>\n    where\n        R: Resource,\n    {\n        self.recognize_mut_fn(resource, |_, _| true)\n    }\n\n    /// Finds the value in the router that matches a given [routing resource](Resource) and passes\n    /// an additional predicate check using context data.\n    ///\n    /// Similar to [`recognize`](Self::recognize). However, before accepting the route as matched,\n    /// the `check` closure is executed, passing the resource and each route's context data. If the\n    /// closure returns true then the match result is stored into `resource` and a reference to\n    /// the matched _value_ is returned.\n    pub fn recognize_fn<R, F>(&self, resource: &mut R, mut check: F) -> Option<(&T, ResourceId)>\n    where\n        R: Resource,\n        F: FnMut(&R, &U) -> bool,\n    {\n        for (rdef, val, ctx) in self.routes.iter() {\n            if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {\n                return Some((val, ResourceId(rdef.id())));\n            }\n        }\n\n        None\n    }\n\n    /// Same as [`recognize_fn`](Self::recognize_fn) but returns a mutable reference to the matched\n    /// value.\n    pub fn recognize_mut_fn<R, F>(\n        &mut self,\n        resource: &mut R,\n        mut check: F,\n    ) -> Option<(&mut T, ResourceId)>\n    where\n        R: Resource,\n        F: FnMut(&R, &U) -> bool,\n    {\n        for (rdef, val, ctx) in self.routes.iter_mut() {\n            if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {\n                return Some((val, ResourceId(rdef.id())));\n            }\n        }\n\n        None\n    }\n}\n\n/// Builder for an ordered [routing](Router) list.\npub struct RouterBuilder<T, U = ()> {\n    routes: Vec<(ResourceDef, T, U)>,\n}\n\nimpl<T, U> RouterBuilder<T, U> {\n    /// Adds a new route to the end of the routing list.\n    ///\n    /// Returns mutable references to elements of the new route.\n    pub fn push(\n        &mut self,\n        rdef: ResourceDef,\n        val: T,\n        ctx: U,\n    ) -> (&mut ResourceDef, &mut T, &mut U) {\n        self.routes.push((rdef, val, ctx));\n        #[allow(clippy::map_identity)] // map is used to distribute &mut-ness to tuple elements\n        self.routes\n            .last_mut()\n            .map(|(rdef, val, ctx)| (rdef, val, ctx))\n            .unwrap()\n    }\n\n    /// Finish configuration and create router instance.\n    pub fn finish(self) -> Router<T, U> {\n        Router {\n            routes: self.routes,\n        }\n    }\n}\n\n/// Convenience methods provided when context data impls [`Default`]\nimpl<T, U> RouterBuilder<T, U>\nwhere\n    U: Default,\n{\n    /// Registers resource for specified path.\n    pub fn path(&mut self, path: impl IntoPatterns, val: T) -> (&mut ResourceDef, &mut T, &mut U) {\n        self.push(ResourceDef::new(path), val, U::default())\n    }\n\n    /// Registers resource for specified path prefix.\n    pub fn prefix(\n        &mut self,\n        prefix: impl IntoPatterns,\n        val: T,\n    ) -> (&mut ResourceDef, &mut T, &mut U) {\n        self.push(ResourceDef::prefix(prefix), val, U::default())\n    }\n\n    /// Registers resource for [`ResourceDef`].\n    pub fn rdef(&mut self, rdef: ResourceDef, val: T) -> (&mut ResourceDef, &mut T, &mut U) {\n        self.push(rdef, val, U::default())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{\n        path::Path,\n        router::{ResourceId, Router},\n    };\n\n    #[allow(clippy::cognitive_complexity)]\n    #[allow(clippy::literal_string_with_formatting_args)]\n    #[test]\n    fn test_recognizer_1() {\n        let mut router = Router::<usize>::build();\n        router.path(\"/name\", 10).0.set_id(0);\n        router.path(\"/name/{val}\", 11).0.set_id(1);\n        router.path(\"/name/{val}/index.html\", 12).0.set_id(2);\n        router.path(\"/file/{file}.{ext}\", 13).0.set_id(3);\n        router.path(\"/v{val}/{val2}/index.html\", 14).0.set_id(4);\n        router.path(\"/v/{tail:.*}\", 15).0.set_id(5);\n        router.path(\"/test2/{test}.html\", 16).0.set_id(6);\n        router.path(\"/{test}/index.html\", 17).0.set_id(7);\n        let mut router = router.finish();\n\n        let mut path = Path::new(\"/unknown\");\n        assert!(router.recognize_mut(&mut path).is_none());\n\n        let mut path = Path::new(\"/name\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 10);\n        assert_eq!(info, ResourceId(0));\n        assert!(path.is_empty());\n\n        let mut path = Path::new(\"/name/value\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 11);\n        assert_eq!(info, ResourceId(1));\n        assert_eq!(path.get(\"val\").unwrap(), \"value\");\n        assert_eq!(&path[\"val\"], \"value\");\n\n        let mut path = Path::new(\"/name/value2/index.html\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 12);\n        assert_eq!(info, ResourceId(2));\n        assert_eq!(path.get(\"val\").unwrap(), \"value2\");\n\n        let mut path = Path::new(\"/file/file.gz\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 13);\n        assert_eq!(info, ResourceId(3));\n        assert_eq!(path.get(\"file\").unwrap(), \"file\");\n        assert_eq!(path.get(\"ext\").unwrap(), \"gz\");\n\n        let mut path = Path::new(\"/v2/ttt/index.html\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 14);\n        assert_eq!(info, ResourceId(4));\n        assert_eq!(path.get(\"val\").unwrap(), \"2\");\n        assert_eq!(path.get(\"val2\").unwrap(), \"ttt\");\n\n        let mut path = Path::new(\"/v/blah-blah/index.html\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 15);\n        assert_eq!(info, ResourceId(5));\n        assert_eq!(path.get(\"tail\").unwrap(), \"blah-blah/index.html\");\n\n        let mut path = Path::new(\"/test2/index.html\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 16);\n        assert_eq!(info, ResourceId(6));\n        assert_eq!(path.get(\"test\").unwrap(), \"index\");\n\n        let mut path = Path::new(\"/bbb/index.html\");\n        let (h, info) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 17);\n        assert_eq!(info, ResourceId(7));\n        assert_eq!(path.get(\"test\").unwrap(), \"bbb\");\n    }\n\n    #[test]\n    fn test_recognizer_2() {\n        let mut router = Router::<usize>::build();\n        router.path(\"/index.json\", 10);\n        router.path(\"/{source}.json\", 11);\n        let mut router = router.finish();\n\n        let mut path = Path::new(\"/index.json\");\n        let (h, _) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 10);\n\n        let mut path = Path::new(\"/test.json\");\n        let (h, _) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 11);\n    }\n\n    #[test]\n    fn test_recognizer_with_prefix() {\n        let mut router = Router::<usize>::build();\n        router.path(\"/name\", 10).0.set_id(0);\n        router.path(\"/name/{val}\", 11).0.set_id(1);\n        let mut router = router.finish();\n\n        let mut path = Path::new(\"/name\");\n        path.skip(5);\n        assert!(router.recognize_mut(&mut path).is_none());\n\n        let mut path = Path::new(\"/test/name\");\n        path.skip(5);\n        let (h, _) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 10);\n\n        let mut path = Path::new(\"/test/name/value\");\n        path.skip(5);\n        let (h, id) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 11);\n        assert_eq!(id, ResourceId(1));\n        assert_eq!(path.get(\"val\").unwrap(), \"value\");\n        assert_eq!(&path[\"val\"], \"value\");\n\n        // same patterns\n        let mut router = Router::<usize>::build();\n        router.path(\"/name\", 10);\n        router.path(\"/name/{val}\", 11);\n        let mut router = router.finish();\n\n        // test skip beyond path length\n        let mut path = Path::new(\"/name\");\n        path.skip(6);\n        assert!(router.recognize_mut(&mut path).is_none());\n\n        let mut path = Path::new(\"/test2/name\");\n        path.skip(6);\n        let (h, _) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 10);\n\n        let mut path = Path::new(\"/test2/name-test\");\n        path.skip(6);\n        assert!(router.recognize_mut(&mut path).is_none());\n\n        let mut path = Path::new(\"/test2/name/ttt\");\n        path.skip(6);\n        let (h, _) = router.recognize_mut(&mut path).unwrap();\n        assert_eq!(*h, 11);\n        assert_eq!(&path[\"val\"], \"ttt\");\n    }\n}\n"
  },
  {
    "path": "actix-router/src/url.rs",
    "content": "use crate::{Quoter, ResourcePath};\n\nthread_local! {\n    static DEFAULT_QUOTER: Quoter = Quoter::new(b\"\", b\"%/+\");\n}\n\n#[derive(Debug, Clone, Default)]\npub struct Url {\n    uri: http::Uri,\n    path: Option<String>,\n}\n\nimpl Url {\n    #[inline]\n    pub fn new(uri: http::Uri) -> Url {\n        let path = DEFAULT_QUOTER.with(|q| q.requote_str_lossy(uri.path()));\n        Url { uri, path }\n    }\n\n    #[inline]\n    pub fn new_with_quoter(uri: http::Uri, quoter: &Quoter) -> Url {\n        Url {\n            path: quoter.requote_str_lossy(uri.path()),\n            uri,\n        }\n    }\n\n    /// Returns URI.\n    #[inline]\n    pub fn uri(&self) -> &http::Uri {\n        &self.uri\n    }\n\n    /// Returns path.\n    #[inline]\n    pub fn path(&self) -> &str {\n        match self.path {\n            Some(ref path) => path,\n            _ => self.uri.path(),\n        }\n    }\n\n    #[inline]\n    pub fn update(&mut self, uri: &http::Uri) {\n        self.uri = uri.clone();\n        self.path = DEFAULT_QUOTER.with(|q| q.requote_str_lossy(uri.path()));\n    }\n\n    #[inline]\n    pub fn update_with_quoter(&mut self, uri: &http::Uri, quoter: &Quoter) {\n        self.uri = uri.clone();\n        self.path = quoter.requote_str_lossy(uri.path());\n    }\n}\n\nimpl ResourcePath for Url {\n    #[inline]\n    fn path(&self) -> &str {\n        self.path()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::fmt::Write as _;\n\n    use http::Uri;\n\n    use super::*;\n    use crate::{Path, ResourceDef};\n\n    const PROTECTED: &[u8] = b\"%/+\";\n\n    fn match_url(pattern: &'static str, url: impl AsRef<str>) -> Path<Url> {\n        let re = ResourceDef::new(pattern);\n        let uri = Uri::try_from(url.as_ref()).unwrap();\n        let mut path = Path::new(Url::new(uri));\n        assert!(re.capture_match_info(&mut path));\n        path\n    }\n\n    fn percent_encode(data: &[u8]) -> String {\n        data.iter()\n            .fold(String::with_capacity(data.len() * 3), |mut buf, c| {\n                write!(&mut buf, \"%{:02X}\", c).unwrap();\n                buf\n            })\n    }\n\n    #[test]\n    fn parse_url() {\n        let re = \"/user/{id}/test\";\n\n        let path = match_url(re, \"/user/2345/test\");\n        assert_eq!(path.get(\"id\").unwrap(), \"2345\");\n    }\n\n    #[test]\n    fn protected_chars() {\n        let re = \"/user/{id}/test\";\n\n        let encoded = percent_encode(PROTECTED);\n        let path = match_url(re, format!(\"/user/{}/test\", encoded));\n        // characters in captured segment remain unencoded\n        assert_eq!(path.get(\"id\").unwrap(), &encoded);\n\n        // \"%25\" should never be decoded into '%' to guarantee the output is a valid\n        // percent-encoded format\n        let path = match_url(re, \"/user/qwe%25/test\");\n        assert_eq!(path.get(\"id\").unwrap(), \"qwe%25\");\n\n        let path = match_url(re, \"/user/qwe%25rty/test\");\n        assert_eq!(path.get(\"id\").unwrap(), \"qwe%25rty\");\n    }\n\n    #[test]\n    fn non_protected_ascii() {\n        let non_protected_ascii = ('\\u{0}'..='\\u{7F}')\n            .filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8)))\n            .collect::<String>();\n        let encoded = percent_encode(non_protected_ascii.as_bytes());\n        let path = match_url(\"/user/{id}/test\", format!(\"/user/{}/test\", encoded));\n        assert_eq!(path.get(\"id\").unwrap(), &non_protected_ascii);\n    }\n\n    #[test]\n    fn valid_utf8_multi_byte() {\n        let test = ('\\u{FF00}'..='\\u{FFFF}').collect::<String>();\n        let encoded = percent_encode(test.as_bytes());\n        let path = match_url(\"/a/{id}/b\", format!(\"/a/{}/b\", &encoded));\n        assert_eq!(path.get(\"id\").unwrap(), &test);\n    }\n\n    #[test]\n    fn invalid_utf8() {\n        let invalid_utf8 = percent_encode((0x80..=0xff).collect::<Vec<_>>().as_slice());\n        let uri = Uri::try_from(format!(\"/{}\", invalid_utf8)).unwrap();\n        let path = Path::new(Url::new(uri));\n\n        // We should always get a valid utf8 string\n        assert!(String::from_utf8(path.as_str().as_bytes().to_owned()).is_ok());\n    }\n}\n"
  },
  {
    "path": "actix-test/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n## 0.1.5\n\n- Add `TestServerConfig::listen_address()` method.\n\n## 0.1.4\n\n- Add `TestServerConfig::rustls_0_23()` method for Rustls v0.23 support behind new `rustls-0_23` crate feature.\n- Add `TestServerConfig::disable_redirects()` method.\n- Various types from `awc`, such as `ClientRequest` and `ClientResponse`, are now re-exported.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 0.1.3\n\n- Add `TestServerConfig::rustls_0_22()` method for Rustls v0.22 support behind new `rustls-0_22` crate feature.\n\n## 0.1.2\n\n- Add `TestServerConfig::rustls_021()` method for Rustls v0.21 support behind new `rustls-0_21` crate feature.\n- Add `TestServerConfig::workers()` method.\n- Add `rustls-0_20` crate feature, which the existing `rustls` feature now aliases.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 0.1.1\n\n- Add `TestServerConfig::port()` setter method.\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n## 0.1.0\n\n- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.\n\n## 0.1.0-beta.13\n\n- No significant changes since `0.1.0-beta.12`.\n\n## 0.1.0-beta.12\n\n- Rename `TestServerConfig::{client_timeout => client_request_timeout}`. [#2611]\n\n[#2611]: https://github.com/actix/actix-web/pull/2611\n\n## 0.1.0-beta.11\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 0.1.0-beta.10\n\n- No significant changes since `0.1.0-beta.9`.\n\n## 0.1.0-beta.9\n\n- Re-export `actix_http::body::to_bytes`. [#2518]\n- Update `actix_web::test` re-exports. [#2518]\n\n[#2518]: https://github.com/actix/actix-web/pull/2518\n\n## 0.1.0-beta.8\n\n- No significant changes since `0.1.0-beta.7`.\n\n## 0.1.0-beta.7\n\n- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]\n\n[#2408]: https://github.com/actix/actix-web/pull/2408\n\n## 0.1.0-beta.6\n\n- No significant changes from `0.1.0-beta.5`.\n\n## 0.1.0-beta.5\n\n- Updated rustls to v0.20. [#2414]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n[#2414]: https://github.com/actix/actix-web/pull/2414\n\n## 0.1.0-beta.4\n\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n## 0.1.0-beta.3\n\n- No significant changes from `0.1.0-beta.2`.\n\n## 0.1.0-beta.2\n\n- No significant changes from `0.1.0-beta.1`.\n\n## 0.1.0-beta.1\n\n- Move integration testing structs from `actix-web`. [#2112]\n\n[#2112]: https://github.com/actix/actix-web/pull/2112\n"
  },
  {
    "path": "actix-test/Cargo.toml",
    "content": "[package]\nname = \"actix-test\"\nversion = \"0.1.5\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\", \"Rob Ede <robjtede@icloud.com>\"]\ndescription = \"Integration testing tools for Actix Web applications\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\", \"futures\"]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\ncategories = [\n  \"network-programming\",\n  \"asynchronous\",\n  \"web-programming::http-server\",\n  \"web-programming::websocket\",\n]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix_codec::*\",\n  \"actix_http_test::*\",\n  \"actix_http::*\",\n  \"actix_service::*\",\n  \"actix_web::*\",\n  \"awc::*\",\n  \"bytes::*\",\n  \"futures_core::*\",\n  \"http::*\",\n  \"openssl::*\",\n  \"rustls::*\",\n  \"tokio::*\",\n]\n\n[features]\ndefault = []\n\n# TLS via Rustls v0.20\nrustls = [\"rustls-0_20\"]\n# TLS via Rustls v0.20\nrustls-0_20 = [\"tls-rustls-0_20\", \"actix-http/rustls-0_20\", \"awc/rustls-0_20\"]\n# TLS via Rustls v0.21\nrustls-0_21 = [\"tls-rustls-0_21\", \"actix-http/rustls-0_21\", \"awc/rustls-0_21\"]\n# TLS via Rustls v0.22\nrustls-0_22 = [\"tls-rustls-0_22\", \"actix-http/rustls-0_22\", \"awc/rustls-0_22-webpki-roots\"]\n# TLS via Rustls v0.23\nrustls-0_23 = [\"tls-rustls-0_23\", \"actix-http/rustls-0_23\", \"awc/rustls-0_23-webpki-roots\"]\n\n# TLS via OpenSSL\nopenssl = [\"tls-openssl\", \"actix-http/openssl\", \"awc/openssl\"]\n\n[dependencies]\nactix-codec = \"0.5\"\nactix-http = \"3.7\"\nactix-http-test = \"3\"\nactix-rt = \"2.1\"\nactix-service = \"2\"\nactix-utils = \"3\"\nactix-web = { version = \"4.6\", default-features = false, features = [\"cookies\"] }\nawc = { version = \"3.5\", default-features = false, features = [\"cookies\"] }\n\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"std\"] }\nfutures-util = { version = \"0.3.17\", default-features = false, features = [] }\nlog = \"0.4\"\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nserde_urlencoded = \"0.7\"\ntls-openssl = { package = \"openssl\", version = \"0.10.55\", optional = true }\ntls-rustls-0_20 = { package = \"rustls\", version = \"0.20\", optional = true }\ntls-rustls-0_21 = { package = \"rustls\", version = \"0.21\", optional = true }\ntls-rustls-0_22 = { package = \"rustls\", version = \"0.22\", optional = true }\ntls-rustls-0_23 = { package = \"rustls\", version = \"0.23\", default-features = false, optional = true }\ntokio = { version = \"1.38.2\", features = [\"sync\"] }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-test/README.md",
    "content": "# `actix-test`\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-test?label=latest)](https://crates.io/crates/actix-test)\n[![Documentation](https://docs.rs/actix-test/badge.svg?version=0.1.5)](https://docs.rs/actix-test/0.1.5)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-test.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-test/0.1.5/status.svg)](https://deps.rs/crate/actix-test/0.1.5)\n[![Download](https://img.shields.io/crates/d/actix-test.svg)](https://crates.io/crates/actix-test)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n<!-- cargo-rdme start -->\n\nIntegration testing tools for Actix Web applications.\n\nThe main integration testing tool is [`TestServer`]. It spawns a real HTTP server on an unused port and provides methods that use a real HTTP client. Therefore, it is much closer to real-world cases than using `init_service`, which skips HTTP encoding and decoding.\n\n## Examples\n\n```rust\nuse actix_web::{get, web, test, App, HttpResponse, Error, Responder};\n\n#[get(\"/\")]\nasync fn my_handler() -> Result<impl Responder, Error> {\n    Ok(HttpResponse::Ok())\n}\n\n#[actix_rt::test]\nasync fn test_example() {\n    let srv = actix_test::start(||\n        App::new().service(my_handler)\n    );\n\n    let req = srv.get(\"/\");\n    let res = req.send().await.unwrap();\n\n    assert!(res.status().is_success());\n}\n```\n\n<!-- cargo-rdme end -->\n"
  },
  {
    "path": "actix-test/src/lib.rs",
    "content": "//! Integration testing tools for Actix Web applications.\n//!\n//! The main integration testing tool is [`TestServer`]. It spawns a real HTTP server on an\n//! unused port and provides methods that use a real HTTP client. Therefore, it is much closer to\n//! real-world cases than using `init_service`, which skips HTTP encoding and decoding.\n//!\n//! # Examples\n//!\n//! ```\n//! use actix_web::{get, web, test, App, HttpResponse, Error, Responder};\n//!\n//! #[get(\"/\")]\n//! async fn my_handler() -> Result<impl Responder, Error> {\n//!     Ok(HttpResponse::Ok())\n//! }\n//!\n//! #[actix_rt::test]\n//! async fn test_example() {\n//!     let srv = actix_test::start(||\n//!         App::new().service(my_handler)\n//!     );\n//!\n//!     let req = srv.get(\"/\");\n//!     let res = req.send().await.unwrap();\n//!\n//!     assert!(res.status().is_success());\n//! }\n//! ```\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\n#[cfg(feature = \"openssl\")]\nextern crate tls_openssl as openssl;\n\nuse std::{fmt, net, thread, time::Duration};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed};\npub use actix_http::{body::to_bytes, test::TestBuffer};\nuse actix_http::{header::HeaderMap, ws, HttpService, Method, Request, Response};\npub use actix_http_test::unused_addr;\nuse actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};\npub use actix_web::test::{\n    call_and_read_body, call_and_read_body_json, call_service, init_service, ok_service, read_body,\n    read_body_json, status_service, TestRequest,\n};\nuse actix_web::{\n    body::MessageBody,\n    dev::{AppConfig, Server, ServerHandle, Service},\n    rt::{self, System},\n    web, Error,\n};\npub use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector};\nuse futures_core::Stream;\nuse tokio::sync::mpsc;\n\n/// Start default [`TestServer`].\n///\n/// # Examples\n/// ```\n/// use actix_web::{get, web, test, App, HttpResponse, Error, Responder};\n///\n/// #[get(\"/\")]\n/// async fn my_handler() -> Result<impl Responder, Error> {\n///     Ok(HttpResponse::Ok())\n/// }\n///\n/// #[actix_web::test]\n/// async fn test_example() {\n///     let srv = actix_test::start(||\n///         App::new().service(my_handler)\n///     );\n///\n///     let req = srv.get(\"/\");\n///     let res = req.send().await.unwrap();\n///\n///     assert!(res.status().is_success());\n/// }\n/// ```\npub fn start<F, I, S, B>(factory: F) -> TestServer\nwhere\n    F: Fn() -> I + Send + Clone + 'static,\n    I: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig> + 'static,\n    S::Error: Into<Error> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    B: MessageBody + 'static,\n{\n    start_with(TestServerConfig::default(), factory)\n}\n\n/// Start test server with custom configuration\n///\n/// Check [`TestServerConfig`] docs for configuration options.\n///\n/// # Examples\n/// ```\n/// use actix_web::{get, web, test, App, HttpResponse, Error, Responder};\n///\n/// #[get(\"/\")]\n/// async fn my_handler() -> Result<impl Responder, Error> {\n///     Ok(HttpResponse::Ok())\n/// }\n///\n/// #[actix_web::test]\n/// async fn test_example() {\n///     let srv = actix_test::start_with(actix_test::config().h1(), ||\n///         App::new().service(my_handler)\n///     );\n///\n///     let req = srv.get(\"/\");\n///     let res = req.send().await.unwrap();\n///\n///     assert!(res.status().is_success());\n/// }\n/// ```\npub fn start_with<F, I, S, B>(cfg: TestServerConfig, factory: F) -> TestServer\nwhere\n    F: Fn() -> I + Send + Clone + 'static,\n    I: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig> + 'static,\n    S::Error: Into<Error> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    B: MessageBody + 'static,\n{\n    // for sending handles and server info back from the spawned thread\n    let (started_tx, started_rx) = std::sync::mpsc::channel();\n\n    // for signaling the shutdown of spawned server and system\n    let (thread_stop_tx, thread_stop_rx) = mpsc::channel(1);\n\n    let tls = match cfg.stream {\n        StreamType::Tcp => false,\n        #[cfg(feature = \"openssl\")]\n        StreamType::Openssl(_) => true,\n        #[cfg(feature = \"rustls-0_20\")]\n        StreamType::Rustls020(_) => true,\n        #[cfg(feature = \"rustls-0_21\")]\n        StreamType::Rustls021(_) => true,\n        #[cfg(feature = \"rustls-0_22\")]\n        StreamType::Rustls022(_) => true,\n        #[cfg(feature = \"rustls-0_23\")]\n        StreamType::Rustls023(_) => true,\n    };\n\n    let client_cfg = cfg.clone();\n\n    // run server in separate orphaned thread\n    thread::spawn(move || {\n        rt::System::new().block_on(async move {\n            let tcp = net::TcpListener::bind((cfg.listen_address.clone(), cfg.port)).unwrap();\n            let local_addr = tcp.local_addr().unwrap();\n            let factory = factory.clone();\n            let srv_cfg = cfg.clone();\n            let timeout = cfg.client_request_timeout;\n\n            let builder = Server::build()\n                .workers(cfg.workers)\n                .disable_signals()\n                .system_exit();\n\n            let srv = match srv_cfg.stream {\n                StreamType::Tcp => match srv_cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .tcp()\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .tcp()\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .tcp()\n                    }),\n                },\n                #[cfg(feature = \"openssl\")]\n                StreamType::Openssl(acceptor) => match cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .openssl(acceptor.clone())\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .openssl(acceptor.clone())\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .openssl(acceptor.clone())\n                    }),\n                },\n                #[cfg(feature = \"rustls-0_20\")]\n                StreamType::Rustls020(config) => match cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls(config.clone())\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls(config.clone())\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls(config.clone())\n                    }),\n                },\n                #[cfg(feature = \"rustls-0_21\")]\n                StreamType::Rustls021(config) => match cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_021(config.clone())\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_021(config.clone())\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_021(config.clone())\n                    }),\n                },\n                #[cfg(feature = \"rustls-0_22\")]\n                StreamType::Rustls022(config) => match cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_22(config.clone())\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_22(config.clone())\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_22(config.clone())\n                    }),\n                },\n                #[cfg(feature = \"rustls-0_23\")]\n                StreamType::Rustls023(config) => match cfg.tp {\n                    HttpVer::Http1 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h1(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_23(config.clone())\n                    }),\n                    HttpVer::Http2 => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .h2(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_23(config.clone())\n                    }),\n                    HttpVer::Both => builder.listen(\"test\", tcp, move || {\n                        let app_cfg =\n                            AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);\n\n                        let fac = factory()\n                            .into_factory()\n                            .map_err(|err| err.into().error_response());\n\n                        HttpService::build()\n                            .client_request_timeout(timeout)\n                            .finish(map_config(fac, move |_| app_cfg.clone()))\n                            .rustls_0_23(config.clone())\n                    }),\n                },\n            }\n            .expect(\"test server could not be created\");\n\n            let srv = srv.run();\n            started_tx\n                .send((System::current(), srv.handle(), local_addr))\n                .unwrap();\n\n            // drive server loop\n            srv.await.unwrap();\n\n            // notify TestServer that server and system have shut down\n            // all thread managed resources should be dropped at this point\n        });\n\n        #[allow(clippy::let_underscore_future)]\n        let _ = thread_stop_tx.send(());\n    });\n\n    let (system, server, addr) = started_rx.recv().unwrap();\n\n    let client = {\n        let connector = {\n            #[cfg(feature = \"openssl\")]\n            {\n                use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};\n\n                let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n                builder.set_verify(SslVerifyMode::NONE);\n                let _ = builder\n                    .set_alpn_protos(b\"\\x02h2\\x08http/1.1\")\n                    .map_err(|err| log::error!(\"Can not set alpn protocol: {err:?}\"));\n                Connector::new()\n                    .conn_lifetime(Duration::from_secs(0))\n                    .timeout(Duration::from_millis(30000))\n                    .openssl(builder.build())\n            }\n            #[cfg(not(feature = \"openssl\"))]\n            {\n                Connector::new()\n                    .conn_lifetime(Duration::from_secs(0))\n                    .timeout(Duration::from_millis(30000))\n            }\n        };\n\n        let mut client_builder = Client::builder().connector(connector);\n\n        if client_cfg.disable_redirects {\n            client_builder = client_builder.disable_redirects();\n        }\n\n        client_builder.finish()\n    };\n\n    TestServer {\n        server,\n        thread_stop_rx,\n        client,\n        system,\n        addr,\n        tls,\n    }\n}\n\n#[derive(Debug, Clone)]\nenum HttpVer {\n    Http1,\n    Http2,\n    Both,\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Clone)]\nenum StreamType {\n    Tcp,\n    #[cfg(feature = \"openssl\")]\n    Openssl(openssl::ssl::SslAcceptor),\n    #[cfg(feature = \"rustls-0_20\")]\n    Rustls020(tls_rustls_0_20::ServerConfig),\n    #[cfg(feature = \"rustls-0_21\")]\n    Rustls021(tls_rustls_0_21::ServerConfig),\n    #[cfg(feature = \"rustls-0_22\")]\n    Rustls022(tls_rustls_0_22::ServerConfig),\n    #[cfg(feature = \"rustls-0_23\")]\n    Rustls023(tls_rustls_0_23::ServerConfig),\n}\n\n/// Create default test server config.\npub fn config() -> TestServerConfig {\n    TestServerConfig::default()\n}\n\n#[derive(Clone)]\npub struct TestServerConfig {\n    tp: HttpVer,\n    stream: StreamType,\n    client_request_timeout: Duration,\n    listen_address: String,\n    port: u16,\n    workers: usize,\n    disable_redirects: bool,\n}\n\nimpl Default for TestServerConfig {\n    fn default() -> Self {\n        TestServerConfig::new()\n    }\n}\n\nimpl TestServerConfig {\n    /// Constructs default server configuration.\n    pub(crate) fn new() -> TestServerConfig {\n        TestServerConfig {\n            tp: HttpVer::Both,\n            stream: StreamType::Tcp,\n            client_request_timeout: Duration::from_secs(5),\n            listen_address: \"127.0.0.1\".to_string(),\n            port: 0,\n            workers: 1,\n            disable_redirects: false,\n        }\n    }\n\n    /// Accepts HTTP/1.1 only.\n    pub fn h1(mut self) -> Self {\n        self.tp = HttpVer::Http1;\n        self\n    }\n\n    /// Accepts HTTP/2 only.\n    pub fn h2(mut self) -> Self {\n        self.tp = HttpVer::Http2;\n        self\n    }\n\n    /// Accepts secure connections via OpenSSL.\n    #[cfg(feature = \"openssl\")]\n    pub fn openssl(mut self, acceptor: openssl::ssl::SslAcceptor) -> Self {\n        self.stream = StreamType::Openssl(acceptor);\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(note = \"Renamed to `rustls_0_20()`.\")]\n    #[cfg(feature = \"rustls-0_20\")]\n    pub fn rustls(mut self, config: tls_rustls_0_20::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls020(config);\n        self\n    }\n\n    /// Accepts secure connections via Rustls v0.20.\n    #[cfg(feature = \"rustls-0_20\")]\n    pub fn rustls_0_20(mut self, config: tls_rustls_0_20::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls020(config);\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(note = \"Renamed to `rustls_0_21()`.\")]\n    #[cfg(feature = \"rustls-0_21\")]\n    pub fn rustls_021(mut self, config: tls_rustls_0_21::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls021(config);\n        self\n    }\n\n    /// Accepts secure connections via Rustls v0.21.\n    #[cfg(feature = \"rustls-0_21\")]\n    pub fn rustls_0_21(mut self, config: tls_rustls_0_21::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls021(config);\n        self\n    }\n\n    /// Accepts secure connections via Rustls v0.22.\n    #[cfg(feature = \"rustls-0_22\")]\n    pub fn rustls_0_22(mut self, config: tls_rustls_0_22::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls022(config);\n        self\n    }\n\n    /// Accepts secure connections via Rustls v0.23.\n    #[cfg(feature = \"rustls-0_23\")]\n    pub fn rustls_0_23(mut self, config: tls_rustls_0_23::ServerConfig) -> Self {\n        self.stream = StreamType::Rustls023(config);\n        self\n    }\n\n    /// Sets client timeout for first request.\n    pub fn client_request_timeout(mut self, dur: Duration) -> Self {\n        self.client_request_timeout = dur;\n        self\n    }\n\n    /// Sets the address the server will listen on.\n    ///\n    /// By default, only listens on `127.0.0.1`.\n    pub fn listen_address(mut self, addr: impl Into<String>) -> Self {\n        self.listen_address = addr.into();\n        self\n    }\n\n    /// Sets test server port.\n    ///\n    /// By default, a random free port is determined by the OS.\n    pub fn port(mut self, port: u16) -> Self {\n        self.port = port;\n        self\n    }\n\n    /// Sets number of workers for the test server.\n    ///\n    /// By default, the server uses 1 worker\n    pub fn workers(mut self, workers: usize) -> Self {\n        self.workers = workers;\n        self\n    }\n\n    /// Instruct the client to not follow redirects.\n    ///\n    /// By default, the client will follow up to 10 consecutive redirects\n    /// before giving up.\n    pub fn disable_redirects(mut self) -> Self {\n        self.disable_redirects = true;\n        self\n    }\n}\n\n/// A basic HTTP server controller that simplifies the process of writing integration tests for\n/// Actix Web applications.\n///\n/// See [`start`] for usage example.\npub struct TestServer {\n    server: ServerHandle,\n    thread_stop_rx: mpsc::Receiver<()>,\n    client: awc::Client,\n    system: rt::System,\n    addr: net::SocketAddr,\n    tls: bool,\n}\n\nimpl TestServer {\n    /// Construct test server url\n    pub fn addr(&self) -> net::SocketAddr {\n        self.addr\n    }\n\n    /// Construct test server url\n    pub fn url(&self, uri: &str) -> String {\n        let scheme = if self.tls { \"https\" } else { \"http\" };\n\n        if uri.starts_with('/') {\n            format!(\"{}://{}{}\", scheme, self.addr, uri)\n        } else {\n            format!(\"{}://{}/{}\", scheme, self.addr, uri)\n        }\n    }\n\n    /// Create `GET` request.\n    pub fn get(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.get(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `POST` request.\n    pub fn post(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.post(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `HEAD` request.\n    pub fn head(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.head(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `PUT` request.\n    pub fn put(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.put(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `PATCH` request.\n    pub fn patch(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.patch(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `DELETE` request.\n    pub fn delete(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.delete(self.url(path.as_ref()).as_str())\n    }\n\n    /// Create `OPTIONS` request.\n    pub fn options(&self, path: impl AsRef<str>) -> ClientRequest {\n        self.client.options(self.url(path.as_ref()).as_str())\n    }\n\n    /// Connect request with given method and path.\n    pub fn request(&self, method: Method, path: impl AsRef<str>) -> ClientRequest {\n        self.client.request(method, path.as_ref())\n    }\n\n    pub async fn load_body<S>(\n        &mut self,\n        mut response: ClientResponse<S>,\n    ) -> Result<web::Bytes, PayloadError>\n    where\n        S: Stream<Item = Result<web::Bytes, PayloadError>> + Unpin + 'static,\n    {\n        response.body().limit(10_485_760).await\n    }\n\n    /// Connect to WebSocket server at a given path.\n    pub async fn ws_at(\n        &mut self,\n        path: &str,\n    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {\n        let url = self.url(path);\n        let connect = self.client.ws(url).connect();\n        connect.await.map(|(_, framed)| framed)\n    }\n\n    /// Connect to a WebSocket server.\n    pub async fn ws(\n        &mut self,\n    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {\n        self.ws_at(\"/\").await\n    }\n\n    /// Get default HeaderMap of Client.\n    ///\n    /// Returns Some(&mut HeaderMap) when Client object is unique\n    /// (No other clone of client exists at the same time).\n    pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {\n        self.client.headers()\n    }\n\n    /// Stop HTTP server.\n    ///\n    /// Waits for spawned `Server` and `System` to shutdown (force) shutdown.\n    pub async fn stop(mut self) {\n        // signal server to stop\n        self.server.stop(false).await;\n\n        // also signal system to stop\n        // though this is handled by `ServerBuilder::exit_system` too\n        self.system.stop();\n\n        // wait for thread to be stopped but don't care about result\n        let _ = self.thread_stop_rx.recv().await;\n    }\n}\n\nimpl Drop for TestServer {\n    fn drop(&mut self) {\n        // calls in this Drop impl should be enough to shut down the server, system, and thread\n        // without needing to await anything\n\n        // signal server to stop\n        #[allow(clippy::let_underscore_future)]\n        let _ = self.server.stop(true);\n\n        // signal system to stop\n        self.system.stop();\n    }\n}\n"
  },
  {
    "path": "actix-web/CHANGES.md",
    "content": "# Changelog\n\n## Unreleased\n\n- Panic when calling `Route::to()` or `Route::service()` after `Route::wrap()` to prevent silently dropping route middleware. [#3944]\n- Fix `HttpRequest::{match_pattern,match_name}` reporting path-only matches when route guards disambiguate overlapping resources. [#3346]\n- Fix `Readlines` handling of lines split across payload chunks so combined line limits are enforced and complete lines are yielded.\n\n[#3944]: https://github.com/actix/actix-web/pull/3944\n[#3346]: https://github.com/actix/actix-web/issues/3346\n\n## 4.13.0\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n- Improve HTTP/2 upload throughput by increasing default flow control window sizes. [#3638]\n- Add `HttpServer::{h2_initial_window_size, h2_initial_connection_window_size}` methods for tuning. [#3638]\n- Add `HttpRequest::url_for_map` and `HttpRequest::url_for_iter` methods for named URL parameters. [#3895]\n- Ignore unparsable cookies in `Cookie` request header.\n- Add `experimental-introspection` feature to report configured routes [#3594]\n- Add config/method for `TCP_NODELAY`. [#3918]\n- Fix panic when `NormalizePath` rewrites a scoped dynamic path before extraction (e.g., `scope(\"{tail:.*}\")` + `Path<String>`). [#3562]\n- Do not compress 206 Partial Content responses. [#3191]\n\n[#3895]: https://github.com/actix/actix-web/pull/3895\n[#3594]: https://github.com/actix/actix-web/pull/3594\n[#3918]: https://github.com/actix/actix-web/pull/3918\n[#3638]: https://github.com/actix/actix-web/issues/3638\n[#3562]: https://github.com/actix/actix-web/issues/3562\n[#3191]: https://github.com/actix/actix-web/issues/3191\n\n## 4.12.1\n\n- Correct `actix-http` dependency requirement.\n\n## 4.12.0\n\n- `actix_web::response::builder::HttpResponseBuilder::streaming()` now sets `Content-Type` to `application/octet-stream` if `Content-Type` does not exist.\n- `actix_web::response::builder::HttpResponseBuilder::streaming()` now calls `actix_web::response::builder::HttpResponseBuilder::no_chunking()` and returns `SizedStream` if `Content-Length` is set by user.\n- Add `ws` crate feature (on-by-default) which forwards to `actix-http` and guards some of its `ResponseError` impls.\n- Add public export for `EitherExtractError` in `error` module.\n\n## 4.11.0\n\n- Add `Logger::log_level()` method.\n- Improve handling of non-UTF-8 header values in `Logger` middleware.\n- Add `HttpServer::shutdown_signal()` method.\n- Mark `HttpServer` as `#[must_use]`.\n- Allow SVG images to be compressed by the `Compress` middleware.\n- Ignore `Host` header in `Host` guard when connection protocol is HTTP/2.\n- Re-export `mime` dependency.\n- Update `brotli` dependency to `8`.\n\n## 4.10.2\n\n- No significant changes since `4.10.1`.\n\n## 4.10.1\n\n- No significant changes since `4.10.0`.\n\n## 4.10.0\n\n### Added\n\n- Implement `Responder` for `Result<(), E: Into<Error>>`. Returning `Ok(())` responds with HTTP 204 No Content.\n\n### Changed\n\n- On Windows, an error is now returned from `HttpServer::bind()` (or TLS variants) when binding to a socket that's already in use.\n- Update `brotli` dependency to `7`.\n- Minimum supported Rust version (MSRV) is now 1.75.\n\n## 4.9.0\n\n### Added\n\n- Add `middleware::from_fn()` helper.\n- Add `web::ThinData` extractor.\n\n## 4.8.0\n\n### Added\n\n- Add `web::Html` responder.\n- Add `HttpRequest::full_url()` method to get the complete URL of the request.\n\n### Fixed\n\n- Always remove port from return value of `ConnectionInfo::realip_remote_addr()` when handling IPv6 addresses. from the `Forwarded` header.\n- The `UrlencodedError::ContentType` variant (relevant to the `Form` extractor) now uses the 415 (Media Type Unsupported) status code in it's `ResponseError` implementation.\n- Apply `HttpServer::max_connection_rate()` setting when using rustls v0.22 or v0.23.\n\n## 4.7.0\n\n### Added\n\n- Add `#[scope]` macro.\n- Add `middleware::Identity` type.\n- Add `CustomizeResponder::add_cookie()` method.\n- Add `guard::GuardContext::app_data()` method.\n- Add `compat-routing-macros-force-pub` crate feature which (on-by-default) which, when disabled, causes handlers to inherit their attached function's visibility.\n- Add `compat` crate feature group (on-by-default) which, when disabled, helps with transitioning to some planned v5.0 breaking changes, starting only with `compat-routing-macros-force-pub`.\n- Implement `From<Box<dyn ResponseError>>` for `Error`.\n\n## 4.6.0\n\n### Added\n\n- Add `unicode` crate feature (on-by-default) to switch between `regex` and `regex-lite` as a trade-off between full unicode support and binary size.\n- Add `rustls-0_23` crate feature.\n- Add `HttpServer::{bind_rustls_0_23, listen_rustls_0_23}()` builder methods.\n- Add `HttpServer::tls_handshake_timeout()` builder method for `rustls-0_22` and `rustls-0_23`.\n\n### Changed\n\n- Update `brotli` dependency to `6`.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n### Fixed\n\n- Avoid type confusion with `rustls` in some circumstances.\n\n## 4.5.1\n\n### Fixed\n\n- Fix missing import when using enabling Rustls v0.22 support.\n\n## 4.5.0\n\n### Added\n\n- Add `rustls-0_22` crate feature.\n- Add `HttpServer::{bind_rustls_0_22, listen_rustls_0_22}()` builder methods.\n\n## 4.4.1\n\n### Changed\n\n- Updated `zstd` dependency to `0.13`.\n- Compression middleware now prefers brotli over zstd over gzip.\n\n### Fixed\n\n- Fix validation of `Json` extractor when `JsonConfig::validate_content_type()` is set to false.\n\n## 4.4.0\n\n### Added\n\n- Add `HttpServer::{bind, listen}_auto_h2c()` methods behind new `http2` crate feature.\n- Add `HttpServer::{bind, listen}_rustls_021()` methods for Rustls v0.21 support behind new `rustls-0_21` crate feature.\n- Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards.\n- Add `web::Payload::to_bytes[_limited]()` helper methods.\n- Add missing constructors on `HttpResponse` for several status codes.\n- Add `http::header::ContentLength` typed header.\n- Implement `Default` for `web::Data`.\n- Implement `serde::Deserialize` for `web::Data`.\n- Add `rustls-0_20` crate feature, which the existing `rustls` feature now aliases.\n\n### Changed\n\n- Handler functions can now receive up to 16 extractor parameters.\n- The `Compress` middleware no longer compresses image or video content.\n- Hide sensitive header values in `HttpRequest`'s `Debug` output.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 4.3.1\n\n### Added\n\n- Add support for custom methods with the `#[route]` macro. [#2969]\n\n[#2969]: https://github.com/actix/actix-web/pull/2969\n\n## 4.3.0\n\n### Added\n\n- Add `ContentDisposition::attachment()` constructor. [#2867]\n- Add `ErrorHandlers::default_handler()` (as well as `default_handler_{server, client}()`) to make registering handlers for groups of response statuses easier. [#2784]\n- Add `Logger::custom_response_replace()`. [#2631]\n- Add rudimentary redirection service at `web::redirect()` / `web::Redirect`. [#1961]\n- Add `guard::Acceptable` for matching against `Accept` header MIME types. [#2265]\n- Add fallible versions of `test` helpers: `try_call_service()`, `try_call_and_read_body_json()`, `try_read_body()`, and `try_read_body_json()`. [#2961]\n\n### Fixed\n\n- Add `Allow` header to `Resource`'s default responses when no routes are matched. [#2949]\n\n[#1961]: https://github.com/actix/actix-web/pull/1961\n[#2265]: https://github.com/actix/actix-web/pull/2265\n[#2631]: https://github.com/actix/actix-web/pull/2631\n[#2784]: https://github.com/actix/actix-web/pull/2784\n[#2867]: https://github.com/actix/actix-web/pull/2867\n[#2949]: https://github.com/actix/actix-web/pull/2949\n[#2961]: https://github.com/actix/actix-web/pull/2961\n\n## 4.2.1\n\n### Fixed\n\n- Bump minimum version of `actix-http` dependency to fix compatibility issue. [#2871]\n\n[#2871]: https://github.com/actix/actix-web/pull/2871\n\n## 4.2.0\n\n### Added\n\n- Add `#[routes]` macro to support multiple paths for one handler. [#2718]\n- Add `ServiceRequest::{parts, request}()` getter methods. [#2786]\n- Add configuration options for TLS handshake timeout via `HttpServer::{rustls, openssl}_with_config` methods. [#2752]\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n[#2718]: https://github.com/actix/actix-web/pull/2718\n[#2752]: https://github.com/actix/actix-web/pull/2752\n[#2786]: https://github.com/actix/actix-web/pull/2786\n\n## 4.1.0\n\n### Added\n\n- Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647]\n- Add `Route::wrap()` to allow individual routes to use middleware. [#2725]\n- Add `ServiceConfig::default_service()`. [#2338] [#2743]\n- Implement `ResponseError` for `std::convert::Infallible`\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.\n\n### Fixed\n\n- Clear connection-level data on `HttpRequest` drop. [#2742]\n\n[#2338]: https://github.com/actix/actix-web/pull/2338\n[#2647]: https://github.com/actix/actix-web/pull/2647\n[#2725]: https://github.com/actix/actix-web/pull/2725\n[#2742]: https://github.com/actix/actix-web/pull/2742\n[#2743]: https://github.com/actix/actix-web/pull/2743\n\n## 4.0.1\n\n### Fixed\n\n- Use stable version in readme example.\n\n## 4.0.0\n\n### Dependencies\n\n- Updated `actix-*` to Tokio v1-based versions. [#1813]\n- Updated `actix-web-codegen` to `4.0.0`.\n- Updated `cookie` to `0.16`. [#2555]\n- Updated `language-tags` to `0.3`.\n- Updated `rand` to `0.8`.\n- Updated `rustls` to `0.20`. [#2414]\n- Updated `tokio` to `1`.\n\n### Added\n\n- Crate Features:\n  - `cookies`; enabled by default. [#2619]\n  - `compress-brotli`; enabled by default. [#2618]\n  - `compress-gzip`; enabled by default. [#2618]\n  - `compress-zstd`; enabled by default. [#2618]\n  - `macros`; enables routing and runtime macros, enabled by default. [#2619]\n- Types:\n  - `CustomizeResponder` for customizing response. [#2510]\n  - `dev::ServerHandle` re-export from `actix-server`. [#2442]\n  - `dev::ServiceFactory` re-export from `actix-service`. [#2325]\n  - `guard::GuardContext` for use with the `Guard` trait. [#2552]\n  - `http::header::AcceptEncoding` typed header. [#2482]\n  - `http::header::Range` typed header. [#2485]\n  - `http::KeepAlive` re-export from `actix-http`. [#2625]\n  - `middleware::Compat` that boxes middleware types like `Logger` and `Compress` to be used with constrained type bounds. [#1865]\n  - `web::Header` extractor for extracting typed HTTP headers in handlers. [#2094]\n- Methods:\n  - `dev::ServiceRequest::guard_ctx()` for obtaining a guard context. [#2552]\n  - `dev::ServiceRequest::parts_mut()`. [#2177]\n  - `dev::ServiceResponse::map_into_{left,right}_body()` and `HttpResponse::map_into_boxed_body()`. [#2468]\n  - `Either<web::Json<T>, web::Form<T>>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either<web::Form<T>, web::Json<T>>`. [#1894]\n  - `http::header::AcceptLanguage::{ranked, preference}()`. [#2480]\n  - `HttpResponse::add_removal_cookie()`. [#2586]\n  - `HttpResponse::map_into_{left,right}_body()` and `HttpResponse::map_into_boxed_body()`. [#2468]\n  - `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]\n  - `middleware::Logger::log_target()` to allow customize. [#2594]\n  - `Responder::customize()` trait method that wraps responder in `CustomizeResponder`. [#2510]\n  - `Route::service()` for using hand-written services as handlers. [#2262]\n  - `ServiceResponse::into_parts()`. [#2499]\n  - `TestServer::client_headers()` method. [#2097]\n  - `web::ServiceConfig::configure()` to allow easy nesting of configuration functions. [#1988]\n- Trait Implementations:\n  - Implement `Debug` for `DefaultHeaders`. [#2510]\n  - Implement `FromRequest` for `ConnectionInfo` and `PeerAddr`. [#2263]\n  - Implement `FromRequest` for `Method`. [#2263]\n  - Implement `FromRequest` for `Uri`. [#2263]\n  - Implement `Hash` for `http::header::Encoding`. [#2501]\n  - Implement `Responder` for `Vec<u8>`. [#2625]\n- Misc:\n  - `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]\n  - Enable registering a vec of services of the same type to `App` [#1933]\n  - Add `services!` macro for helping register multiple services to `App`. [#1933]\n  - Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]\n  - Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]\n\n### Changed\n\n- Functions:\n  - `guard::fn_guard` functions now receives a `&GuardContext`. [#2552]\n  - `guard::Not` is now generic over the type of guard it wraps. [#2552]\n  - `test::{call_service, read_response, read_response_json, send_request}()` now receive a `&Service`. [#1905]\n  - Some guard functions now return `impl Guard` and their concrete types are made private: `guard::Header` and all the method guards. [#2552]\n  - Rename `test::{default_service => status_service}()`. Old name is deprecated. [#2518]\n  - Rename `test::{read_response_json => call_and_read_body_json}()`. Old name is deprecated. [#2518]\n  - Rename `test::{read_response => call_and_read_body}()`. Old name is deprecated. [#2518]\n- Traits:\n  - `guard::Guard::check` now receives a `&GuardContext`. [#2552]\n  - `FromRequest::Config` associated type was removed. [#2233]\n  - `Responder` trait has been reworked and now `Response`/`HttpResponse` synchronously, making it simpler and more performant. [#1891]\n  - Rename `Factory` trait to `Handler`. [#1852]\n- Types:\n  - `App`'s `B` (body) type parameter been removed. As a result, `App`s can be returned from functions now. [#2493]\n  - `Compress` middleware's response type is now `EitherBody<Encoder<B>>`. [#2448]\n  - `error::BlockingError` is now a unit struct. It's now only triggered when blocking thread pool has shutdown. [#1957]\n  - `ErrorHandlerResponse`'s response variants now use `ServiceResponse<EitherBody<B>>`. [#2515]\n  - `ErrorHandlers` middleware's response types now use `ServiceResponse<EitherBody<B>>`. [#2515]\n  - `http::header::Encoding` now only represents `Content-Encoding` types. [#2501]\n  - `middleware::Condition` gained a broader middleware compatibility. [#2635]\n  - `Resource` no longer require service body type to be boxed. [#2526]\n  - `Scope` no longer require service body type to be boxed. [#2523]\n  - `web::Path`s inner field is now private. [#1894]\n  - `web::Payload`'s inner field is now private. [#2384]\n  - Error enums are now marked `#[non_exhaustive]`. [#2148]\n- Enum Variants:\n  - `Either` now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]\n  - Include size and limits in `JsonPayloadError::Overflow`. [#2162]\n- Methods:\n  - `App::data()` is deprecated; `App::app_data()` should be preferred. [#2271]\n  - `dev::JsonBody::new()` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json<T>` extractor. [#2010]\n  - `dev::ServiceRequest::{into_parts, from_parts}()` can no longer fail. [#1893]\n  - `dev::ServiceRequest::from_request` can no longer fail. [#1893]\n  - `dev::ServiceResponse::error_response()` now uses body type of `BoxBody`. [#2201]\n  - `dev::ServiceResponse::map_body()` closure receives and returns `B` instead of `ResponseBody<B>`. [#2201]\n  - `http::header::ContentType::html()` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423]\n  - `HttpRequest::url_for`'s constructed URLs no longer contain query or fragment. [#2430]\n  - `HttpResponseBuilder::json()` can now receive data by value and reference. [#1903]\n  - `HttpServer::{listen_rustls, bind_rustls}()` now honor the ALPN protocols in the configuration parameter. [#2226]\n  - `middleware::NormalizePath()` now will not try to normalize URIs with no valid path [#2246]\n  - `test::TestRequest::param()` now accepts more than just static strings. [#2172]\n  - `web::Data::into_inner()` and `Data::get_ref()` no longer require `T: Sized`. [#2403]\n  - Rename `HttpServer::{client_timeout => client_request_timeout}()`. [#2611]\n  - Rename `HttpServer::{client_shutdown => client_disconnect_timeout}()`. [#2611]\n  - Rename `http::header::Accept::{mime_precedence => ranked}()`. [#2480]\n  - Rename `http::header::Accept::{mime_preference => preference}()`. [#2480]\n  - Rename `middleware::DefaultHeaders::{content_type => add_content_type}()`. [#1875]\n  - Rename `dev::ConnectionInfo::{remote_addr => peer_addr}`, deprecating the old name. [#2554]\n- Trait Implementations:\n  - `HttpResponse` can now be used as a `Responder` with any body type. [#2567]\n- Misc:\n  - Maximum number of handler extractors has increased to 12. [#2582]\n  - The default `TrailingSlash` behavior is now `Trim`, in line with existing documentation. See migration guide for implications. [#1875]\n  - `Result` extractor wrapper can now convert error types. [#2581]\n  - Compress middleware will return `406 Not Acceptable` when no content encoding is acceptable to the client. [#2344]\n  - Adjusted default JSON payload limit to 2MB (from 32kb). [#2162]\n  - All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<BoxBody>>`. [#2253]\n  - All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]\n  - Improve spec compliance of `dev::ConnectionInfo` extractor. [#2282]\n  - Associated types in `FromRequest` implementation for `Option` and `Result` have changed. [#2581]\n  - Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]\n  - Minimum supported Rust version (MSRV) is now 1.54.\n\n### Fixed\n\n- Auto-negotiation of content encoding is more fault-tolerant when using the `Compress` middleware. [#2501]\n- Scope and Resource middleware can access data items set on their own layer. [#2288]\n- Multiple calls to `App::data()` with the same type now keeps the latest call's data. [#1906]\n- Typed headers containing lists that require one or more items now enforce this minimum. [#2482]\n- `dev::ConnectionInfo::peer_addr` will no longer return the port number. [#2554]\n- `dev::ConnectionInfo::realip_remote_addr` will no longer return the port number if sourcing the IP from the peer's socket address. [#2554]\n- Accept wildcard `*` items in `AcceptLanguage`. [#2480]\n- Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448]\n- Fix quality parse error in `http::header::AcceptEncoding` typed header. [#2344]\n- Double ampersand in `middleware::Logger` format is escaped correctly. [#2067]\n- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812]\n\n### Security\n\n- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].\n\n[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html\n\n### Removed\n\n- Crate Features:\n  - `compress` feature. [#2065]\n- Functions:\n  - `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518]\n  - `test::start_with`; moved to new `actix-test` crate. [#2112]\n  - `test::start`; moved to new `actix-test` crate. [#2112]\n  - `test::unused_addr`; moved to new `actix-test` crate. [#2112]\n- Traits:\n  - `BodyEncoding`; signalling content encoding is now only done via the `Content-Encoding` header. [#2565]\n- Types:\n  - `dev::{BodySize, MessageBody, SizedStream}` re-exports; they are exposed through the `body` module. [#2468]\n  - `EitherExtractError` direct export. [#2510]\n  - `rt::{Arbiter, ArbiterHandle}` re-exports. [#2619]\n  - `test::TestServer`; moved to new `actix-test` crate. [#2112]\n  - `test::TestServerConfig`; moved to new `actix-test` crate. [#2112]\n  - `web::HttpRequest` re-export. [#2663]\n  - `web::HttpResponse` re-export. [#2663]\n- Methods:\n  - `AppService::set_service_data`; for custom HTTP service factories adding application data, use the layered data model by calling `ServiceRequest::add_data_container` when handling requests instead. [#1906]\n  - `dev::ConnectionInfo::get`. [#2487]\n  - `dev::ServiceResponse::checked_expr`. [#2401]\n  - `HttpRequestBuilder::del_cookie`. [#2591]\n  - `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201]\n  - `HttpResponseBuilder::json2()`. [#1903]\n  - `middleware::Compress::new`; restricting compression algorithm is done through feature flags. [#2501]\n  - `test::TestRequest::with_header()`; use `test::TestRequest::default().insert_header()`. [#1869]\n- Trait Implementations:\n  - Implementation of `From<either::Either>` for `Either` crate. [#2516]\n  - Implementation of `Future` for `HttpResponse`. [#2601]\n- Misc:\n  - The `client` module was removed; use the `awc` crate directly. [871ca5e4]\n  - `middleware::{normalize, err_handlers}` modules; all necessary middleware types are now exposed in the `middleware` module.\n\n[#1812]: https://github.com/actix/actix-web/pull/1812\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1852]: https://github.com/actix/actix-web/pull/1852\n[#1865]: https://github.com/actix/actix-web/pull/1865\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1875]: https://github.com/actix/actix-web/pull/1875\n[#1878]: https://github.com/actix/actix-web/pull/1878\n[#1891]: https://github.com/actix/actix-web/pull/1891\n[#1893]: https://github.com/actix/actix-web/pull/1893\n[#1894]: https://github.com/actix/actix-web/pull/1894\n[#1903]: https://github.com/actix/actix-web/pull/1903\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1906]: https://github.com/actix/actix-web/pull/1906\n[#1933]: https://github.com/actix/actix-web/pull/1933\n[#1957]: https://github.com/actix/actix-web/pull/1957\n[#1957]: https://github.com/actix/actix-web/pull/1957\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#1988]: https://github.com/actix/actix-web/pull/1988\n[#2010]: https://github.com/actix/actix-web/pull/2010\n[#2065]: https://github.com/actix/actix-web/pull/2065\n[#2067]: https://github.com/actix/actix-web/pull/2067\n[#2093]: https://github.com/actix/actix-web/pull/2093\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2097]: https://github.com/actix/actix-web/pull/2097\n[#2112]: https://github.com/actix/actix-web/pull/2112\n[#2148]: https://github.com/actix/actix-web/pull/2148\n[#2162]: https://github.com/actix/actix-web/pull/2162\n[#2172]: https://github.com/actix/actix-web/pull/2172\n[#2177]: https://github.com/actix/actix-web/pull/2177\n[#2200]: https://github.com/actix/actix-web/pull/2200\n[#2201]: https://github.com/actix/actix-web/pull/2201\n[#2201]: https://github.com/actix/actix-web/pull/2201\n[#2233]: https://github.com/actix/actix-web/pull/2233\n[#2246]: https://github.com/actix/actix-web/pull/2246\n[#2250]: https://github.com/actix/actix-web/pull/2250\n[#2253]: https://github.com/actix/actix-web/pull/2253\n[#2262]: https://github.com/actix/actix-web/pull/2262\n[#2263]: https://github.com/actix/actix-web/pull/2263\n[#2271]: https://github.com/actix/actix-web/pull/2271\n[#2282]: https://github.com/actix/actix-web/pull/2282\n[#2288]: https://github.com/actix/actix-web/pull/2288\n[#2325]: https://github.com/actix/actix-web/pull/2325\n[#2344]: https://github.com/actix/actix-web/pull/2344\n[#2362]: https://github.com/actix/actix-web/pull/2362\n[#2379]: https://github.com/actix/actix-web/pull/2379\n[#2384]: https://github.com/actix/actix-web/pull/2384\n[#2401]: https://github.com/actix/actix-web/pull/2401\n[#2403]: https://github.com/actix/actix-web/pull/2403\n[#2409]: https://github.com/actix/actix-web/pull/2409\n[#2414]: https://github.com/actix/actix-web/pull/2414\n[#2423]: https://github.com/actix/actix-web/pull/2423\n[#2430]: https://github.com/actix/actix-web/pull/2430\n[#2442]: https://github.com/actix/actix-web/pull/2442\n[#2446]: https://github.com/actix/actix-web/pull/2446\n[#2448]: https://github.com/actix/actix-web/pull/2448\n[#2468]: https://github.com/actix/actix-web/pull/2468\n[#2474]: https://github.com/actix/actix-web/pull/2474\n[#2480]: https://github.com/actix/actix-web/pull/2480\n[#2482]: https://github.com/actix/actix-web/pull/2482\n[#2484]: https://github.com/actix/actix-web/pull/2484\n[#2485]: https://github.com/actix/actix-web/pull/2485\n[#2487]: https://github.com/actix/actix-web/pull/2487\n[#2491]: https://github.com/actix/actix-web/pull/2491\n[#2492]: https://github.com/actix/actix-web/pull/2492\n[#2493]: https://github.com/actix/actix-web/pull/2493\n[#2499]: https://github.com/actix/actix-web/pull/2499\n[#2501]: https://github.com/actix/actix-web/pull/2501\n[#2510]: https://github.com/actix/actix-web/pull/2510\n[#2515]: https://github.com/actix/actix-web/pull/2515\n[#2516]: https://github.com/actix/actix-web/pull/2516\n[#2518]: https://github.com/actix/actix-web/pull/2518\n[#2523]: https://github.com/actix/actix-web/pull/2523\n[#2526]: https://github.com/actix/actix-web/pull/2526\n[#2552]: https://github.com/actix/actix-web/pull/2552\n[#2554]: https://github.com/actix/actix-web/pull/2554\n[#2555]: https://github.com/actix/actix-web/pull/2555\n[#2565]: https://github.com/actix/actix-web/pull/2565\n[#2567]: https://github.com/actix/actix-web/pull/2567\n[#2569]: https://github.com/actix/actix-web/pull/2569\n[#2581]: https://github.com/actix/actix-web/pull/2581\n[#2582]: https://github.com/actix/actix-web/pull/2582\n[#2584]: https://github.com/actix/actix-web/pull/2584\n[#2585]: https://github.com/actix/actix-web/pull/2585\n[#2586]: https://github.com/actix/actix-web/pull/2586\n[#2591]: https://github.com/actix/actix-web/pull/2591\n[#2594]: https://github.com/actix/actix-web/pull/2594\n[#2601]: https://github.com/actix/actix-web/pull/2601\n[#2611]: https://github.com/actix/actix-web/pull/2611\n[#2619]: https://github.com/actix/actix-web/pull/2619\n[#2625]: https://github.com/actix/actix-web/pull/2625\n[#2635]: https://github.com/actix/actix-web/pull/2635\n[#2659]: https://github.com/actix/actix-web/pull/2659\n[#2663]: https://github.com/actix/actix-web/pull/2663\n[871ca5e4]: https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7\n\n<details>\n<summary>4.0.0 Pre-Releases</summary>\n\n## 4.0.0-rc.3\n\n### Changed\n\n- `middleware::Condition` gained a broader compatibility; `Compat` is needed in fewer cases. [#2635]\n\n### Added\n\n- Implement `Responder` for `Vec<u8>`. [#2625]\n- Re-export `KeepAlive` in `http` mod. [#2625]\n\n[#2625]: https://github.com/actix/actix-web/pull/2625\n[#2635]: https://github.com/actix/actix-web/pull/2635\n\n## 4.0.0-rc.2\n\n### Added\n\n- On-by-default `macros` feature flag to enable routing and runtime macros. [#2619]\n\n### Removed\n\n- `rt::{Arbiter, ArbiterHandle}` re-exports. [#2619]\n\n[#2619]: https://github.com/actix/actix-web/pull/2619\n\n## 4.0.0-rc.1\n\n### Changed\n\n- Rename `HttpServer::{client_timeout => client_request_timeout}`. [#2611]\n- Rename `HttpServer::{client_shutdown => client_disconnect_timeout}`. [#2611]\n\n### Removed\n\n- `impl Future for HttpResponse`. [#2601]\n\n[#2601]: https://github.com/actix/actix-web/pull/2601\n[#2611]: https://github.com/actix/actix-web/pull/2611\n\n## 4.0.0-beta.21\n\n### Added\n\n- `HttpResponse::add_removal_cookie`. [#2586]\n- `Logger::log_target`. [#2594]\n\n### Removed\n\n- `HttpRequest::req_data[_mut]()`; request-local data is still available through `.extensions()`. [#2585]\n- `HttpRequestBuilder::del_cookie`. [#2591]\n\n[#2585]: https://github.com/actix/actix-web/pull/2585\n[#2586]: https://github.com/actix/actix-web/pull/2586\n[#2591]: https://github.com/actix/actix-web/pull/2591\n[#2594]: https://github.com/actix/actix-web/pull/2594\n\n## 4.0.0-beta.20\n\n### Added\n\n- `GuardContext::header` [#2569]\n- `ServiceConfig::configure` to allow easy nesting of configuration functions. [#1988]\n\n### Changed\n\n- `HttpResponse` can now be used as a `Responder` with any body type. [#2567]\n- `Result` extractor wrapper can now convert error types. [#2581]\n- Associated types in `FromRequest` impl for `Option` and `Result` has changed. [#2581]\n- Maximum number of handler extractors has increased to 12. [#2582]\n- Removed bound `<B as MessageBody>::Error: Debug` in test utility functions in order to support returning opaque apps. [#2584]\n\n[#1988]: https://github.com/actix/actix-web/pull/1988\n[#2567]: https://github.com/actix/actix-web/pull/2567\n[#2569]: https://github.com/actix/actix-web/pull/2569\n[#2581]: https://github.com/actix/actix-web/pull/2581\n[#2582]: https://github.com/actix/actix-web/pull/2582\n[#2584]: https://github.com/actix/actix-web/pull/2584\n\n## 4.0.0-beta.19\n\n### Added\n\n- `impl Hash` for `http::header::Encoding`. [#2501]\n- `AcceptEncoding::negotiate()`. [#2501]\n\n### Changed\n\n- `AcceptEncoding::preference` now returns `Option<Preference<Encoding>>`. [#2501]\n- Rename methods `BodyEncoding::{encoding => encode_with, get_encoding => preferred_encoding}`. [#2501]\n- `http::header::Encoding` now only represents `Content-Encoding` types. [#2501]\n\n### Fixed\n\n- Auto-negotiation of content encoding is more fault-tolerant when using the `Compress` middleware. [#2501]\n\n### Removed\n\n- `Compress::new`; restricting compression algorithm is done through feature flags. [#2501]\n- `BodyEncoding` trait; signalling content encoding is now only done via the `Content-Encoding` header. [#2565]\n\n[#2501]: https://github.com/actix/actix-web/pull/2501\n[#2565]: https://github.com/actix/actix-web/pull/2565\n\n## 4.0.0-beta.18\n\n### Changed\n\n- Update `cookie` dependency (re-exported) to `0.16`. [#2555]\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n### Security\n\n- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].\n\n[#2555]: https://github.com/actix/actix-web/pull/2555\n[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html\n\n## 4.0.0-beta.17\n\n### Added\n\n- `guard::GuardContext` for use with the `Guard` trait. [#2552]\n- `ServiceRequest::guard_ctx` for obtaining a guard context. [#2552]\n\n### Changed\n\n- `Guard` trait now receives a `&GuardContext`. [#2552]\n- `guard::fn_guard` functions now receives a `&GuardContext`. [#2552]\n- Some guards now return `impl Guard` and their concrete types are made private: `guard::Header` and all the method guards. [#2552]\n- The `Not` guard is now generic over the type of guard it wraps. [#2552]\n\n### Fixed\n\n- Rename `ConnectionInfo::{remote_addr => peer_addr}`, deprecating the old name. [#2554]\n- `ConnectionInfo::peer_addr` will not return the port number. [#2554]\n- `ConnectionInfo::realip_remote_addr` will not return the port number if sourcing the IP from the peer's socket address. [#2554]\n\n[#2552]: https://github.com/actix/actix-web/pull/2552\n[#2554]: https://github.com/actix/actix-web/pull/2554\n\n## 4.0.0-beta.16\n\n### Changed\n\n- No longer require `Scope` service body type to be boxed. [#2523]\n- No longer require `Resource` service body type to be boxed. [#2526]\n\n[#2523]: https://github.com/actix/actix-web/pull/2523\n[#2526]: https://github.com/actix/actix-web/pull/2526\n\n## 4.0.0-beta.15\n\n### Added\n\n- Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510]\n- Implement `Debug` for `DefaultHeaders`. [#2510]\n\n### Changed\n\n- Align `DefaultHeader` method terminology, deprecating previous methods. [#2510]\n- Response service types in `ErrorHandlers` middleware now use `ServiceResponse<EitherBody<B>>` to allow changing the body type. [#2515]\n- Both variants in `ErrorHandlerResponse` now use `ServiceResponse<EitherBody<B>>`. [#2515]\n- Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518]\n- Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518]\n- Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518]\n- Relax body type and error bounds on test utilities. [#2518]\n\n### Removed\n\n- Top-level `EitherExtractError` export. [#2510]\n- Conversion implementations for `either` crate. [#2516]\n- `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518]\n\n[#2510]: https://github.com/actix/actix-web/pull/2510\n[#2515]: https://github.com/actix/actix-web/pull/2515\n[#2516]: https://github.com/actix/actix-web/pull/2516\n[#2518]: https://github.com/actix/actix-web/pull/2518\n\n## 4.0.0-beta.14\n\n### Added\n\n- Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480]\n- `AcceptEncoding` typed header. [#2482]\n- `Range` typed header. [#2485]\n- `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]\n- `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]\n- Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]\n- `HttpRequest::{req_data,req_data_mut}`. [#2487]\n- `ServiceResponse::into_parts`. [#2499]\n\n### Changed\n\n- Rename `Accept::{mime_precedence => ranked}`. [#2480]\n- Rename `Accept::{mime_preference => preference}`. [#2480]\n- Un-deprecate `App::data_factory`. [#2484]\n- `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430]\n- Remove `B` (body) type parameter on `App`. [#2493]\n- Add `B` (body) type parameter on `Scope`. [#2492]\n- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487]\n\n### Fixed\n\n- Accept wildcard `*` items in `AcceptLanguage`. [#2480]\n- Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468]\n- Typed headers containing lists that require one or more items now enforce this minimum. [#2482]\n\n### Removed\n\n- `ConnectionInfo::get`. [#2487]\n\n[#2430]: https://github.com/actix/actix-web/pull/2430\n[#2468]: https://github.com/actix/actix-web/pull/2468\n[#2480]: https://github.com/actix/actix-web/pull/2480\n[#2482]: https://github.com/actix/actix-web/pull/2482\n[#2484]: https://github.com/actix/actix-web/pull/2484\n[#2485]: https://github.com/actix/actix-web/pull/2485\n[#2487]: https://github.com/actix/actix-web/pull/2487\n[#2491]: https://github.com/actix/actix-web/pull/2491\n[#2492]: https://github.com/actix/actix-web/pull/2492\n[#2493]: https://github.com/actix/actix-web/pull/2493\n[#2499]: https://github.com/actix/actix-web/pull/2499\n\n## 4.0.0-beta.13\n\n### Changed\n\n- Update `actix-tls` to `3.0.0-rc.1`. [#2474]\n\n[#2474]: https://github.com/actix/actix-web/pull/2474\n\n## 4.0.0-beta.12\n\n### Changed\n\n- Compress middleware's response type is now `AnyBody<Encoder<B>>`. [#2448]\n\n### Fixed\n\n- Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448]\n\n### Removed\n\n- `dev::ResponseBody` re-export; is function is replaced by the new `dev::AnyBody` enum. [#2446]\n\n[#2446]: https://github.com/actix/actix-web/pull/2446\n[#2448]: https://github.com/actix/actix-web/pull/2448\n\n## 4.0.0-beta.11\n\n### Added\n\n- Re-export `dev::ServerHandle` from `actix-server`. [#2442]\n\n### Changed\n\n- `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423]\n- Update `actix-server` to `2.0.0-beta.9`. [#2442]\n\n[#2423]: https://github.com/actix/actix-web/pull/2423\n[#2442]: https://github.com/actix/actix-web/pull/2442\n\n## 4.0.0-beta.10\n\n### Added\n\n- Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]\n- `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]\n\n### Changed\n\n- Associated type `FromRequest::Config` was removed. [#2233]\n- Inner field made private on `web::Payload`. [#2384]\n- `Data::into_inner` and `Data::get_ref` no longer requires `T: Sized`. [#2403]\n- Updated rustls to v0.20. [#2414]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n### Removed\n\n- Useless `ServiceResponse::checked_expr` method. [#2401]\n\n[#2233]: https://github.com/actix/actix-web/pull/2233\n[#2362]: https://github.com/actix/actix-web/pull/2362\n[#2384]: https://github.com/actix/actix-web/pull/2384\n[#2401]: https://github.com/actix/actix-web/pull/2401\n[#2403]: https://github.com/actix/actix-web/pull/2403\n[#2409]: https://github.com/actix/actix-web/pull/2409\n[#2414]: https://github.com/actix/actix-web/pull/2414\n\n## 4.0.0-beta.9\n\n### Added\n\n- Re-export actix-service `ServiceFactory` in `dev` module. [#2325]\n\n### Changed\n\n- Compress middleware will return 406 Not Acceptable when no content encoding is acceptable to the client. [#2344]\n- Move `BaseHttpResponse` to `dev::Response`. [#2379]\n- Enable `TestRequest::param` to accept more than just static strings. [#2172]\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n### Fixed\n\n- Fix quality parse error in Accept-Encoding header. [#2344]\n- Re-export correct type at `web::HttpResponse`. [#2379]\n\n[#2172]: https://github.com/actix/actix-web/pull/2172\n[#2325]: https://github.com/actix/actix-web/pull/2325\n[#2344]: https://github.com/actix/actix-web/pull/2344\n[#2379]: https://github.com/actix/actix-web/pull/2379\n\n## 4.0.0-beta.8\n\n### Added\n\n- Add `ServiceRequest::parts_mut`. [#2177]\n- Add extractors for `Uri` and `Method`. [#2263]\n- Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263]\n- Add `Route::service` for using hand-written services as handlers. [#2262]\n\n### Changed\n\n- Change compression algorithm features flags. [#2250]\n- Deprecate `App::data` and `App::data_factory`. [#2271]\n- Smarter extraction of `ConnectionInfo` parts. [#2282]\n\n### Fixed\n\n- Scope and Resource middleware can access data items set on their own layer. [#2288]\n\n[#2177]: https://github.com/actix/actix-web/pull/2177\n[#2250]: https://github.com/actix/actix-web/pull/2250\n[#2271]: https://github.com/actix/actix-web/pull/2271\n[#2262]: https://github.com/actix/actix-web/pull/2262\n[#2263]: https://github.com/actix/actix-web/pull/2263\n[#2282]: https://github.com/actix/actix-web/pull/2282\n[#2288]: https://github.com/actix/actix-web/pull/2288\n\n## 4.0.0-beta.7\n\n### Added\n\n- `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]\n\n### Changed\n\n- Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162]\n- `ServiceResponse::error_response` now uses body type of `Body`. [#2201]\n- `ServiceResponse::checked_expr` now returns a `Result`. [#2201]\n- Update `language-tags` to `0.3`.\n- `ServiceResponse::take_body`. [#2201]\n- `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody<B>` types. [#2201]\n- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<AnyBody>>`. [#2253]\n- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]\n- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226]\n- `middleware::normalize` now will not try to normalize URIs with no valid path [#2246]\n\n### Removed\n\n- `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201]\n\n[#2162]: https://github.com/actix/actix-web/pull/2162\n[#2200]: https://github.com/actix/actix-web/pull/2200\n[#2201]: https://github.com/actix/actix-web/pull/2201\n[#2253]: https://github.com/actix/actix-web/pull/2253\n[#2246]: https://github.com/actix/actix-web/pull/2246\n\n## 4.0.0-beta.6\n\n### Added\n\n- `HttpResponse` and `HttpResponseBuilder` types. [#2065]\n\n### Changed\n\n- Most error types are now marked `#[non_exhaustive]`. [#2148]\n- Methods on `ContentDisposition` that took `T: AsRef<str>` now take `impl AsRef<str>`.\n\n[#2065]: https://github.com/actix/actix-web/pull/2065\n[#2148]: https://github.com/actix/actix-web/pull/2148\n\n## 4.0.0-beta.5\n\n### Added\n\n- `Header` extractor for extracting common HTTP headers in handlers. [#2094]\n- Added `TestServer::client_headers` method. [#2097]\n\n### Changed\n\n- `CustomResponder` would return error as `HttpResponse` when `CustomResponder::with_header` failed instead of skipping. (Only the first error is kept when multiple error occur) [#2093]\n\n### Fixed\n\n- Double ampersand in Logger format is escaped correctly. [#2067]\n\n### Removed\n\n- The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)\n- Integration testing was moved to new `actix-test` crate. Namely these items from the `test` module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112]\n\n[#2067]: https://github.com/actix/actix-web/pull/2067\n[#2093]: https://github.com/actix/actix-web/pull/2093\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2097]: https://github.com/actix/actix-web/pull/2097\n[#2112]: https://github.com/actix/actix-web/pull/2112\n\n## 4.0.0-beta.4\n\n### Changed\n\n- Feature `cookies` is now optional and enabled by default. [#1981]\n- `JsonBody::new` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json<T>` extractor. [#2010]\n\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#2010]: https://github.com/actix/actix-web/pull/2010\n\n## 4.0.0-beta.3\n\n- Update `actix-web-codegen` to `0.5.0-beta.1`.\n\n## 4.0.0-beta.2\n\n### Added\n\n- The method `Either<web::Json<T>, web::Form<T>>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either<web::Form<T>, web::Json<T>>`. [#1894]\n- Add `services!` macro for helping register multiple services to `App`. [#1933]\n- Enable registering a vec of services of the same type to `App` [#1933]\n\n### Changed\n\n- Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. Making it simpler and more performant. [#1891]\n- `ServiceRequest::into_parts` and `ServiceRequest::from_parts` can no longer fail. [#1893]\n- `ServiceRequest::from_request` can no longer fail. [#1893]\n- Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]\n- `test::{call_service, read_response, read_response_json, send_request}` take `&Service` in argument [#1905]\n- `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure argument. [#1905]\n- `web::block` no longer requires the output is a Result. [#1957]\n\n### Fixed\n\n- Multiple calls to `App::data` with the same type now keeps the latest call's data. [#1906]\n\n### Removed\n\n- Public field of `web::Path` has been made private. [#1894]\n- Public field of `web::Query` has been made private. [#1894]\n- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869]\n- `AppService::set_service_data`; for custom HTTP service factories adding application data, use the layered data model by calling `ServiceRequest::add_data_container` when handling requests instead. [#1906]\n\n[#1891]: https://github.com/actix/actix-web/pull/1891\n[#1893]: https://github.com/actix/actix-web/pull/1893\n[#1894]: https://github.com/actix/actix-web/pull/1894\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1906]: https://github.com/actix/actix-web/pull/1906\n[#1933]: https://github.com/actix/actix-web/pull/1933\n[#1957]: https://github.com/actix/actix-web/pull/1957\n\n## 4.0.0-beta.1\n\n### Added\n\n- `Compat` middleware enabling generic response body/error type of middlewares like `Logger` and `Compress` to be used in `middleware::Condition` and `Resource`, `Scope` services. [#1865]\n\n### Changed\n\n- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813]\n- Bumped `rand` to `0.8`.\n- Update `rust-tls` to `0.19`. [#1813]\n- Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852]\n- The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration guide for implications. [#1875]\n- Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875]\n- MSRV is now 1.46.0.\n\n### Fixed\n\n- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812]\n\n### Removed\n\n- Public modules `middleware::{normalize, err_handlers}`. All necessary middleware types are now exposed directly by the `middleware` module.\n- Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported from `actix_web::error` module. [#1878]\n\n[#1812]: https://github.com/actix/actix-web/pull/1812\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1852]: https://github.com/actix/actix-web/pull/1852\n[#1865]: https://github.com/actix/actix-web/pull/1865\n[#1875]: https://github.com/actix/actix-web/pull/1875\n[#1878]: https://github.com/actix/actix-web/pull/1878\n\n</details>\n\n## 3.3.3\n\n### Changed\n\n- Soft-deprecate `NormalizePath::default()`, noting upcoming behavior change in v4. [#2529]\n\n[#2529]: https://github.com/actix/actix-web/pull/2529\n\n## 3.3.2\n\n### Fixed\n\n- Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762]\n- Fix `match_pattern()` returning `None` for scope with empty path resource. [#1798]\n- Increase minimum `socket2` version. [#1803]\n\n[#1762]: https://github.com/actix/actix-web/pull/1762\n[#1798]: https://github.com/actix/actix-web/pull/1798\n[#1803]: https://github.com/actix/actix-web/pull/1803\n\n## 3.3.1\n\n- Ensure `actix-http` dependency uses same `serde_urlencoded`.\n\n## 3.3.0\n\n### Added\n\n- Add `Either<A, B>` extractor helper. [#1788]\n\n### Changed\n\n- Upgrade `serde_urlencoded` to `0.7`. [#1773]\n\n[#1773]: https://github.com/actix/actix-web/pull/1773\n[#1788]: https://github.com/actix/actix-web/pull/1788\n\n## 3.2.0\n\n### Added\n\n- Implement `exclude_regex` for Logger middleware. [#1723]\n- Add request-local data extractor `web::ReqData`. [#1748]\n- Add ability to register closure for request middleware logging. [#1749]\n- Add `app_data` to `ServiceConfig`. [#1757]\n- Expose `on_connect` for access to the connection stream before request is handled. [#1754]\n\n### Changed\n\n- Updated `actix-web-codegen` dependency for access to new `#[route(...)]` multi-method macro.\n- Print non-configured `Data<T>` type when attempting extraction. [#1743]\n- Re-export `bytes::Buf{Mut}` in web module. [#1750]\n- Upgrade `pin-project` to `1.0`.\n\n[#1723]: https://github.com/actix/actix-web/pull/1723\n[#1743]: https://github.com/actix/actix-web/pull/1743\n[#1748]: https://github.com/actix/actix-web/pull/1748\n[#1750]: https://github.com/actix/actix-web/pull/1750\n[#1754]: https://github.com/actix/actix-web/pull/1754\n[#1757]: https://github.com/actix/actix-web/pull/1757\n[#1749]: https://github.com/actix/actix-web/pull/1749\n\n## 3.1.0\n\n### Changed\n\n- Add `TrailingSlash::MergeOnly` behaviour to `NormalizePath`, which allows `NormalizePath` to retain any trailing slashes. [#1695]\n- Remove bound `std::marker::Sized` from `web::Data` to support storing `Arc<dyn Trait>` via `web::Data::from` [#1710]\n\n### Fixed\n\n- `ResourceMap` debug printing is no longer infinitely recursive. [#1708]\n\n[#1695]: https://github.com/actix/actix-web/pull/1695\n[#1708]: https://github.com/actix/actix-web/pull/1708\n[#1710]: https://github.com/actix/actix-web/pull/1710\n\n## 3.0.2\n\n### Fixed\n\n- `NormalizePath` when used with `TrailingSlash::Trim` no longer trims the root path \"/\". [#1678]\n\n[#1678]: https://github.com/actix/actix-web/pull/1678\n\n## 3.0.1\n\n### Changed\n\n- `middleware::normalize::TrailingSlash` enum is now accessible. [#1673]\n\n[#1673]: https://github.com/actix/actix-web/pull/1673\n\n## 3.0.0\n\n- No significant changes from `3.0.0-beta.4`.\n\n## 3.0.0-beta.4\n\n### Added\n\n- `middleware::NormalizePath` now has configurable behavior for either always having a trailing slash, or as the new addition, always trimming trailing slashes. [#1639]\n\n### Changed\n\n- Update actix-codec and actix-utils dependencies. [#1634]\n- `FormConfig` and `JsonConfig` configurations are now also considered when set using `App::data`. [#1641]\n- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655]\n- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`. [#1655]\n\n[#1639]: https://github.com/actix/actix-web/pull/1639\n[#1641]: https://github.com/actix/actix-web/pull/1641\n[#1634]: https://github.com/actix/actix-web/pull/1634\n[#1655]: https://github.com/actix/actix-web/pull/1655\n\n## 3.0.0-beta.3\n\n### Changed\n\n- Update `rustls` to 0.18\n\n## 3.0.0-beta.2\n\n### Changed\n\n- `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set using `App::data`. [#1610]\n- `web::Path` now has a public representation: `web::Path(pub T)` that enables destructuring. [#1594]\n- `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to access `HttpRequest` which already allows this. [#1618]\n- Re-export all error types from `awc`. [#1621]\n- MSRV is now 1.42.0.\n\n### Fixed\n\n- Memory leak of app data in pooled requests. [#1609]\n\n[#1594]: https://github.com/actix/actix-web/pull/1594\n[#1609]: https://github.com/actix/actix-web/pull/1609\n[#1610]: https://github.com/actix/actix-web/pull/1610\n[#1618]: https://github.com/actix/actix-web/pull/1618\n[#1621]: https://github.com/actix/actix-web/pull/1621\n\n## 3.0.0-beta.1\n\n### Added\n\n- Re-export `actix_rt::main` as `actix_web::main`.\n- `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched resource pattern.\n- `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.\n\n### Changed\n\n- Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]\n- Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.\n- MSRV is now 1.41.1\n\n### Fixed\n\n- `NormalizePath` improved consistency when path needs slashes added _and_ removed.\n\n## 3.0.0-alpha.3\n\n### Added\n\n- Add option to create `Data<T>` from `Arc<T>` [#1509]\n\n### Changed\n\n- Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486]\n- Fix audit issue logging by default peer address [#1485]\n- Bump minimum supported Rust version to 1.40\n- Replace deprecated `net2` crate with `socket2`\n\n[#1485]: https://github.com/actix/actix-web/pull/1485\n[#1509]: https://github.com/actix/actix-web/pull/1509\n\n## 3.0.0-alpha.2\n\n### Changed\n\n- `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452]\n- Implement `std::error::Error` for our custom errors [#1422]\n- NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433]\n- Remove the `failure` feature and support.\n\n[#1422]: https://github.com/actix/actix-web/pull/1422\n[#1433]: https://github.com/actix/actix-web/pull/1433\n[#1452]: https://github.com/actix/actix-web/pull/1452\n[#1486]: https://github.com/actix/actix-web/pull/1486\n\n## 3.0.0-alpha.1\n\n### Added\n\n- Add helper function for creating routes with `TRACE` method guard `web::trace()`\n- Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing.\n\n### Changed\n\n- Use `sha-1` crate instead of unmaintained `sha1` crate\n- Skip empty chunks when returning response from a `Stream` [#1308]\n- Update the `time` dependency to 0.2.7\n- Update `actix-tls` dependency to 2.0.0-alpha.1\n- Update `rustls` dependency to 0.17\n\n[#1308]: https://github.com/actix/actix-web/pull/1308\n\n## 2.0.0\n\n### Changed\n\n- Rename `HttpServer::start()` to `HttpServer::run()`\n\n- Allow to gracefully stop test server via `TestServer::stop()`\n\n- Allow to specify multi-patterns for resources\n\n## 2.0.0-rc\n\n### Changed\n\n- Move `BodyEncoding` to `dev` module #1220\n\n- Allow to set `peer_addr` for TestRequest #1074\n\n- Make web::Data deref to Arc<T> #1214\n\n- Rename `App::register_data()` to `App::app_data()`\n\n- `HttpRequest::app_data<T>()` returns `Option<&T>` instead of `Option<&Data<T>>`\n\n### Fixed\n\n- Fix `AppConfig::secure()` is always false. #1202\n\n## 2.0.0-alpha.6\n\n### Fixed\n\n- Fixed compilation with default features off\n\n## 2.0.0-alpha.5\n\n### Added\n\n- Add test server, `test::start()` and `test::start_with()`\n\n## 2.0.0-alpha.4\n\n### Deleted\n\n- Delete HttpServer::run(), it is not useful with async/await\n\n## 2.0.0-alpha.3\n\n### Changed\n\n- Migrate to tokio 0.2\n\n## 2.0.0-alpha.1\n\n### Changed\n\n- Migrated to `std::future`\n\n- Remove implementation of `Responder` for `()`. (#1167)\n\n## 1.0.9\n\n### Added\n\n- Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110)\n\n### Changed\n\n- Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129)\n\n## 1.0.8\n\n### Added\n\n- Add `Scope::register_data` and `Resource::register_data` methods, parallel to `App::register_data`.\n\n- Add `middleware::Condition` that conditionally enables another middleware\n\n- Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload`\n\n- Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, which is useful for example with systemd.\n\n### Changed\n\n- Make UrlEncodedError::Overflow more informative\n\n- Use actix-testing for testing utils\n\n## 1.0.7\n\n### Fixed\n\n- Request Extensions leak #1062\n\n## 1.0.6\n\n### Added\n\n- Re-implement Host predicate (#989)\n\n- Form implements Responder, returning a `application/x-www-form-urlencoded` response\n\n- Add `into_inner` to `Data`\n\n- Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set the header in test requests.\n\n### Changed\n\n- `Query` payload made `pub`. Allows user to pattern-match the payload.\n\n- Enable `rust-tls` feature for client #1045\n\n- Update serde_urlencoded to 0.6.1\n\n- Update url to 2.1\n\n## 1.0.5\n\n### Added\n\n- Unix domain sockets (HttpServer::bind_uds) #92\n\n- Actix now logs errors resulting in \"internal server error\" responses always, with the `error` logging level\n\n### Fixed\n\n- Restored logging of errors through the `Logger` middleware\n\n## 1.0.4\n\n### Added\n\n- Add `Responder` impl for `(T, StatusCode) where T: Responder`\n\n- Allow to access app's resource map via `ServiceRequest::resource_map()` and `HttpRequest::resource_map()` methods.\n\n### Changed\n\n- Upgrade `rand` dependency version to 0.7\n\n## 1.0.3\n\n### Added\n\n- Support asynchronous data factories #850\n\n### Changed\n\n- Use `encoding_rs` crate instead of unmaintained `encoding` crate\n\n## 1.0.2\n\n### Changed\n\n- Move cors middleware to `actix-cors` crate.\n\n- Move identity middleware to `actix-identity` crate.\n\n## 1.0.1\n\n### Added\n\n- Add support for PathConfig #903\n\n- Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`.\n\n### Changed\n\n- Move cors middleware to `actix-cors` crate.\n\n- Move identity middleware to `actix-identity` crate.\n\n- Disable default feature `secure-cookies`.\n\n- Allow to test an app that uses async actors #897\n\n- Re-apply patch from #637 #894\n\n### Fixed\n\n- HttpRequest::url_for is broken with nested scopes #915\n\n## 1.0.0\n\n### Added\n\n- Add `Scope::configure()` method.\n\n- Add `ServiceRequest::set_payload()` method.\n\n- Add `test::TestRequest::set_json()` convenience method to automatically serialize data and set header in test requests.\n\n- Add macros for head, options, trace, connect and patch http methods\n\n### Changed\n\n- Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863\n\n### Fixed\n\n- Fix Logger request time format, and use rfc3339. #867\n\n- Clear http requests pool on app service drop #860\n\n## 1.0.0-rc\n\n### Added\n\n- Add `Query<T>::from_query()` to extract parameters from a query string. #846\n- `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors.\n\n### Changed\n\n- `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too.\n\n### Fixed\n\n- Codegen with parameters in the path only resolves the first registered endpoint #841\n\n## 1.0.0-beta.4\n\n### Added\n\n- Allow to set/override app data on scope level\n\n### Changed\n\n- `App::configure` take an `FnOnce` instead of `Fn`\n- Upgrade actix-net crates\n\n## 1.0.0-beta.3\n\n### Added\n\n- Add helper function for executing futures `test::block_fn()`\n\n### Changed\n\n- Extractor configuration could be registered with `App::data()` or with `Resource::data()` #775\n\n- Route data is unified with app data, `Route::data()` moved to resource level to `Resource::data()`\n\n- CORS handling without headers #702\n\n- Allow constructing `Data` instances to avoid double `Arc` for `Send + Sync` types.\n\n### Fixed\n\n- Fix `NormalizePath` middleware impl #806\n\n### Deleted\n\n- `App::data_factory()` is deleted.\n\n## 1.0.0-beta.2\n\n### Added\n\n- Add raw services support via `web::service()`\n\n- Add helper functions for reading response body `test::read_body()`\n\n- Add support for `remainder match` (i.e \"/path/{tail}\\*\")\n\n- Extend `Responder` trait, allow to override status code and headers.\n\n- Store visit and login timestamp in the identity cookie #502\n\n### Changed\n\n- `.to_async()` handler can return `Responder` type #792\n\n### Fixed\n\n- Fix async web::Data factory handling\n\n## 1.0.0-beta.1\n\n### Added\n\n- Add helper functions for reading test response body, `test::read_response()` and test::read_response_json()`\n\n- Add `.peer_addr()` #744\n\n- Add `NormalizePath` middleware\n\n### Changed\n\n- Rename `RouterConfig` to `ServiceConfig`\n\n- Rename `test::call_success` to `test::call_service`\n\n- Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts.\n\n- `CookieIdentityPolicy::max_age()` accepts value in seconds\n\n### Fixed\n\n- Fixed `TestRequest::app_data()`\n\n## 1.0.0-alpha.6\n\n### Changed\n\n- Allow using any service as default service.\n\n- Remove generic type for request payload, always use default.\n\n- Removed `Decompress` middleware. Bytes, String, Json, Form extractors automatically decompress payload.\n\n- Make extractor config type explicit. Add `FromRequest::Config` associated type.\n\n## 1.0.0-alpha.5\n\n### Added\n\n- Added async io `TestBuffer` for testing.\n\n### Deleted\n\n- Removed native-tls support\n\n## 1.0.0-alpha.4\n\n### Added\n\n- `App::configure()` allow to offload app configuration to different methods\n\n- Added `URLPath` option for logger\n\n- Added `ServiceRequest::app_data()`, returns `Data<T>`\n\n- Added `ServiceFromRequest::app_data()`, returns `Data<T>`\n\n### Changed\n\n- `FromRequest` trait refactoring\n\n- Move multipart support to actix-multipart crate\n\n### Fixed\n\n- Fix body propagation in Response::from_error. #760\n\n## 1.0.0-alpha.3\n\n### Changed\n\n- Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()`\n\n- Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()`\n\n- Removed `Deref` impls\n\n### Removed\n\n- Removed unused `actix_web::web::md()`\n\n## 1.0.0-alpha.2\n\n### Added\n\n- Rustls support\n\n### Changed\n\n- Use forked cookie\n\n- Multipart::Field renamed to MultipartField\n\n## 1.0.0-alpha.1\n\n### Changed\n\n- Complete architecture re-design.\n\n- Return 405 response if no matching route found within resource #538\n"
  },
  {
    "path": "actix-web/Cargo.toml",
    "content": "[package]\nname = \"actix-web\"\nversion = \"4.13.0\"\ndescription = \"Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\", \"Rob Ede <robjtede@icloud.com>\"]\nkeywords = [\"actix\", \"http\", \"web\", \"framework\", \"async\"]\ncategories = [\n  \"network-programming\",\n  \"asynchronous\",\n  \"web-programming::http-server\",\n  \"web-programming::websocket\",\n]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[package.metadata.docs.rs]\nfeatures = [\n  \"macros\",\n  \"openssl\",\n  \"rustls-0_20\",\n  \"rustls-0_21\",\n  \"rustls-0_22\",\n  \"rustls-0_23\",\n  \"compress-brotli\",\n  \"compress-gzip\",\n  \"compress-zstd\",\n  \"cookies\",\n  \"secure-cookies\",\n]\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix_http::*\",\n  \"actix_router::*\",\n  \"actix_rt::*\",\n  \"actix_server::*\",\n  \"actix_service::*\",\n  \"actix_utils::*\",\n  \"actix_web_codegen::*\",\n  \"bytes::*\",\n  \"cookie::*\",\n  \"cookie\",\n  \"futures_core::*\",\n  \"http::*\",\n  \"language_tags::*\",\n  \"mime::*\",\n  \"openssl::*\",\n  \"rustls::*\",\n  \"serde_json::*\",\n  \"serde_urlencoded::*\",\n  \"serde::*\",\n  \"tokio::*\",\n  \"url::*\",\n]\n\n[features]\ndefault = [\n  \"macros\",\n  \"compress-brotli\",\n  \"compress-gzip\",\n  \"compress-zstd\",\n  \"cookies\",\n  \"http2\",\n  \"unicode\",\n  \"compat\",\n  \"ws\",\n]\n\n# Brotli algorithm content-encoding support\ncompress-brotli = [\"actix-http/compress-brotli\", \"__compress\"]\n# Gzip and deflate algorithms content-encoding support\ncompress-gzip = [\"actix-http/compress-gzip\", \"__compress\"]\n# Zstd algorithm content-encoding support\ncompress-zstd = [\"actix-http/compress-zstd\", \"__compress\"]\n\n# Routing and runtime proc macros\nmacros = [\"dep:actix-macros\", \"dep:actix-web-codegen\"]\n\n# Cookies support\ncookies = [\"dep:cookie\"]\n\n# Secure & signed cookies\nsecure-cookies = [\"cookies\", \"cookie/secure\"]\n\n# HTTP/2 support (including h2c)\nhttp2 = [\"actix-http/http2\"]\n\n# WebSocket support\nws = [\"actix-http/ws\"]\n\n# TLS via OpenSSL\nopenssl = [\"__tls\", \"http2\", \"actix-http/openssl\", \"actix-tls/accept\", \"actix-tls/openssl\"]\n\n# TLS via Rustls v0.20\nrustls = [\"rustls-0_20\"]\n# TLS via Rustls v0.20\nrustls-0_20 = [\"__tls\", \"http2\", \"actix-http/rustls-0_20\", \"actix-tls/accept\", \"actix-tls/rustls-0_20\"]\n# TLS via Rustls v0.21\nrustls-0_21 = [\"__tls\", \"http2\", \"actix-http/rustls-0_21\", \"actix-tls/accept\", \"actix-tls/rustls-0_21\"]\n# TLS via Rustls v0.22\nrustls-0_22 = [\"__tls\", \"http2\", \"actix-http/rustls-0_22\", \"actix-tls/accept\", \"actix-tls/rustls-0_22\"]\n# TLS via Rustls v0.23\nrustls-0_23 = [\"__tls\", \"http2\", \"actix-http/rustls-0_23\", \"actix-tls/accept\", \"actix-tls/rustls-0_23\"]\n\n# Full unicode support\nunicode = [\"dep:regex\", \"actix-router/unicode\"]\n\n# Internal (PRIVATE!) features used to aid testing and checking feature status.\n# Don't rely on these whatsoever. They may disappear at anytime.\n__compress = []\n\n# Internal (PRIVATE!) features used to aid checking feature status.\n# Don't rely on these whatsoever. They may disappear at anytime.\n__tls = []\n\n# io-uring feature only available for Linux OSes.\nexperimental-io-uring = [\"actix-server/io-uring\"]\n\n# Feature group which, when disabled, helps migrate code to v5.0.\ncompat = [\"compat-routing-macros-force-pub\"]\n\n# Opt-out forwards-compatibility for handler visibility inheritance fix.\ncompat-routing-macros-force-pub = [\"actix-web-codegen?/compat-routing-macros-force-pub\"]\n\n# Enabling the retrieval of metadata for initialized resources, including path and HTTP method.\nexperimental-introspection = [\"serde/derive\"]\n\n[dependencies]\nactix-codec = \"0.5\"\nactix-macros = { version = \"0.2.3\", optional = true }\nactix-rt = { version = \"2.6\", default-features = false }\nactix-server = \"2.6\"\nactix-service = \"2\"\nactix-tls = { version = \"3.4\", default-features = false, optional = true }\nactix-utils = \"3\"\n\nactix-http = \"3.12.0\"\nactix-router = { version = \"0.5.4\", default-features = false, features = [\"http\"] }\nactix-web-codegen = { version = \"4.3\", optional = true, default-features = false }\n\nbytes = \"1\"\nbytestring = \"1\"\ncfg-if = \"1\"\ncookie = { version = \"0.16\", features = [\"percent-encode\"], optional = true }\nderive_more = { version = \"2\", features = [\"as_ref\", \"deref\", \"deref_mut\", \"display\", \"error\", \"from\"] }\nencoding_rs = \"0.8\"\nfoldhash = \"0.1\"\nfutures-core = { version = \"0.3.17\", default-features = false }\nfutures-util = { version = \"0.3.17\", default-features = false }\nimpl-more = \"0.1.4\"\nitoa = \"1\"\nlanguage-tags = \"0.3\"\nlog = \"0.4\"\nmime = \"0.3\"\nonce_cell = \"1.21\"\npin-project-lite = \"0.2.7\"\nregex = { version = \"1.5.5\", optional = true }\nregex-lite = \"0.1\"\nserde = \"1.0\"\nserde_json = \"1.0\"\nserde_urlencoded = \"0.7\"\nsmallvec = \"1.6.1\"\nsocket2 = \"0.6\"\ntime = { version = \"0.3\", default-features = false, features = [\"formatting\"] }\ntracing = \"0.1.30\"\nurl = \"2.5.4\"\n\n[dev-dependencies]\nactix-files = \"0.6\"\nactix-test = { version = \"0.1\", features = [\"openssl\", \"rustls-0_23\"] }\nawc = { version = \"3\", features = [\"openssl\"] }\n\nbrotli = \"8\"\nconst-str = \"0.5\" # TODO(MSRV 1.77): update to 0.6\ncore_affinity = \"0.8\"\ncriterion = { version = \"0.5\", features = [\"html_reports\"] }\nenv_logger = \"0.11\"\nflate2 = \"1.0.13\"\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"std\"] }\nrand = \"0.9\"\nrcgen = \"0.13\"\nrustls-pki-types = \"1.13.1\"\nserde = { version = \"1\", features = [\"derive\"] }\nstatic_assertions = \"1\"\ntls-openssl = { package = \"openssl\", version = \"0.10.55\" }\ntls-rustls = { package = \"rustls\", version = \"0.23\" }\ntokio = { version = \"1.38.2\", features = [\"rt-multi-thread\", \"macros\"] }\ntokio-util = \"0.7\"\nzstd = \"0.13\"\n\n[lints]\nworkspace = true\n\n[[test]]\nname = \"test_server\"\nrequired-features = [\"compress-brotli\", \"compress-gzip\", \"compress-zstd\", \"cookies\"]\n\n[[test]]\nname = \"compression\"\nrequired-features = [\"compress-brotli\", \"compress-gzip\", \"compress-zstd\"]\n\n[[example]]\nname = \"basic\"\nrequired-features = [\"compress-gzip\"]\n\n[[example]]\nname = \"uds\"\nrequired-features = [\"compress-gzip\"]\n\n[[example]]\nname = \"on-connect\"\nrequired-features = []\n\n[[bench]]\nname = \"server\"\nharness = false\n\n[[bench]]\nname = \"service\"\nharness = false\n\n[[bench]]\nname = \"responder\"\nharness = false\n"
  },
  {
    "path": "actix-web/MIGRATION-0.x.md",
    "content": "# 0.7.15\n\n- The `' '` character is not percent decoded anymore before matching routes. If you need to use it in your routes, you should use `%20`.\n\ninstead of\n\n```rust\nfn main() {\n      let app = App::new().resource(\"/my index\", |r| {\n          r.method(http::Method::GET)\n                .with(index);\n      });\n}\n```\n\nuse\n\n```rust\nfn main() {\n      let app = App::new().resource(\"/my%20index\", |r| {\n          r.method(http::Method::GET)\n                .with(index);\n      });\n}\n```\n\n- If you used `AsyncResult::async` you need to replace it with `AsyncResult::future`\n\n# 0.7.4\n\n- `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple even for handler with one parameter.\n\n# 0.7\n\n- `HttpRequest` does not implement `Stream` anymore. If you need to read request payload use `HttpMessage::payload()` method.\n\ninstead of\n\n```rust\nfn index(req: HttpRequest) -> impl Responder {\n      req\n        .from_err()\n        .fold(...)\n        ....\n}\n```\n\nuse `.payload()`\n\n```rust\nfn index(req: HttpRequest) -> impl Responder {\n      req\n        .payload()  // <- get request payload stream\n        .from_err()\n        .fold(...)\n        ....\n}\n```\n\n- [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html) trait uses `&HttpRequest` instead of `&mut HttpRequest`.\n\n- Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead.\n\ninstead of\n\n```rust\nfn index(query: Query<..>, info: Json<MyStruct) -> impl Responder {}\n```\n\nuse tuple of extractors and use `.with()` for registration:\n\n```rust\nfn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {}\n```\n\n- `Handler::handle()` uses `&self` instead of `&mut self`\n\n- `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value\n\n- Removed deprecated `HttpServer::threads()`, use [HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead.\n\n- Renamed `client::ClientConnectorError::Connector` to `client::ClientConnectorError::Resolver`\n\n- `Route::with()` does not return `ExtractorConfig`, to configure extractor use `Route::with_config()`\n\ninstead of\n\n```rust\nfn main() {\n      let app = App::new().resource(\"/index.html\", |r| {\n          r.method(http::Method::GET)\n                .with(index)\n                .limit(4096);  // <- limit size of the payload\n      });\n}\n```\n\nuse\n\n```rust\n\nfn main() {\n      let app = App::new().resource(\"/index.html\", |r| {\n          r.method(http::Method::GET)\n                .with_config(index, |cfg| { // <- register handler\n                    cfg.limit(4096);  // <- limit size of the payload\n                  })\n      });\n}\n```\n\n- `Route::with_async()` does not return `ExtractorConfig`, to configure extractor use `Route::with_async_config()`\n\n# 0.6\n\n- `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`\n\n- `ws::Message::Close` now includes optional close reason. `ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.\n\n- `HttpServer::threads()` renamed to `HttpServer::workers()`.\n\n- `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated. Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead.\n\n- `HttpRequest::extensions()` returns read only reference to the request's Extension `HttpRequest::extensions_mut()` returns mutable reference.\n\n- Instead of\n\n  `use actix_web::middleware::{ CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`\n\n  use `actix_web::middleware::session`\n\n  `use actix_web::middleware::session{CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`\n\n- `FromRequest::from_request()` accepts mutable reference to a request\n\n- `FromRequest::Result` has to implement `Into<Reply<Self>>`\n\n- [`Responder::respond_to()`](https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to) is generic over `S`\n\n- Use `Query` extractor instead of HttpRequest::query()`.\n\n```rust\nfn index(q: Query<HashMap<String, String>>) -> Result<..> {\n    ...\n}\n```\n\nor\n\n```rust\nlet q = Query::<HashMap<String, String>>::extract(req);\n```\n\n- Websocket operations are implemented as `WsWriter` trait. you need to use `use actix_web::ws::WsWriter`\n\n# 0.5\n\n- `HttpResponseBuilder::body()`, `.finish()`, `.json()` methods return `HttpResponse` instead of `Result<HttpResponse>`\n\n- `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version` moved to `actix_web::http` module\n\n- `actix_web::header` moved to `actix_web::http::header`\n\n- `NormalizePath` moved to `actix_web::http` module\n\n- `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function, shortcut for `actix_web::server::HttpServer::new()`\n\n- `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself\n\n- `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead.\n\n- `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type\n\n- `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()` functions should be used instead\n\n- `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>` instead of `Result<_, http::Error>`\n\n- `Application` renamed to a `App`\n\n- `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev`\n"
  },
  {
    "path": "actix-web/MIGRATION-1.0.md",
    "content": "## 1.0.1\n\n- Cors middleware has been moved to `actix-cors` crate\n\n  instead of\n\n  ```rust\n  use actix_web::middleware::cors::Cors;\n  ```\n\n  use\n\n  ```rust\n  use actix_cors::Cors;\n  ```\n\n- Identity middleware has been moved to `actix-identity` crate\n\n  instead of\n\n  ```rust\n  use actix_web::middleware::identity::{Identity, CookieIdentityPolicy, IdentityService};\n  ```\n\n  use\n\n  ```rust\n  use actix_identity::{Identity, CookieIdentityPolicy, IdentityService};\n  ```\n\n## 1.0.0\n\n- Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration\n\n  instead of\n\n  ```rust\n\n  #[derive(Default)]\n  struct ExtractorConfig {\n     config: String,\n  }\n\n  impl FromRequest for YourExtractor {\n     type Config = ExtractorConfig;\n     type Result = Result<YourExtractor, Error>;\n\n     fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result {\n         println!(\"use the config: {:?}\", cfg.config);\n         ...\n     }\n  }\n\n  App::new().resource(\"/route_with_config\", |r| {\n     r.post().with_config(handler_fn, |cfg| {\n         cfg.0.config = \"test\".to_string();\n     })\n  })\n\n  ```\n\n  use the HttpRequest to get the configuration like any other `Data` with `req.app_data::<C>()` and set it with the `data()` method on the `resource`\n\n  ```rust\n  #[derive(Default)]\n  struct ExtractorConfig {\n     config: String,\n  }\n\n  impl FromRequest for YourExtractor {\n     type Error = Error;\n     type Future = Result<Self, Self::Error>;\n     type Config = ExtractorConfig;\n\n     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n         let cfg = req.app_data::<ExtractorConfig>();\n         println!(\"config data?: {:?}\", cfg.unwrap().role);\n         ...\n     }\n  }\n\n  App::new().service(\n     resource(\"/route_with_config\")\n         .data(ExtractorConfig {\n             config: \"test\".to_string(),\n         })\n         .route(post().to(handler_fn)),\n  )\n  ```\n\n- Resource registration. 1.0 version uses generalized resource registration via `.service()` method.\n\n  instead of\n\n  ```rust\n    App.new().resource(\"/welcome\", |r| r.f(welcome))\n  ```\n\n  use App's or Scope's `.service()` method. `.service()` method accepts object that implements `HttpServiceFactory` trait. By default actix-web provides `Resource` and `Scope` services.\n\n  ```rust\n    App.new().service(\n        web::resource(\"/welcome\")\n            .route(web::get().to(welcome))\n            .route(web::post().to(post_handler))\n  ```\n\n- Scope registration.\n\n  instead of\n\n  ```rust\n      let app = App::new().scope(\"/{project_id}\", |scope| {\n            scope\n                .resource(\"/path1\", |r| r.f(|_| HttpResponse::Ok()))\n                .resource(\"/path2\", |r| r.f(|_| HttpResponse::Ok()))\n                .resource(\"/path3\", |r| r.f(|_| HttpResponse::MethodNotAllowed()))\n      });\n  ```\n\n  use `.service()` for registration and `web::scope()` as scope object factory.\n\n  ```rust\n      let app = App::new().service(\n          web::scope(\"/{project_id}\")\n              .service(web::resource(\"/path1\").to(|| HttpResponse::Ok()))\n              .service(web::resource(\"/path2\").to(|| HttpResponse::Ok()))\n              .service(web::resource(\"/path3\").to(|| HttpResponse::MethodNotAllowed()))\n      );\n  ```\n\n- `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`.\n\n  instead of\n\n  ```rust\n    App.new().resource(\"/welcome\", |r| r.with(welcome))\n  ```\n\n  use `.to()` or `.to_async()` methods\n\n  ```rust\n    App.new().service(web::resource(\"/welcome\").to(welcome))\n  ```\n\n- Passing arguments to handler with extractors, multiple arguments are allowed\n\n  instead of\n\n  ```rust\n  fn welcome((body, req): (Bytes, HttpRequest)) -> ... {\n    ...\n  }\n  ```\n\n  use multiple arguments\n\n  ```rust\n  fn welcome(body: Bytes, req: HttpRequest) -> ... {\n    ...\n  }\n  ```\n\n- `.f()`, `.a()` and `.h()` handler registration methods have been removed. Use `.to()` for handlers and `.to_async()` for async handlers. Handler function must use extractors.\n\n  instead of\n\n  ```rust\n    App.new().resource(\"/welcome\", |r| r.f(welcome))\n  ```\n\n  use App's `to()` or `to_async()` methods\n\n  ```rust\n    App.new().service(web::resource(\"/welcome\").to(welcome))\n  ```\n\n- `HttpRequest` does not provide access to request's payload stream.\n\n  instead of\n\n  ```rust\n  fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {\n    req\n       .payload()\n       .from_err()\n       .fold((), |_, chunk| {\n            ...\n        })\n       .map(|_| HttpResponse::Ok().finish())\n       .responder()\n  }\n  ```\n\n  use `Payload` extractor\n\n  ```rust\n  fn index(stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> {\n     stream\n       .from_err()\n       .fold((), |_, chunk| {\n            ...\n        })\n       .map(|_| HttpResponse::Ok().finish())\n  }\n  ```\n\n- `State` is now `Data`. You register Data during the App initialization process and then access it from handlers either using a Data extractor or using HttpRequest's api.\n\n  instead of\n\n  ```rust\n    App.with_state(T)\n  ```\n\n  use App's `data` method\n\n  ```rust\n  App.new()\n       .data(T)\n  ```\n\n  and either use the Data extractor within your handler\n\n  ```rust\n    use actix_web::web::Data;\n\n  fn endpoint_handler(Data<T>)){\n      ...\n    }\n  ```\n\n  .. or access your Data element from the HttpRequest\n\n  ```rust\n  fn endpoint_handler(req: HttpRequest) {\n  \tlet data: Option<Data<T>> = req.app_data::<T>();\n    }\n  ```\n\n- AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type.\n\n  instead of\n\n  ```rust\n  use actix_web::AsyncResponder;\n\n    fn endpoint_handler(...) -> impl Future<Item=HttpResponse, Error=Error>{\n  \t...\n        .responder()\n  }\n  ```\n\n  .. simply omit AsyncResponder and the corresponding responder() finish method\n\n- Middleware\n\n  instead of\n\n  ```rust\n      let app = App::new()\n           .middleware(middleware::Logger::default())\n  ```\n\n  use `.wrap()` method\n\n  ```rust\n      let app = App::new()\n           .wrap(middleware::Logger::default())\n           .route(\"/index.html\", web::get().to(index));\n  ```\n\n- `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()` method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead.\n\n  instead of\n\n  ```rust\n  fn index(req: &HttpRequest) -> Responder {\n     req.body()\n       .and_then(|body| {\n          ...\n       })\n  }\n  ```\n\n  use\n\n  ```rust\n  fn index(body: Bytes) -> Responder {\n     ...\n  }\n  ```\n\n- `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type\n\n- StaticFiles and NamedFile have been moved to a separate crate.\n\n  instead of `use actix_web::fs::StaticFile`\n\n  use `use actix_files::Files`\n\n  instead of `use actix_web::fs::Namedfile`\n\n  use `use actix_files::NamedFile`\n\n- Multipart has been moved to a separate crate.\n\n  instead of `use actix_web::multipart::Multipart`\n\n  use `use actix_multipart::Multipart`\n\n- Response compression is not enabled by default. To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`.\n\n- Session middleware moved to actix-session crate\n\n- Actors support have been moved to `actix-web-actors` crate\n\n- Custom Error\n\n  Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller.\n\n  Simplest migration from 0.7 to 1.0 shall include below method to the custom implementation of ResponseError:\n\n  ```rust\n  fn render_response(&self) -> HttpResponse {\n    self.error_response()\n  }\n  ```\n"
  },
  {
    "path": "actix-web/MIGRATION-2.0.md",
    "content": "# Migrating to 2.0.0\n\n- `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to `.await` on `run` method result, in that case it awaits server exit.\n\n- `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. Stored data is available via `HttpRequest::app_data()` method at runtime.\n\n- Extractor configuration must be registered with `App::app_data()` instead of `App::data()`\n\n- Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` replace `fn` with `async fn` to convert sync handler to async\n\n- `actix_http_test::TestServer` moved to `actix_web::test` module. To start test server use `test::start()` or `test_start_with_config()` methods\n\n- `ResponseError` trait has been refactored. `ResponseError::error_response()` renders http response.\n\n- Feature `rust-tls` renamed to `rustls`\n\n  instead of\n\n  ```rust\n  actix-web = { version = \"2.0.0\", features = [\"rust-tls\"] }\n  ```\n\n  use\n\n  ```rust\n  actix-web = { version = \"2.0.0\", features = [\"rustls\"] }\n  ```\n\n- Feature `ssl` renamed to `openssl`\n\n  instead of\n\n  ```rust\n  actix-web = { version = \"2.0.0\", features = [\"ssl\"] }\n  ```\n\n  use\n\n  ```rust\n  actix-web = { version = \"2.0.0\", features = [\"openssl\"] }\n  ```\n\n- `Cors` builder now requires that you call `.finish()` to construct the middleware\n"
  },
  {
    "path": "actix-web/MIGRATION-3.0.md",
    "content": "# Migrating to 3.0.0\n\n- The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.\n\n- Cookie handling has been offloaded to the `cookie` crate:\n  - `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.\n  - Some types now require lifetime parameters.\n\n- The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects any `actix-web` method previously expecting a time v0.1 input.\n\n- Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now result in `SameSite=None` being sent with the response Set-Cookie header. To create a cookie without a SameSite attribute, remove any calls setting same_site.\n\n- actix-http support for Actors messages was moved to actix-http crate and is enabled with feature `actors`\n\n- content_length function is removed from actix-http. You can set Content-Length by normally setting the response body or calling no_chunking function.\n\n- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`.\n\n- Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use destructuring or `.into_inner()`. For example:\n\n  ```rust\n  // Previously:\n  async fn some_route(path: web::Path<(String, String)>) -> String {\n    format!(\"Hello, {} {}\", path.0, path.1)\n  }\n\n  // Now (this also worked before):\n  async fn some_route(path: web::Path<(String, String)>) -> String {\n    let (first_name, last_name) = path.into_inner();\n    format!(\"Hello, {} {}\", first_name, last_name)\n  }\n  // Or (this wasn't previously supported):\n  async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String {\n    format!(\"Hello, {} {}\", first_name, last_name)\n  }\n  ```\n\n- `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one. It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`, or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`.\n\n- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.\n\n- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.\n"
  },
  {
    "path": "actix-web/MIGRATION-4.0.md",
    "content": "# Migrating to 4.0.0\n\nThis guide walks you through the process of migrating from v3.x.y to v4.x.y.  \nIf you are migrating to v4.x.y from an older version of Actix Web (v2.x.y or earlier), check out the other historical migration notes in this folder.\n\nThis document is not designed to be exhaustive—it focuses on the most significant changes in v4. You can find an exhaustive changelog in the changelogs for [`actix-web`](./CHANGES.md#400---2022-02-25) and [`actix-http`](../actix-http/CHANGES.md#300---2022-02-25), complete with PR links. If you think there are any changes that deserve to be called out in this document, please open an issue or pull request.\n\nHeadings marked with :warning: are **breaking behavioral changes**. They will probably not surface as compile-time errors though automated tests _might_ detect their effects on your app.\n\n## Table of Contents:\n\n- [MSRV](#msrv)\n- [Tokio v1 Ecosystem](#tokio-v1-ecosystem)\n- [Module Structure](#module-structure)\n- [`NormalizePath` Middleware :warning:](#normalizepath-middleware-warning)\n- [Server Settings :warning:](#server-settings-warning)\n- [`FromRequest` Trait](#fromrequest-trait)\n- [Compression Feature Flags](#compression-feature-flags)\n- [`web::Path`](#webpath)\n- [Rustls Crate Upgrade](#rustls-crate-upgrade)\n- [Removed `awc` Client Re-export](#removed-awc-client-re-export)\n- [Integration Testing Utils Moved To `actix-test`](#integration-testing-utils-moved-to-actix-test)\n- [Header APIs](#header-apis)\n- [Response Body Types](#response-body-types)\n- [Middleware Trait APIs](#middleware-trait-apis)\n- [`Responder` Trait](#responder-trait)\n- [`App::data` Deprecation :warning:](#appdata-deprecation-warning)\n- [Direct Dependency On `actix-rt` And `actix-service`](#direct-dependency-on-actix-rt-and-actix-service)\n- [Server Must Be Polled :warning:](#server-must-be-polled-warning)\n- [Guards API](#guards-api)\n- [Returning `HttpResponse` synchronously](#returning-httpresponse-synchronously)\n- [`#[actix_web::main]` and `#[tokio::main]`](#actix_webmain-and-tokiomain)\n- [`web::block`](#webblock)\n\n## MSRV\n\nThe MSRV of Actix Web has been raised from 1.42 to 1.54.\n\n## Tokio v1 Ecosystem\n\nActix Web v4 is now underpinned by `tokio`'s v1 ecosystem.\n\n`cargo` supports having multiple versions of the same crate within the same dependency tree, but `tokio` v1 does not interoperate transparently with its previous versions (v0.2, v0.1). Some of your dependencies might rely on `tokio`, either directly or indirectly—if they are using an older version of `tokio`, check if an update is available.  \nThe following command can help you to identify these dependencies:\n\n```sh\n# Find all crates in your dependency tree that depend on `tokio`\n# It also reports the different versions of `tokio` in your dependency tree.\ncargo tree -i tokio\n\n# if you depend on multiple versions of tokio, use this command to\n# list the dependencies relying on a specific version of tokio:\ncargo tree -i tokio:0.2.25\n```\n\n## Module Structure\n\nLots of modules have been re-organized in this release. If a compile error refers to \"item XYZ not found in module...\" or \"module XYZ not found\", check the [documentation on docs.rs](https://docs.rs/actix-web) to search for items' new locations.\n\n## `NormalizePath` Middleware :warning:\n\nThe default `NormalizePath` behavior now strips trailing slashes by default. This was the _documented_ behaviour in Actix Web v3, but the _actual_ behaviour differed. The discrepancy has now been resolved.\n\nAs a consequence of this change, routes defined with trailing slashes will become inaccessible when using `NormalizePath::default()`. Calling `NormalizePath::default()` will log a warning. We suggest to use `new` or `trim`.\n\n```diff\n- #[get(\"/test/\")]\n+ #[get(\"/test\")]\n  async fn handler() {\n\n  App::new()\n-   .wrap(NormalizePath::default())\n+   .wrap(NormalizePath::trim())\n```\n\nAlternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.\n\n## Server Settings :warning:\n\nUntil Actix Web v4, the underlying `actix-server` crate used the number of available **logical** cores as the default number of worker threads. The new default is the number of [physical CPU cores available](https://github.com/actix/actix-net/commit/3a3d654c). For more information about this change, refer to [this analysis](https://github.com/actix/actix-web/issues/957).\n\nIf you notice performance regressions, please open a new issue detailing your observations.\n\n## `FromRequest` Trait\n\nThe associated type `Config` of `FromRequest` was removed. If you have custom extractors, you can just remove this implementation and refer to config types directly, if required.\n\n```diff\n  impl FromRequest for MyExtractor {\n-   type Config = ();\n  }\n```\n\nConsequently, the `FromRequest::configure` method was also removed. Config for extractors is still provided using `App::app_data` but should now be constructed in a standalone way.\n\n## Compression Feature Flags\n\nThe `compress` feature flag has been split into more granular feature flags, one for each supported algorithm (brotli, gzip, zstd). By default, all compression algorithms are enabled. If you want to select specific compression codecs, the new flags are:\n\n- `compress-brotli`\n- `compress-gzip`\n- `compress-zstd`\n\n## `web::Path`\n\nThe inner field for `web::Path` is now private. It was causing ambiguity when trying to use tuple indexing due to its `Deref` implementation.\n\n```diff\n- async fn handler(web::Path((foo, bar)): web::Path<(String, String)>) {\n+ async fn handler(params: web::Path<(String, String)>) {\n+   let (foo, bar) = params.into_inner();\n```\n\nAn alternative [path param type with public field but no `Deref` impl is available in `actix-web-lab`](https://docs.rs/actix-web-lab/0.12.0/actix_web_lab/extract/struct.Path.html).\n\n## Rustls Crate Upgrade\n\nActix Web now depends on version 0.20 of `rustls`. As a result, the server config builder has changed. [See the updated example project.](https://github.com/actix/examples/tree/main/https-tls/rustls/)\n\n## Removed `awc` Client Re-export\n\nActix Web's sister crate `awc` is no longer re-exported through the `client` module. This allows `awc` to have its own release cadence—its breaking changes are no longer blocked by Actix Web's (more conservative) release schedule.\n\n```diff\n- use actix_web::client::Client;\n+ use awc::Client;\n```\n\n## Integration Testing Utils Moved To `actix-test`\n\n`TestServer` has been moved to its own crate, [`actix-test`](https://docs.rs/actix-test).\n\n```diff\n- use use actix_web::test::start;\n+ use use actix_test::start;\n```\n\n`TestServer` previously lived in `actix_web::test`, but it depends on `awc` which is no longer part of Actix Web's public API (see above).\n\n## Header APIs\n\nHeader related APIs have been standardized across all `actix-*` crates. The terminology now better matches the underlying `HeaderMap` naming conventions.\n\nIn short, \"insert\" always indicates that any existing headers with the same name are overridden, while \"append\" is used for adding with no removal (e.g. multi-valued headers).\n\nFor request and response builder APIs, the new methods provide a unified interface for adding key-value pairs _and_ typed headers, which can often be more expressive.\n\n```diff\n- .set_header(\"Api-Key\", \"1234\")\n+ .insert_header((\"Api-Key\", \"1234\"))\n\n- .header(\"Api-Key\", \"1234\")\n+ .append_header((\"Api-Key\", \"1234\"))\n\n- .set(ContentType::json())\n+ .insert_header(ContentType::json())\n```\n\nWe chose to deprecate most of the old methods instead of removing them immediately—the warning notes will guide you on how to update.\n\n## Response Body Types\n\nThere have been a lot of changes to response body types. They are now more expressive and their purpose should be more intuitive.\n\nWe have boosted the quality and completeness of the documentation for all items in the [`body` module](https://docs.rs/actix-web/4/actix_web/body).\n\n### `ResponseBody`\n\n`ResponseBody` is gone. Its purpose was confusing and has been replaced by better components.\n\n### `Body`\n\n`Body` is also gone. In combination with `ResponseBody`, the API it provided was sub-optimal and did not encourage expressive types. Here are the equivalents in the new system (check docs):\n\n- `Body::None` => `body::None::new()`\n- `Body::Empty` => `()` / `web::Bytes::new()`\n- `Body::Bytes` => `web::Bytes::from(...)`\n- `Body::Message` => `.boxed()` / `BoxBody`\n\n### `BoxBody`\n\n`BoxBody` is a new type-erased body type.\n\nIt can be useful when writing handlers, responders, and middleware when you want to trade a (very) small amount of performance for a simpler type.\n\nCreating a boxed body is done most efficiently by calling [`.boxed()`](https://docs.rs/actix-web/4/actix_web/body/trait.MessageBody.html#method.boxed) on a `MessageBody` type.\n\n### `EitherBody`\n\n`EitherBody` is a new \"either\" type that implements `MessageBody`\n\nIt is particularly useful in middleware that can bail early, returning their own response plus body type. By default the \"right\" variant is `BoxBody` (i.e., `EitherBody<B>` === `EitherBody<B, BoxBody>`) but it can be anything that implements `MessageBody`.\n\nFor example, it will be common among middleware which value performance of the hot path to use:\n\n```rust\ntype Response = Result<ServiceResponse<EitherBody<B>>, Error>\n```\n\nThis can be read (ignoring the `Result`) as \"resolves with a `ServiceResponse` that is either the inner service's `B` body type or a boxed body type from elsewhere, likely constructed within the middleware itself\". Of course, if your middleware contains only simple string other/error responses, it's possible to use them without boxes at the cost of a less simple implementation:\n\n```rust\ntype Response = Result<ServiceResponse<EitherBody<B, String>>, Error>\n```\n\n### Error Handlers\n\n`ErrorHandlers` is a commonly used middleware that has changed in design slightly due to the other body type changes.\n\nIn particular, an implicit `EitherBody` is used in the `ErrorHandlerResponse<B>` type. An `ErrorHandlerResponse<B>` now expects a `ServiceResponse<EitherBody<B>>` to be returned within response variants. The following is a migration for an error handler that **only modifies** the response argument (left body).\n\n```diff\n  fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {\n      res.response_mut().headers_mut().insert(\n          header::CONTENT_TYPE,\n          header::HeaderValue::from_static(\"Error\"),\n      );\n-     Ok(ErrorHandlerResponse::Response(res))\n+     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n  }\n```\n\nThe following is a migration for an error handler that creates a new response instead (right body).\n\n```diff\n  fn error_handler<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {\n-     let req = res.request().clone();\n+     let (req, _res) = res.into_parts();\n\n      let res = actix_files::NamedFile::open(\"./templates/404.html\")?\n          .set_status_code(StatusCode::NOT_FOUND)\n-         .into_response(&req)?\n-         .into_body();\n+         .into_response(&req);\n\n-     let res = ServiceResponse::new(req, res);\n+     let res = ServiceResponse::new(req, res).map_into_right_body();\n      Ok(ErrorHandlerResponse::Response(res))\n  }\n```\n\n## Middleware Trait APIs\n\nThe underlying traits that are used for creating middleware, `Service`, `ServiceFactory`, and `Transform`, have changed in design.\n\n- The associated `Request` type has moved to the type parameter position in order to allow multiple request implementations in other areas of the service stack.\n- The `self` arguments in `Service` have changed from exclusive (mutable) borrows to shared (immutable) borrows. Since most service layers, such as middleware, do not host mutable state, it reduces the runtime overhead in places where a `RefCell` used to be required for wrapping an inner service.\n- We've also introduced some macros that reduce boilerplate when implementing `poll_ready`.\n- Further to the guidance on [response body types](#response-body-types), any use of the old methods on `ServiceResponse` designed to match up body types (e.g., the old `into_body` method), should be replaced with an explicit response body type utilizing `EitherBody<B>`.\n\nA typical migration would look like this:\n\n```diff\n  use std::{\n-     cell::RefCell,\n      future::Future,\n      pin::Pin,\n      rc::Rc,\n-     task::{Context, Poll},\n  };\n\n  use actix_web::{\n      dev::{Service, ServiceRequest, ServiceResponse, Transform},\n      Error,\n  };\n  use futures_util::future::{ok, LocalBoxFuture, Ready};\n\n  pub struct SayHi;\n\n- impl<S, B> Transform<S> for SayHi\n+ impl<S, B> Transform<S, ServiceRequest> for SayHi\n  where\n-     S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n+     S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n      S::Future: 'static,\n      B: 'static,\n  {\n-     type Request = ServiceRequest;\n      type Response = ServiceResponse<B>;\n      type Error = Error;\n      type InitError = ();\n      type Transform = SayHiMiddleware<S>;\n      type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n      fn new_transform(&self, service: S) -> Self::Future {\n          ok(SayHiMiddleware {\n-             service: Rc::new(RefCell::new(service)),\n+             service: Rc::new(service),\n          })\n      }\n  }\n\n  pub struct SayHiMiddleware<S> {\n-     service: Rc<RefCell<S>>,\n+     service: Rc<S>,\n  }\n\n- impl<S, B> Service for SayHiMiddleware<S>\n+ impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>\n  where\n-     S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n+     S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n      S::Future: 'static,\n      B: 'static,\n  {\n-     type Request = ServiceRequest;\n      type Response = ServiceResponse<B>;\n      type Error = Error;\n      type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n-     fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n-         self.service.poll_ready(cx)\n-     }\n+     actix_web::dev::forward_ready!(service);\n\n-     fn call(&mut self, req: ServiceRequest) -> Self::Future {\n+     fn call(&self, req: ServiceRequest) -> Self::Future {\n          println!(\"Hi from start. You requested: {}\", req.path());\n\n          let fut = self.service.call(req);\n\n          Box::pin(async move {\n              let res = fut.await?;\n\n              println!(\"Hi from response\");\n              Ok(res)\n          })\n      }\n  }\n```\n\nThis new design is forward-looking and should ease transition to traits that support the upcoming Generic Associated Type (GAT) feature in Rust while also trimming down the boilerplate required to implement middleware.\n\nWe understand that creating middleware is still a pain point for Actix Web and we hope to provide [an even more ergonomic solution](https://docs.rs/actix-web-lab/0.11.0/actix_web_lab/middleware/fn.from_fn.html) in a v4.x release.\n\n## `Responder` Trait\n\nThe `Responder` trait's interface has changed. Errors should be handled and converted to responses within the `respond_to` method. It's also no longer async so the associated `type Future` has been removed; there was no compelling use case found for it. These changes simplify the interface and implementation a lot.\n\nNow that more emphasis is placed on expressive body types, as explained in the [body types migration section](#response-body-types), this trait has introduced an associated `type Body`. The simplest migration will be to use `BoxBody` + `.map_into_boxed_body()` but if there is a more expressive type for your responder then try to use that instead.\n\n```diff\n  impl Responder for &'static str {\n-     type Error = Error;\n-     type Future = Ready<Result<HttpResponse, Error>>;\n+     type Body = &'static str;\n\n-     fn respond_to(self, req: &HttpRequest) -> Self::Future {\n+     fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n          let res = HttpResponse::build(StatusCode::OK)\n              .content_type(\"text/plain; charset=utf-8\")\n              .body(self);\n\n-         ok(res)\n+         res\n      }\n  }\n```\n\n## `App::data` Deprecation :warning:\n\nThe `App::data` method is deprecated. Replace instances of this with `App::app_data`. Exposing both methods led to lots of confusion when trying to extract the data in handlers. Now, when using the `Data` wrapper, the type you put in to `app_data` is the same type you extract in handler arguments.\n\nYou may need to review the [guidance on shared mutable state](https://docs.rs/actix-web/4/actix_web/struct.App.html#shared-mutable-state) in order to migrate this correctly.\n\n```diff\n  use actix_web::web::Data;\n\n  #[get(\"/\")]\n  async fn handler(my_state: Data<MyState>) -> { todo!() }\n\n  HttpServer::new(|| {\n-     App::new()\n-         .data(MyState::default())\n-         .service(handler)\n\n+     let my_state: Data<MyState> = Data::new(MyState::default());\n+\n+     App::new()\n+         .app_data(my_state)\n+         .service(handler)\n  })\n```\n\n## Direct Dependency On `actix-rt` And `actix-service`\n\nImprovements to module management and re-exports have resulted in not needing direct dependencies on these underlying crates for the vast majority of cases. In particular:\n\n- all traits necessary for creating middlewares are now re-exported through the `dev` modules;\n- `#[actix_web::test]` now exists for async test definitions.\n\nRelying on these re-exports will ease the transition to future versions of Actix Web.\n\n```diff\n- use actix_service::{Service, Transform};\n+ use actix_web::dev::{Service, Transform};\n```\n\n```diff\n- #[actix_rt::test]\n+ #[actix_web::test]\n  async fn test_thing() {\n```\n\n## Server Must Be Polled :warning:\n\nIn order to _start_ serving requests, the `Server` object returned from `run` **must** be `poll`ed, `await`ed, or `spawn`ed. This was done to prevent unexpected behavior and ensure that things like signal handlers are able to function correctly when enabled.\n\nFor example, in this contrived example where the server is started and then the main thread is sent to sleep, the server will no longer be able to serve requests with v4.0:\n\n```rust\n#[actix_web::main]\nasync fn main() {\n    HttpServer::new(|| App::new().default_service(web::to(HttpResponse::Conflict)))\n        .bind((\"127.0.0.1\", 8080))\n        .unwrap()\n        .run();\n\n    thread::sleep(Duration::from_secs(1000));\n}\n```\n\n## Guards API\n\nImplementors of routing guards will need to use the modified interface of the `Guard` trait. The API is more flexible than before. See [guard module docs](https://docs.rs/actix-web/4/actix_web/guard/struct.GuardContext.html) for more details.\n\n```diff\n  struct MethodGuard(HttpMethod);\n\n  impl Guard for MethodGuard {\n-     fn check(&self, request: &RequestHead) -> bool {\n+     fn check(&self, ctx: &GuardContext<'_>) -> bool {\n-         request.method == self.0\n+         ctx.head().method == self.0\n      }\n  }\n```\n\n## Returning `HttpResponse` synchronously\n\nThe implementation of `Future` for `HttpResponse` was removed because it was largely useless for all but the simplest handlers like `web::to(|| HttpResponse::Ok().finish())`. It also caused false positives on the `async_yields_async` clippy lint in reasonable scenarios. The compiler errors will looks something like:\n\n```\nweb::to(|| HttpResponse::Ok().finish())\n^^^^^^^ the trait `Handler<_>` is not implemented for `[closure@...]`\n```\n\nThis form should be replaced with explicit async functions and closures:\n\n```diff\n- fn handler() -> HttpResponse {\n+ async fn handler() -> HttpResponse {\n      HttpResponse::Ok().finish()\n  }\n```\n\n```diff\n- web::to(|| HttpResponse::Ok().finish())\n+ web::to(|| async { HttpResponse::Ok().finish() })\n```\n\nOr, for these extremely simple cases, utilise an `HttpResponseBuilder`:\n\n```diff\n- web::to(|| HttpResponse::Ok().finish())\n+ web::to(HttpResponse::Ok)\n```\n\n## `#[actix_web::main]` and `#[tokio::main]`\n\nActix Web now works seamlessly with the primary way of starting a multi-threaded Tokio runtime, `#[tokio::main]`. Therefore, it is no longer necessary to spawn a thread when you need to run something alongside Actix Web that uses Tokio's multi-threaded mode; you can simply await the server within this context or, if preferred, use `tokio::spawn` just like any other async task.\n\nFor now, `actix` actor support (and therefore WebSocket support via `actix-web-actors`) still requires `#[actix_web::main]` so that a `System` context is created. Designs are being created for an alternative WebSocket interface that does not require actors that should land sometime in the v4.x cycle.\n\n## `web::block`\n\nThe `web::block` helper has changed return type from roughly `async fn(fn() -> Result<T, E>) Result<T, BlockingError<E>>` to `async fn(fn() -> T) Result<T, BlockingError>`. That's to say that the blocking function can now return things that are not `Result`s and it does not wrap error types anymore. If you still need to return `Result`s then you'll likely want to use double `?` after the `.await`.\n\n```diff\n- let n: u32 = web::block(|| Ok(123)).await?;\n+ let n: u32 = web::block(|| 123).await?;\n\n- let n: u32 = web::block(|| Ok(123)).await?;\n+ let n: u32 = web::block(|| Ok(123)).await??;\n```\n\n## `HttpResponse` as a `ResponseError`\n\nThe implementation of `ResponseError` for `HttpResponse` has been removed.\n\nIt was common in v3 to use `HttpResponse` as an error type in fallible handlers. The problem is that `HttpResponse` contains no knowledge or reference to the source error. Being able to guarantee that an \"error\" response actually contains an error reference makes middleware and other parts of Actix Web more effective.\n\nThe error response builders in the `error` module were available in v3 but are now the best method for simple error responses without requiring you to implement the trait on your own custom error types. These builders can receive simple strings and third party errors that can not implement the `ResponseError` trait.\n\nA few common patterns are affected by this change:\n\n```diff\n- Err(HttpResponse::InternalServerError().finish())\n+ Err(error::ErrorInternalServerError(\"reason\"))\n\n- Err(HttpResponse::InternalServerError().body(third_party_error.to_string()))\n+ Err(error::ErrorInternalServerError(err))\n\n- .map_err(|err| HttpResponse::InternalServerError().finish())?\n+ .map_err(error::ErrorInternalServerError)?\n```\n"
  },
  {
    "path": "actix-web/README.md",
    "content": "<div align=\"center\">\n  <h1>Actix Web</h1>\n  <p>\n    <strong>Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust</strong>\n  </p>\n  <p>\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web)\n[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.13.0)](https://docs.rs/actix-web/4.13.0)\n![MSRV](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg)\n[![Dependency Status](https://deps.rs/crate/actix-web/4.13.0/status.svg)](https://deps.rs/crate/actix-web/4.13.0)\n<br />\n[![CI](https://github.com/actix/actix-web/actions/workflows/ci.yml/badge.svg)](https://github.com/actix/actix-web/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/actix/actix-web/graph/badge.svg?token=dSwOnp9QCv)](https://codecov.io/gh/actix/actix-web)\n![downloads](https://img.shields.io/crates/d/actix-web.svg)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n  </p>\n</div>\n\n## Features\n\n- Supports _HTTP/1.x_ and _HTTP/2_\n- Streaming and pipelining\n- Powerful [request routing](https://actix.rs/docs/url-dispatch/) with optional macros\n- Full [Tokio](https://tokio.rs) compatibility\n- Keep-alive and slow requests handling\n- Client/server [WebSockets](https://actix.rs/docs/websockets/) support\n- Transparent content compression/decompression (br, gzip, deflate, zstd)\n- Multipart streams\n- Static assets\n- SSL support using OpenSSL or Rustls\n- Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))\n- Integrates with the [`awc` HTTP client](https://docs.rs/awc/)\n- Runs on stable Rust 1.88+\n\n### Experimental features\n\nTo enable faster release iterations, we mark some features as experimental.\nThese features are prefixed with `experimental` and a breaking change may happen at any release.\nPlease use them in a production environment at your own risk.\n\n- `experimental-introspection`: exposes route and method reporting helpers for local diagnostics\n  and tooling. See [`examples/introspection.rs`](examples/introspection.rs) and\n  [`examples/introspection_multi_servers.rs`](examples/introspection_multi_servers.rs).\n\n## Documentation\n\n- [Website & User Guide](https://actix.rs)\n- [Examples Repository](https://github.com/actix/examples)\n- [API Documentation](https://docs.rs/actix-web)\n- [API Documentation (mainranch)](https://actix.rs/actix-web/actix_web)\n\n## Example\n\nDependencies:\n\n```toml\n[dependencies]\nactix-web = \"4\"\n```\n\nCode:\n\n```rust\nuse actix_web::{get, web, App, HttpServer, Responder};\n\n#[get(\"/hello/{name}\")]\nasync fn greet(name: web::Path<String>) -> impl Responder {\n    format!(\"Hello {name}!\")\n}\n\n#[actix_web::main] // or #[tokio::main]\nasync fn main() -> std::io::Result<()> {\n    HttpServer::new(|| {\n        App::new().service(greet)\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\n### More Examples\n\n- [Hello World](https://github.com/actix/examples/tree/main/basics/hello-world)\n- [Basic Setup](https://github.com/actix/examples/tree/main/basics/basics)\n- [Application State](https://github.com/actix/examples/tree/main/basics/state)\n- [JSON Handling](https://github.com/actix/examples/tree/main/json/json)\n- [Multipart Streams](https://github.com/actix/examples/tree/main/forms/multipart)\n- [MongoDB Integration](https://github.com/actix/examples/tree/main/databases/mongodb)\n- [Diesel Integration](https://github.com/actix/examples/tree/main/databases/diesel)\n- [SQLite Integration](https://github.com/actix/examples/tree/main/databases/sqlite)\n- [Postgres Integration](https://github.com/actix/examples/tree/main/databases/postgres)\n- [Tera Templates](https://github.com/actix/examples/tree/main/templating/tera)\n- [Askama Templates](https://github.com/actix/examples/tree/main/templating/askama)\n- [HTTPS using Rustls](https://github.com/actix/examples/tree/main/https-tls/rustls)\n- [HTTPS using OpenSSL](https://github.com/actix/examples/tree/main/https-tls/openssl)\n- [Simple WebSocket](https://github.com/actix/examples/tree/main/websockets)\n- [WebSocket Chat](https://github.com/actix/examples/tree/main/websockets/chat)\n\nYou may consider checking out [this directory](https://github.com/actix/examples/tree/main) for more examples.\n\n## Benchmarks\n\nOne of the fastest web frameworks available according to the [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r21&test=composite).\n\n## License\n\nThis project is licensed under either of the following licenses, at your option:\n\n- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0])\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT])\n\n## Code of Conduct\n\nContribution to the `actix/actix-web` repo is organized under the terms of the Contributor Covenant. The Actix team promises to intervene to uphold that code of conduct.\n"
  },
  {
    "path": "actix-web/benches/responder.rs",
    "content": "use std::{future::Future, time::Instant};\n\nuse actix_http::body::BoxBody;\nuse actix_utils::future::{ready, Ready};\nuse actix_web::{http::StatusCode, test::TestRequest, Error, HttpRequest, HttpResponse, Responder};\nuse criterion::{criterion_group, criterion_main, Criterion};\nuse futures_util::future::join_all;\n\n// responder simulate the old responder trait.\ntrait FutureResponder {\n    type Error;\n    type Future: Future<Output = Result<HttpResponse, Self::Error>>;\n\n    fn future_respond_to(self, req: &HttpRequest) -> Self::Future;\n}\n\n// a simple wrapper type around string\nstruct StringResponder(String);\n\nimpl FutureResponder for StringResponder {\n    type Error = Error;\n    type Future = Ready<Result<HttpResponse, Self::Error>>;\n\n    fn future_respond_to(self, _: &HttpRequest) -> Self::Future {\n        // this is default builder for string response in both new and old responder trait.\n        ready(Ok(HttpResponse::build(StatusCode::OK)\n            .content_type(\"text/plain; charset=utf-8\")\n            .body(self.0)))\n    }\n}\n\nimpl Responder for StringResponder {\n    type Body = BoxBody;\n\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        HttpResponse::build(StatusCode::OK)\n            .content_type(\"text/plain; charset=utf-8\")\n            .body(self.0)\n    }\n}\n\nfn future_responder(c: &mut Criterion) {\n    let rt = actix_rt::System::new();\n    let req = TestRequest::default().to_http_request();\n\n    c.bench_function(\"future_responder\", move |b| {\n        b.iter_custom(|_| {\n            let futs = (0..100_000).map(|_| async {\n                StringResponder(String::from(\"Hello World!!\"))\n                    .future_respond_to(&req)\n                    .await\n            });\n\n            let futs = join_all(futs);\n\n            let start = Instant::now();\n\n            let _res = rt.block_on(futs);\n\n            start.elapsed()\n        })\n    });\n}\n\nfn responder(c: &mut Criterion) {\n    let rt = actix_rt::System::new();\n    let req = TestRequest::default().to_http_request();\n    c.bench_function(\"responder\", move |b| {\n        b.iter_custom(|_| {\n            let responders = (0..100_000).map(|_| StringResponder(String::from(\"Hello World!!\")));\n\n            let start = Instant::now();\n            let _res = rt.block_on(async {\n                // don't need runtime block on but to be fair.\n                responders.map(|r| r.respond_to(&req)).collect::<Vec<_>>()\n            });\n\n            start.elapsed()\n        })\n    });\n}\n\ncriterion_group!(responder_bench, future_responder, responder);\ncriterion_main!(responder_bench);\n"
  },
  {
    "path": "actix-web/benches/server.rs",
    "content": "use actix_web::{web, App, HttpResponse};\nuse awc::Client;\nuse criterion::{criterion_group, criterion_main, Criterion};\nuse futures_util::future::join_all;\n\nconst STR: &str = \"Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World \\\n                   Hello World Hello World Hello World Hello World Hello World\";\n\n// benchmark sending all requests at the same time\nfn bench_async_burst(c: &mut Criterion) {\n    // We are using System here, since Runtime requires preinitialized tokio\n    // Maybe add to actix_rt docs\n    let rt = actix_rt::System::new();\n\n    let srv = rt.block_on(async {\n        actix_test::start(|| {\n            App::new().service(\n                web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(STR) })),\n            )\n        })\n    });\n\n    let url = srv.url(\"/\");\n\n    c.bench_function(\"get_body_async_burst\", move |b| {\n        b.iter_custom(|iters| {\n            rt.block_on(async {\n                let client = Client::new().get(url.clone()).freeze().unwrap();\n\n                let start = std::time::Instant::now();\n                // benchmark body\n\n                let burst = (0..iters).map(|_| client.send());\n                let resps = join_all(burst).await;\n\n                let elapsed = start.elapsed();\n\n                // if there are failed requests that might be an issue\n                let failed = resps.iter().filter(|r| r.is_err()).count();\n                if failed > 0 {\n                    eprintln!(\"failed {} requests (might be bench timeout)\", failed);\n                };\n\n                elapsed\n            })\n        })\n    });\n}\n\ncriterion_group!(server_benches, bench_async_burst);\ncriterion_main!(server_benches);\n"
  },
  {
    "path": "actix-web/benches/service.rs",
    "content": "use std::{cell::RefCell, rc::Rc};\n\nuse actix_service::Service;\nuse actix_web::{\n    dev::{ServiceRequest, ServiceResponse},\n    test::{init_service, ok_service, TestRequest},\n    web, App, Error, HttpResponse,\n};\nuse criterion::{criterion_main, Criterion};\n\n/// Criterion Benchmark for async Service\n/// Should be used from within criterion group:\n/// ```ignore\n/// let mut criterion: ::criterion::Criterion<_> =\n///     ::criterion::Criterion::default().configure_from_args();\n/// bench_async_service(&mut criterion, ok_service(), \"async_service_direct\");\n/// ```\n///\n/// Usable for benching Service wrappers:\n/// Using minimum service code implementation we first measure\n/// time to run minimum service, then measure time with wrapper.\n///\n/// Sample output\n/// async_service_direct    time:   [1.0908 us 1.1656 us 1.2613 us]\npub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,\n{\n    let rt = actix_rt::System::new();\n    let srv = Rc::new(RefCell::new(srv));\n\n    let req = TestRequest::default().to_srv_request();\n    assert!(rt\n        .block_on(srv.borrow_mut().call(req))\n        .unwrap()\n        .status()\n        .is_success());\n\n    // start benchmark loops\n    c.bench_function(name, move |b| {\n        b.iter_custom(|iters| {\n            let srv = srv.clone();\n            // exclude request generation, it appears it takes significant time vs call (3us vs 1us)\n            let futs = (0..iters)\n                .map(|_| TestRequest::default().to_srv_request())\n                .map(|req| srv.borrow_mut().call(req));\n\n            let start = std::time::Instant::now();\n            // benchmark body\n            rt.block_on(async move {\n                for fut in futs {\n                    fut.await.unwrap();\n                }\n            });\n            // check that at least first request succeeded\n            start.elapsed()\n        })\n    });\n}\n\nasync fn index(req: ServiceRequest) -> Result<ServiceResponse, Error> {\n    Ok(req.into_response(HttpResponse::Ok().finish()))\n}\n\n// Benchmark basic WebService directly\n// this approach is usable for benching WebService, though it adds some time to direct service call:\n// Sample results on MacBook Pro '14\n// time:   [2.0724 us 2.1345 us 2.2074 us]\nfn async_web_service(c: &mut Criterion) {\n    let rt = actix_rt::System::new();\n    let srv = Rc::new(RefCell::new(rt.block_on(init_service(\n        App::new().service(web::service(\"/\").finish(index)),\n    ))));\n\n    let req = TestRequest::get().uri(\"/\").to_request();\n    assert!(rt\n        .block_on(srv.borrow_mut().call(req))\n        .unwrap()\n        .status()\n        .is_success());\n\n    // start benchmark loops\n    c.bench_function(\"async_web_service_direct\", move |b| {\n        b.iter_custom(|iters| {\n            let srv = srv.clone();\n            let futs = (0..iters)\n                .map(|_| TestRequest::get().uri(\"/\").to_request())\n                .map(|req| srv.borrow_mut().call(req));\n            let start = std::time::Instant::now();\n            // benchmark body\n            rt.block_on(async move {\n                for fut in futs {\n                    fut.await.unwrap();\n                }\n            });\n            // check that at least first request succeeded\n            start.elapsed()\n        })\n    });\n}\n\npub fn service_benches() {\n    let mut criterion: ::criterion::Criterion<_> =\n        ::criterion::Criterion::default().configure_from_args();\n    bench_async_service(&mut criterion, ok_service(), \"async_service_direct\");\n    async_web_service(&mut criterion);\n}\ncriterion_main!(service_benches);\n"
  },
  {
    "path": "actix-web/examples/README.md",
    "content": "# Actix Web Examples\n\nThis folder contain just a few standalone code samples. There is a much larger registry of example projects [in the examples repo](https://github.com/actix/examples).\n"
  },
  {
    "path": "actix-web/examples/basic.rs",
    "content": "use actix_web::{get, middleware, web, App, HttpRequest, HttpResponse, HttpServer};\n\n#[get(\"/resource1/{name}/index.html\")]\nasync fn index(req: HttpRequest, name: web::Path<String>) -> String {\n    println!(\"REQ: {:?}\", req);\n    format!(\"Hello: {}!\\r\\n\", name)\n}\n\nasync fn index_async(req: HttpRequest) -> &'static str {\n    println!(\"REQ: {:?}\", req);\n    \"Hello world!\\r\\n\"\n}\n\n#[get(\"/\")]\nasync fn no_params() -> &'static str {\n    \"Hello world!\\r\\n\"\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    log::info!(\"starting HTTP server at http://localhost:8080\");\n\n    HttpServer::new(|| {\n        App::new()\n            .wrap(middleware::DefaultHeaders::new().add((\"X-Version\", \"0.2\")))\n            .wrap(middleware::Compress::default())\n            .wrap(middleware::Logger::default().log_target(\"http_log\"))\n            .service(index)\n            .service(no_params)\n            .service(\n                web::resource(\"/resource2/index.html\")\n                    .wrap(middleware::DefaultHeaders::new().add((\"X-Version-R2\", \"0.3\")))\n                    .default_service(web::route().to(HttpResponse::MethodNotAllowed))\n                    .route(web::get().to(index_async)),\n            )\n            .service(web::resource(\"/test1.html\").to(|| async { \"Test\\r\\n\" }))\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .workers(1)\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-web/examples/from_fn.rs",
    "content": "//! Shows a few of ways to use the `from_fn` middleware.\n\nuse std::{collections::HashMap, io, rc::Rc, time::Duration};\n\nuse actix_web::{\n    body::MessageBody,\n    dev::{Service, ServiceRequest, ServiceResponse, Transform},\n    http::header::{self, HeaderValue, Range},\n    middleware::{from_fn, Logger, Next},\n    web::{self, Header, Query},\n    App, Error, HttpResponse, HttpServer,\n};\nuse tracing::info;\n\nasync fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {\n    next.call(req).await\n}\n\nasync fn print_range_header<B>(\n    range_header: Option<Header<Range>>,\n    req: ServiceRequest,\n    next: Next<B>,\n) -> Result<ServiceResponse<B>, Error> {\n    if let Some(Header(range)) = range_header {\n        println!(\"Range: {range}\");\n    } else {\n        println!(\"No Range header\");\n    }\n\n    next.call(req).await\n}\n\nasync fn mutate_body_type(\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    let res = next.call(req).await?;\n    Ok(res.map_into_left_body::<()>())\n}\n\nasync fn mutate_body_type_with_extractors(\n    string_body: String,\n    query: Query<HashMap<String, String>>,\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    println!(\"body is: {string_body}\");\n    println!(\"query string: {query:?}\");\n\n    let res = next.call(req).await?;\n\n    Ok(res.map_body(move |_, _| string_body))\n}\n\nasync fn timeout_10secs(\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    match tokio::time::timeout(Duration::from_secs(10), next.call(req)).await {\n        Ok(res) => res,\n        Err(_err) => Err(actix_web::error::ErrorRequestTimeout(\"\")),\n    }\n}\n\nstruct MyMw(bool);\n\nimpl MyMw {\n    async fn mw_cb(\n        &self,\n        req: ServiceRequest,\n        next: Next<impl MessageBody + 'static>,\n    ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n        let mut res = match self.0 {\n            true => req.into_response(\"short-circuited\").map_into_right_body(),\n            false => next.call(req).await?.map_into_left_body(),\n        };\n\n        res.headers_mut()\n            .insert(header::WARNING, HeaderValue::from_static(\"42\"));\n\n        Ok(res)\n    }\n\n    pub fn into_middleware<S, B>(\n        self,\n    ) -> impl Transform<\n        S,\n        ServiceRequest,\n        Response = ServiceResponse<impl MessageBody>,\n        Error = Error,\n        InitError = (),\n    >\n    where\n        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n        B: MessageBody + 'static,\n    {\n        let this = Rc::new(self);\n        from_fn(move |req, next| {\n            let this = Rc::clone(&this);\n            async move { Self::mw_cb(&this, req, next).await }\n        })\n    }\n}\n\n#[actix_web::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    let bind = (\"127.0.0.1\", 8080);\n    info!(\"staring server at http://{}:{}\", &bind.0, &bind.1);\n\n    HttpServer::new(|| {\n        App::new()\n            .wrap(from_fn(noop))\n            .wrap(from_fn(print_range_header))\n            .wrap(from_fn(mutate_body_type))\n            .wrap(from_fn(mutate_body_type_with_extractors))\n            .wrap(from_fn(timeout_10secs))\n            // switch bool to true to observe early response\n            .wrap(MyMw(false).into_middleware())\n            .wrap(Logger::default())\n            .default_service(web::to(HttpResponse::Ok))\n    })\n    .workers(1)\n    .bind(bind)?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-web/examples/introspection.rs",
    "content": "// Example showcasing the experimental introspection feature.\n// Run with: `cargo run --features experimental-introspection --example introspection`\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    #[cfg(feature = \"experimental-introspection\")]\n    {\n        use actix_web::{dev::Service, guard, web, App, HttpResponse, HttpServer, Responder};\n        use serde::Deserialize;\n\n        // Initialize logging\n        env_logger::Builder::new()\n            .filter_level(log::LevelFilter::Debug)\n            .init();\n\n        // Custom guard to check if the Content-Type header is present.\n        struct ContentTypeGuard;\n\n        impl guard::Guard for ContentTypeGuard {\n            fn check(&self, req: &guard::GuardContext<'_>) -> bool {\n                req.head()\n                    .headers()\n                    .contains_key(actix_web::http::header::CONTENT_TYPE)\n            }\n        }\n\n        // Data structure for endpoints that receive JSON.\n        #[derive(Deserialize)]\n        struct UserInfo {\n            username: String,\n            age: u8,\n        }\n\n        // GET /introspection for JSON response\n        async fn introspection_handler_json(\n            tree: web::Data<actix_web::introspection::IntrospectionTree>,\n        ) -> impl Responder {\n            let report = tree.report_as_json();\n            HttpResponse::Ok()\n                .content_type(\"application/json\")\n                .body(report)\n        }\n\n        // GET /introspection/externals for external resources report\n        async fn introspection_handler_externals(\n            tree: web::Data<actix_web::introspection::IntrospectionTree>,\n        ) -> impl Responder {\n            let report = tree.report_externals_as_json();\n            HttpResponse::Ok()\n                .content_type(\"application/json\")\n                .body(report)\n        }\n\n        // GET /introspection for plain text response\n        async fn introspection_handler_text(\n            tree: web::Data<actix_web::introspection::IntrospectionTree>,\n        ) -> impl Responder {\n            let report = tree.report_as_text();\n            HttpResponse::Ok().content_type(\"text/plain\").body(report)\n        }\n\n        // GET /api/v1/item/{id} and GET /v1/item/{id}\n        #[actix_web::get(\"/item/{id}\")]\n        async fn get_item(path: web::Path<u32>) -> impl Responder {\n            let id = path.into_inner();\n            HttpResponse::Ok().body(format!(\"Requested item with id: {}\", id))\n        }\n\n        // POST /api/v1/info\n        #[actix_web::post(\"/info\")]\n        async fn post_user_info(info: web::Json<UserInfo>) -> impl Responder {\n            HttpResponse::Ok().json(format!(\n                \"User {} with age {} received\",\n                info.username, info.age\n            ))\n        }\n\n        // /api/v1/guarded\n        async fn guarded_handler() -> impl Responder {\n            HttpResponse::Ok().body(\"Passed the Content-Type guard!\")\n        }\n\n        // GET /api/v2/hello\n        async fn hello_v2() -> impl Responder {\n            HttpResponse::Ok().body(\"Hello from API v2!\")\n        }\n\n        // GET /admin/dashboard\n        async fn admin_dashboard() -> impl Responder {\n            HttpResponse::Ok().body(\"Welcome to the Admin Dashboard!\")\n        }\n\n        // GET /admin/settings\n        async fn get_settings() -> impl Responder {\n            HttpResponse::Ok().body(\"Current settings: ...\")\n        }\n\n        // POST /admin/settings\n        async fn update_settings() -> impl Responder {\n            HttpResponse::Ok().body(\"Settings have been updated!\")\n        }\n\n        // GET and POST on /\n        async fn root_index() -> impl Responder {\n            HttpResponse::Ok().body(\"Welcome to the Root Endpoint!\")\n        }\n\n        // GET /alpha and /beta (named multi-pattern resource)\n        async fn multi_pattern() -> impl Responder {\n            HttpResponse::Ok().body(\"Hello from multi-pattern resource!\")\n        }\n\n        // GET /acceptable (Acceptable guard)\n        async fn acceptable_guarded() -> impl Responder {\n            HttpResponse::Ok().body(\"Acceptable guard matched!\")\n        }\n\n        // GET /hosted (Host guard)\n        async fn host_guarded() -> impl Responder {\n            HttpResponse::Ok().body(\"Host guard matched!\")\n        }\n\n        // Additional endpoints for /extra\n        fn extra_endpoints(cfg: &mut web::ServiceConfig) {\n            cfg.service(\n                web::scope(\"/extra\")\n                    .route(\n                        \"/ping\",\n                        web::get().to(|| async { HttpResponse::Ok().body(\"pong\") }), // GET /extra/ping\n                    )\n                    .service(\n                        web::resource(\"/multi\")\n                            .route(web::get().to(|| async {\n                                HttpResponse::Ok().body(\"GET response from /extra/multi\")\n                            })) // GET /extra/multi\n                            .route(web::post().to(|| async {\n                                HttpResponse::Ok().body(\"POST response from /extra/multi\")\n                            })), // POST /extra/multi\n                    )\n                    .service(\n                        web::scope(\"{entities_id:\\\\d+}\")\n                            .service(\n                                web::scope(\"/secure\")\n                                    .route(\n                                        \"\",\n                                        web::get().to(|| async {\n                                            HttpResponse::Ok()\n                                                .body(\"GET response from /extra/secure\")\n                                        }),\n                                    ) // GET /extra/{entities_id}/secure/\n                                    .route(\n                                        \"/post\",\n                                        web::post().to(|| async {\n                                            HttpResponse::Ok()\n                                                .body(\"POST response from /extra/secure\")\n                                        }),\n                                    ), // POST /extra/{entities_id}/secure/post\n                            )\n                            .wrap_fn(|req, srv| {\n                                println!(\n                                    \"Request to /extra/secure with id: {}\",\n                                    req.match_info().get(\"entities_id\").unwrap()\n                                );\n                                let fut = srv.call(req);\n                                async move {\n                                    let res = fut.await?;\n                                    Ok(res)\n                                }\n                            }),\n                    ),\n            );\n        }\n\n        // Additional endpoints for /foo\n        fn other_endpoints(cfg: &mut web::ServiceConfig) {\n            cfg.service(\n                web::scope(\"/extra\")\n                    .route(\n                        \"/ping\",\n                        web::post()\n                            .to(|| async { HttpResponse::Ok().body(\"post from /extra/ping\") }), // POST /foo/extra/ping\n                    )\n                    .route(\n                        \"/ping\",\n                        web::delete()\n                            .to(|| async { HttpResponse::Ok().body(\"delete from /extra/ping\") }), // DELETE /foo/extra/ping\n                    ),\n            );\n        }\n\n        // Create the HTTP server with all the routes and handlers\n        let server = HttpServer::new(|| {\n            App::new()\n                // Get introspection report\n                // curl --location '127.0.0.1:8080/introspection' --header 'Accept: application/json'\n                // curl --location '127.0.0.1:8080/introspection' --header 'Accept: text/plain'\n                // curl --location '127.0.0.1:8080/introspection/externals'\n                .external_resource(\"app-external\", \"https://example.com/{id}\")\n                .service(\n                    web::resource(\"/introspection\")\n                        .route(\n                            web::get()\n                                .guard(guard::Header(\"accept\", \"application/json\"))\n                                .to(introspection_handler_json),\n                        )\n                        .route(\n                            web::get()\n                                .guard(guard::Header(\"accept\", \"text/plain\"))\n                                .to(introspection_handler_text),\n                        ),\n                )\n                .service(\n                    web::resource(\"/introspection/externals\")\n                        .route(web::get().to(introspection_handler_externals)),\n                )\n                .service(\n                    web::resource([\"/alpha\", \"/beta\"])\n                        .name(\"multi\")\n                        .route(web::get().to(multi_pattern)),\n                )\n                .route(\n                    \"/acceptable\",\n                    web::get()\n                        .guard(guard::Acceptable::new(mime::APPLICATION_JSON).match_star_star())\n                        .to(acceptable_guarded),\n                )\n                .route(\n                    \"/hosted\",\n                    web::get().guard(guard::Host(\"127.0.0.1\")).to(host_guarded),\n                )\n                // API endpoints under /api\n                .service(\n                    web::scope(\"/api\")\n                        .configure(|cfg| {\n                            cfg.external_resource(\"api-external\", \"https://api.example/{id}\");\n                        })\n                        // Endpoints under /api/v1\n                        .service(\n                            web::scope(\"/v1\")\n                                .service(get_item) // GET /api/v1/item/{id}\n                                .service(post_user_info) // POST /api/v1/info\n                                .route(\n                                    \"/guarded\",\n                                    web::route().guard(ContentTypeGuard).to(guarded_handler), // /api/v1/guarded\n                                ),\n                        )\n                        // Endpoints under /api/v2\n                        .service(web::scope(\"/v2\").route(\"/hello\", web::get().to(hello_v2))), // GET /api/v2/hello\n                )\n                // Endpoints under /v1 (outside /api)\n                .service(web::scope(\"/v1\").service(get_item)) // GET /v1/item/{id}\n                // Admin endpoints under /admin\n                .service(\n                    web::scope(\"/admin\")\n                        .route(\"/dashboard\", web::get().to(admin_dashboard)) // GET /admin/dashboard\n                        .service(\n                            web::resource(\"/settings\")\n                                .route(web::get().to(get_settings)) // GET /admin/settings\n                                .route(web::post().to(update_settings)), // POST /admin/settings\n                        ),\n                )\n                // Root endpoints\n                .service(\n                    web::resource(\"/\")\n                        .route(web::get().to(root_index)) // GET /\n                        .route(web::post().to(root_index)), // POST /\n                )\n                // Endpoints under /bar\n                .service(web::scope(\"/bar\").configure(extra_endpoints)) // /bar/extra/ping, /bar/extra/multi, etc.\n                // Endpoints under /foo\n                .service(web::scope(\"/foo\").configure(other_endpoints)) // /foo/extra/ping with POST and DELETE\n                // Additional endpoints under /extra\n                .configure(extra_endpoints) // /extra/ping, /extra/multi, etc.\n                .configure(other_endpoints)\n                // Endpoint that rejects GET on /not_guard (allows other methods)\n                .route(\n                    \"/not_guard\",\n                    web::route()\n                        .guard(guard::Not(guard::Get()))\n                        .to(HttpResponse::MethodNotAllowed),\n                )\n                // Endpoint that requires GET with header or POST on /all_guard\n                .route(\n                    \"/all_guard\",\n                    web::route()\n                        .guard(\n                            guard::All(guard::Get())\n                                .and(guard::Header(\"content-type\", \"plain/text\"))\n                                .and(guard::Any(guard::Post())),\n                        )\n                        .to(HttpResponse::MethodNotAllowed),\n                )\n        })\n        .workers(5)\n        .bind(\"127.0.0.1:8080\")?;\n\n        server.run().await\n    }\n    #[cfg(not(feature = \"experimental-introspection\"))]\n    {\n        eprintln!(\"This example requires the 'experimental-introspection' feature to be enabled.\");\n        std::process::exit(1);\n    }\n}\n"
  },
  {
    "path": "actix-web/examples/introspection_multi_servers.rs",
    "content": "// Example showcasing the experimental introspection feature with multiple App instances.\n// Run with: `cargo run --features experimental-introspection --example introspection_multi_servers`\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    #[cfg(feature = \"experimental-introspection\")]\n    {\n        use actix_web::{web, App, HttpResponse, HttpServer, Responder};\n        use futures_util::future;\n\n        async fn introspection_handler(\n            tree: web::Data<actix_web::introspection::IntrospectionTree>,\n        ) -> impl Responder {\n            HttpResponse::Ok()\n                .content_type(\"text/plain\")\n                .body(tree.report_as_text())\n        }\n\n        async fn index() -> impl Responder {\n            HttpResponse::Ok().body(\"Hello from app\")\n        }\n\n        let srv1 = HttpServer::new(|| {\n            App::new()\n                .service(web::resource(\"/a\").route(web::get().to(index)))\n                .service(\n                    web::resource(\"/introspection\").route(web::get().to(introspection_handler)),\n                )\n        })\n        .workers(8)\n        .bind(\"127.0.0.1:8081\")?\n        .run();\n\n        let srv2 = HttpServer::new(|| {\n            App::new()\n                .service(web::resource(\"/b\").route(web::get().to(index)))\n                .service(\n                    web::resource(\"/introspection\").route(web::get().to(introspection_handler)),\n                )\n        })\n        .workers(3)\n        .bind(\"127.0.0.1:8082\")?\n        .run();\n\n        future::try_join(srv1, srv2).await?;\n    }\n    #[cfg(not(feature = \"experimental-introspection\"))]\n    {\n        eprintln!(\"This example requires the 'experimental-introspection' feature to be enabled.\");\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "actix-web/examples/macroless.rs",
    "content": "use actix_web::{middleware, rt, web, App, HttpRequest, HttpServer};\n\nasync fn index(req: HttpRequest) -> &'static str {\n    println!(\"REQ: {:?}\", req);\n    \"Hello world!\\r\\n\"\n}\n\nfn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    rt::System::new().block_on(\n        HttpServer::new(|| {\n            App::new()\n                .wrap(middleware::Logger::default())\n                .service(web::resource(\"/\").route(web::get().to(index)))\n        })\n        .bind((\"127.0.0.1\", 8080))?\n        .workers(1)\n        .run(),\n    )\n}\n"
  },
  {
    "path": "actix-web/examples/middleware_from_fn.rs",
    "content": "//! Shows a couple of ways to use the `from_fn` middleware.\n\nuse std::{collections::HashMap, io, rc::Rc, time::Duration};\n\nuse actix_web::{\n    body::MessageBody,\n    dev::{Service, ServiceRequest, ServiceResponse, Transform},\n    http::header::{self, HeaderValue, Range},\n    middleware::{from_fn, Logger, Next},\n    web::{self, Header, Query},\n    App, Error, HttpResponse, HttpServer,\n};\n\nasync fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {\n    next.call(req).await\n}\n\nasync fn print_range_header<B>(\n    range_header: Option<Header<Range>>,\n    req: ServiceRequest,\n    next: Next<B>,\n) -> Result<ServiceResponse<B>, Error> {\n    if let Some(Header(range)) = range_header {\n        println!(\"Range: {range}\");\n    } else {\n        println!(\"No Range header\");\n    }\n\n    next.call(req).await\n}\n\nasync fn mutate_body_type(\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    let res = next.call(req).await?;\n    Ok(res.map_into_left_body::<()>())\n}\n\nasync fn mutate_body_type_with_extractors(\n    string_body: String,\n    query: Query<HashMap<String, String>>,\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    println!(\"body is: {string_body}\");\n    println!(\"query string: {query:?}\");\n\n    let res = next.call(req).await?;\n\n    Ok(res.map_body(move |_, _| string_body))\n}\n\nasync fn timeout_10secs(\n    req: ServiceRequest,\n    next: Next<impl MessageBody + 'static>,\n) -> Result<ServiceResponse<impl MessageBody>, Error> {\n    match tokio::time::timeout(Duration::from_secs(10), next.call(req)).await {\n        Ok(res) => res,\n        Err(_err) => Err(actix_web::error::ErrorRequestTimeout(\"\")),\n    }\n}\n\nstruct MyMw(bool);\n\nimpl MyMw {\n    async fn mw_cb(\n        &self,\n        req: ServiceRequest,\n        next: Next<impl MessageBody + 'static>,\n    ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n        let mut res = match self.0 {\n            true => req.into_response(\"short-circuited\").map_into_right_body(),\n            false => next.call(req).await?.map_into_left_body(),\n        };\n\n        res.headers_mut()\n            .insert(header::WARNING, HeaderValue::from_static(\"42\"));\n\n        Ok(res)\n    }\n\n    pub fn into_middleware<S, B>(\n        self,\n    ) -> impl Transform<\n        S,\n        ServiceRequest,\n        Response = ServiceResponse<impl MessageBody>,\n        Error = Error,\n        InitError = (),\n    >\n    where\n        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n        B: MessageBody + 'static,\n    {\n        let this = Rc::new(self);\n        from_fn(move |req, next| {\n            let this = Rc::clone(&this);\n            async move { Self::mw_cb(&this, req, next).await }\n        })\n    }\n}\n\n#[actix_web::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    let bind = (\"127.0.0.1\", 8080);\n    log::info!(\"staring server at http://{}:{}\", &bind.0, &bind.1);\n\n    HttpServer::new(|| {\n        App::new()\n            .wrap(from_fn(noop))\n            .wrap(from_fn(print_range_header))\n            .wrap(from_fn(mutate_body_type))\n            .wrap(from_fn(mutate_body_type_with_extractors))\n            .wrap(from_fn(timeout_10secs))\n            // switch bool to true to observe early response\n            .wrap(MyMw(false).into_middleware())\n            .wrap(Logger::default())\n            .default_service(web::to(HttpResponse::Ok))\n    })\n    .workers(1)\n    .bind(bind)?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-web/examples/on-connect.rs",
    "content": "//! This example shows how to use `actix_web::HttpServer::on_connect` to access a lower-level socket\n//! properties and pass them to a handler through request-local data.\n//!\n//! For an example of extracting a client TLS certificate, see:\n//! <https://github.com/actix/examples/tree/main/https-tls/rustls-client-cert>\n\nuse std::{any::Any, io, net::SocketAddr};\n\nuse actix_web::{\n    dev::Extensions, rt::net::TcpStream, web, App, HttpRequest, HttpResponse, HttpServer, Responder,\n};\n\n#[allow(dead_code)]\n#[derive(Debug, Clone)]\nstruct ConnectionInfo {\n    bind: SocketAddr,\n    peer: SocketAddr,\n    ttl: Option<u32>,\n}\n\nasync fn route_whoami(req: HttpRequest) -> impl Responder {\n    match req.conn_data::<ConnectionInfo>() {\n        Some(info) => HttpResponse::Ok().body(format!(\n            \"Here is some info about your connection:\\n\\n{info:#?}\",\n        )),\n        None => HttpResponse::InternalServerError().body(\"Missing expected request extension data\"),\n    }\n}\n\nfn get_conn_info(connection: &dyn Any, data: &mut Extensions) {\n    if let Some(sock) = connection.downcast_ref::<TcpStream>() {\n        data.insert(ConnectionInfo {\n            bind: sock.local_addr().unwrap(),\n            peer: sock.peer_addr().unwrap(),\n            ttl: sock.ttl().ok(),\n        });\n    } else {\n        unreachable!(\"connection should only be plaintext since no TLS is set up\");\n    }\n}\n\n#[actix_web::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    let bind = (\"127.0.0.1\", 8080);\n    log::info!(\"staring server at http://{}:{}\", &bind.0, &bind.1);\n\n    HttpServer::new(|| App::new().default_service(web::to(route_whoami)))\n        .on_connect(get_conn_info)\n        .bind_auto_h2c(bind)?\n        .workers(2)\n        .run()\n        .await\n}\n"
  },
  {
    "path": "actix-web/examples/uds.rs",
    "content": "use actix_web::{get, web, HttpRequest};\n#[cfg(unix)]\nuse actix_web::{middleware, App, Error, HttpResponse, HttpServer};\n\n#[get(\"/resource1/{name}/index.html\")]\nasync fn index(req: HttpRequest, name: web::Path<String>) -> String {\n    println!(\"REQ: {:?}\", req);\n    format!(\"Hello: {}!\\r\\n\", name)\n}\n\n#[cfg(unix)]\nasync fn index_async(req: HttpRequest) -> Result<&'static str, Error> {\n    println!(\"REQ: {:?}\", req);\n    Ok(\"Hello world!\\r\\n\")\n}\n\n#[get(\"/\")]\nasync fn no_params() -> &'static str {\n    \"Hello world!\\r\\n\"\n}\n\n#[cfg(unix)]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    HttpServer::new(|| {\n        App::new()\n            .wrap(middleware::DefaultHeaders::new().add((\"X-Version\", \"0.2\")))\n            .wrap(middleware::Compress::default())\n            .wrap(middleware::Logger::default())\n            .service(index)\n            .service(no_params)\n            .service(\n                web::resource(\"/resource2/index.html\")\n                    .wrap(middleware::DefaultHeaders::new().add((\"X-Version-R2\", \"0.3\")))\n                    .default_service(web::route().to(HttpResponse::MethodNotAllowed))\n                    .route(web::get().to(index_async)),\n            )\n            .service(web::resource(\"/test1.html\").to(|| async { \"Test\\r\\n\" }))\n    })\n    .bind_uds(\"/Users/me/uds-test\")?\n    .workers(1)\n    .run()\n    .await\n}\n\n#[cfg(not(unix))]\nfn main() {}\n"
  },
  {
    "path": "actix-web/examples/worker-cpu-pin.rs",
    "content": "use std::{\n    io,\n    sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    },\n    thread,\n};\n\nuse actix_web::{middleware, web, App, HttpServer};\n\nasync fn hello() -> &'static str {\n    \"Hello world!\"\n}\n\n#[actix_web::main]\nasync fn main() -> io::Result<()> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    let core_ids = core_affinity::get_core_ids().unwrap();\n    let n_core_ids = core_ids.len();\n    let next_core_id = Arc::new(AtomicUsize::new(0));\n\n    HttpServer::new(move || {\n        let pin = Arc::clone(&next_core_id).fetch_add(1, Ordering::AcqRel);\n        log::info!(\n            \"setting CPU affinity for worker {}: pinning to core {}\",\n            thread::current().name().unwrap(),\n            pin,\n        );\n        core_affinity::set_for_current(core_ids[pin]);\n\n        App::new()\n            .wrap(middleware::Logger::default())\n            .service(web::resource(\"/\").get(hello))\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .workers(n_core_ids)\n    .run()\n    .await\n}\n"
  },
  {
    "path": "actix-web/src/app.rs",
    "content": "use std::{cell::RefCell, fmt, future::Future, rc::Rc};\n\nuse actix_http::{body::MessageBody, Extensions, Request};\nuse actix_service::{\n    apply, apply_fn_factory, boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt,\n    Transform,\n};\nuse futures_util::FutureExt as _;\n\nuse crate::{\n    app_service::{AppEntry, AppInit, AppRoutingFactory},\n    config::ServiceConfig,\n    data::{Data, DataFactory, FnDataFactory},\n    dev::ResourceDef,\n    error::Error,\n    resource::Resource,\n    route::Route,\n    service::{\n        AppServiceFactory, BoxedHttpServiceFactory, HttpServiceFactory, ServiceFactoryWrapper,\n        ServiceRequest, ServiceResponse,\n    },\n};\n\n/// The top-level builder for an Actix Web application.\npub struct App<T> {\n    endpoint: T,\n    services: Vec<Box<dyn AppServiceFactory>>,\n    default: Option<Rc<BoxedHttpServiceFactory>>,\n    factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,\n    data_factories: Vec<FnDataFactory>,\n    external: Vec<ResourceDef>,\n    extensions: Extensions,\n    #[cfg(feature = \"experimental-introspection\")]\n    introspector: Rc<RefCell<crate::introspection::IntrospectionCollector>>,\n}\n\nimpl App<AppEntry> {\n    /// Create application builder. Application can be configured with a builder-like pattern.\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        let factory_ref = Rc::new(RefCell::new(None));\n\n        App {\n            endpoint: AppEntry::new(Rc::clone(&factory_ref)),\n            data_factories: Vec::new(),\n            services: Vec::new(),\n            default: None,\n            factory_ref,\n            external: Vec::new(),\n            extensions: Extensions::new(),\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: Rc::new(RefCell::new(\n                crate::introspection::IntrospectionCollector::new(),\n            )),\n        }\n    }\n}\n\nimpl<T> App<T>\nwhere\n    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,\n{\n    /// Set application (root level) data.\n    ///\n    /// Application data stored with `App::app_data()` method is available through the\n    /// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime.\n    ///\n    /// # [`Data<T>`]\n    /// Any [`Data<T>`] type added here can utilize its extractor implementation in handlers.\n    /// Types not wrapped in `Data<T>` cannot use this extractor. See [its docs](Data<T>) for more\n    /// about its usage and patterns.\n    ///\n    /// ```\n    /// use std::cell::Cell;\n    /// use actix_web::{web, App, HttpRequest, HttpResponse, Responder};\n    ///\n    /// struct MyData {\n    ///     count: std::cell::Cell<usize>,\n    /// }\n    ///\n    /// async fn handler(req: HttpRequest, counter: web::Data<MyData>) -> impl Responder {\n    ///     // note this cannot use the Data<T> extractor because it was not added with it\n    ///     let incr = *req.app_data::<usize>().unwrap();\n    ///     assert_eq!(incr, 3);\n    ///\n    ///     // update counter using other value from app data\n    ///     counter.count.set(counter.count.get() + incr);\n    ///\n    ///     HttpResponse::Ok().body(counter.count.get().to_string())\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/\")\n    ///         .app_data(3usize)\n    ///         .app_data(web::Data::new(MyData { count: Default::default() }))\n    ///         .route(web::get().to(handler))\n    /// );\n    /// ```\n    ///\n    /// # Shared Mutable State\n    /// [`HttpServer::new`](crate::HttpServer::new) accepts an application factory rather than an\n    /// application instance; the factory closure is called on each worker thread independently.\n    /// Therefore, if you want to share a data object between different workers, a shareable object\n    /// needs to be created first, outside the `HttpServer::new` closure and cloned into it.\n    /// [`Data<T>`] is an example of such a sharable object.\n    ///\n    /// ```ignore\n    /// let counter = web::Data::new(AppStateWithCounter {\n    ///     counter: Mutex::new(0),\n    /// });\n    ///\n    /// HttpServer::new(move || {\n    ///     // move counter object into the closure and clone for each worker\n    ///\n    ///     App::new()\n    ///         .app_data(counter.clone())\n    ///         .route(\"/\", web::get().to(handler))\n    /// })\n    /// ```\n    #[doc(alias = \"manage\")]\n    pub fn app_data<U: 'static>(mut self, data: U) -> Self {\n        self.extensions.insert(data);\n        self\n    }\n\n    /// Add application (root) data after wrapping in `Data<T>`.\n    ///\n    /// Deprecated in favor of [`app_data`](Self::app_data).\n    #[deprecated(since = \"4.0.0\", note = \"Use `.app_data(Data::new(val))` instead.\")]\n    pub fn data<U: 'static>(self, data: U) -> Self {\n        self.app_data(Data::new(data))\n    }\n\n    /// Add application data factory that resolves asynchronously.\n    ///\n    /// Data items are constructed during application initialization, before the server starts\n    /// accepting requests.\n    ///\n    /// The returned data value `D` is wrapped as [`Data<D>`].\n    pub fn data_factory<F, Out, D, E>(mut self, data: F) -> Self\n    where\n        F: Fn() -> Out + 'static,\n        Out: Future<Output = Result<D, E>> + 'static,\n        D: 'static,\n        E: std::fmt::Debug,\n    {\n        self.data_factories.push(Box::new(move || {\n            {\n                let fut = data();\n                async move {\n                    match fut.await {\n                        Err(err) => {\n                            log::error!(\"Can not construct data instance: {err:?}\");\n                            Err(())\n                        }\n                        Ok(data) => {\n                            let data: Box<dyn DataFactory> = Box::new(Data::new(data));\n                            Ok(data)\n                        }\n                    }\n                }\n            }\n            .boxed_local()\n        }));\n\n        self\n    }\n\n    /// Run external configuration as part of the application building\n    /// process\n    ///\n    /// This function is useful for moving parts of configuration to a\n    /// different module or even library. For example,\n    /// some of the resource's configuration could be moved to different module.\n    ///\n    /// ```\n    /// use actix_web::{web, App, HttpResponse};\n    ///\n    /// // this function could be located in different module\n    /// fn config(cfg: &mut web::ServiceConfig) {\n    ///     cfg.service(web::resource(\"/test\")\n    ///         .route(web::get().to(|| HttpResponse::Ok()))\n    ///         .route(web::head().to(|| HttpResponse::MethodNotAllowed()))\n    ///     );\n    /// }\n    ///\n    /// App::new()\n    ///     .configure(config)  // <- register resources\n    ///     .route(\"/index.html\", web::get().to(|| HttpResponse::Ok()));\n    /// ```\n    pub fn configure<F>(mut self, f: F) -> Self\n    where\n        F: FnOnce(&mut ServiceConfig),\n    {\n        let mut cfg = ServiceConfig::new();\n\n        f(&mut cfg);\n\n        self.services.extend(cfg.services);\n        self.external.extend(cfg.external);\n        self.extensions.extend(cfg.app_data);\n\n        if let Some(default) = cfg.default {\n            self.default = Some(default);\n        }\n\n        self\n    }\n\n    /// Configure route for a specific path.\n    ///\n    /// This is a simplified version of the `App::service()` method.\n    /// This method can be used multiple times with same path, in that case\n    /// multiple resources with one route would be registered for same resource path.\n    ///\n    /// ```\n    /// use actix_web::{web, App, HttpResponse};\n    ///\n    /// async fn index(data: web::Path<(String, String)>) -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .route(\"/test1\", web::get().to(index))\n    ///     .route(\"/test2\", web::post().to(|| HttpResponse::MethodNotAllowed()));\n    /// ```\n    pub fn route(self, path: &str, mut route: Route) -> Self {\n        self.service(\n            Resource::new(path)\n                .add_guards(route.take_guards())\n                .route(route),\n        )\n    }\n\n    /// Register HTTP service.\n    ///\n    /// Http service is any type that implements `HttpServiceFactory` trait.\n    ///\n    /// Actix Web provides several services implementations:\n    ///\n    /// * *Resource* is an entry in resource table which corresponds to requested URL.\n    /// * *Scope* is a set of resources with common root path.\n    pub fn service<F>(mut self, factory: F) -> Self\n    where\n        F: HttpServiceFactory + 'static,\n    {\n        self.services\n            .push(Box::new(ServiceFactoryWrapper::new(factory)));\n        self\n    }\n\n    /// Default service that is invoked when no matching resource could be found.\n    ///\n    /// You can use a [`Route`] as default service.\n    ///\n    /// If a default service is not registered, an empty `404 Not Found` response will be sent to\n    /// the client instead.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{web, App, HttpResponse};\n    ///\n    /// async fn index() -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(web::resource(\"/index.html\").route(web::get().to(index)))\n    ///     .default_service(web::to(|| HttpResponse::NotFound()));\n    /// ```\n    pub fn default_service<F, U>(mut self, svc: F) -> Self\n    where\n        F: IntoServiceFactory<U, ServiceRequest>,\n        U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>\n            + 'static,\n        U::InitError: fmt::Debug,\n    {\n        let svc = svc.into_factory().map_init_err(|err| {\n            log::error!(\"Can not construct default service: {err:?}\");\n        });\n\n        self.default = Some(Rc::new(boxed::factory(svc)));\n\n        self\n    }\n\n    /// Register an external resource.\n    ///\n    /// External resources are useful for URL generation purposes only\n    /// and are never considered for matching at request time. Calls to\n    /// `HttpRequest::url_for()` will work as expected.\n    ///\n    /// ```\n    /// use actix_web::{web, App, HttpRequest, HttpResponse, Result};\n    ///\n    /// async fn index(req: HttpRequest) -> Result<HttpResponse> {\n    ///     let url = req.url_for(\"youtube\", &[\"asdlkjqme\"])?;\n    ///     assert_eq!(url.as_str(), \"https://youtube.com/watch/asdlkjqme\");\n    ///     Ok(HttpResponse::Ok().into())\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(web::resource(\"/index.html\").route(\n    ///         web::get().to(index)))\n    ///     .external_resource(\"youtube\", \"https://youtube.com/watch/{video_id}\");\n    /// ```\n    pub fn external_resource<N, U>(mut self, name: N, url: U) -> Self\n    where\n        N: AsRef<str>,\n        U: AsRef<str>,\n    {\n        let mut rdef = ResourceDef::new(url.as_ref());\n        rdef.set_name(name.as_ref());\n        self.external.push(rdef);\n        self\n    }\n\n    /// Registers an app-wide middleware.\n    ///\n    /// Registers middleware, in the form of a middleware component (type), that runs during\n    /// inbound and/or outbound processing in the request life-cycle (request -> response),\n    /// modifying request/response as necessary, across all requests managed by the `App`.\n    ///\n    /// Use middleware when you need to read or modify *every* request or response in some way.\n    ///\n    /// Middleware can be applied similarly to individual `Scope`s and `Resource`s.\n    /// See [`Scope::wrap`](crate::Scope::wrap) and [`Resource::wrap`].\n    ///\n    /// For more info on middleware take a look at the [`middleware` module][crate::middleware].\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{middleware, web, App};\n    ///\n    /// async fn index() -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .wrap(middleware::Logger::default())\n    ///     .route(\"/index.html\", web::get().to(index));\n    /// ```\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap<M, B>(\n        self,\n        mw: M,\n    ) -> App<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        M: Transform<\n                T::Service,\n                ServiceRequest,\n                Response = ServiceResponse<B>,\n                Error = Error,\n                InitError = (),\n            > + 'static,\n        B: MessageBody,\n    {\n        App {\n            endpoint: apply(mw, self.endpoint),\n            data_factories: self.data_factories,\n            services: self.services,\n            default: self.default,\n            factory_ref: self.factory_ref,\n            external: self.external,\n            extensions: self.extensions,\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: self.introspector,\n        }\n    }\n\n    /// Registers an app-wide function middleware.\n    ///\n    /// `mw` is a closure that runs during inbound and/or outbound processing in the request\n    /// life-cycle (request -> response), modifying request/response as necessary, across all\n    /// requests handled by the `App`.\n    ///\n    /// Use middleware when you need to read or modify *every* request or response in some way.\n    ///\n    /// Middleware can also be applied to individual `Scope`s and `Resource`s.\n    ///\n    /// See [`App::wrap`] for details on how middlewares compose with each other.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{dev::Service as _, middleware, web, App};\n    /// use actix_web::http::header::{CONTENT_TYPE, HeaderValue};\n    ///\n    /// async fn index() -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .wrap_fn(|req, srv| {\n    ///         let fut = srv.call(req);\n    ///         async {\n    ///             let mut res = fut.await?;\n    ///             res.headers_mut()\n    ///                 .insert(CONTENT_TYPE, HeaderValue::from_static(\"text/plain\"));\n    ///             Ok(res)\n    ///         }\n    ///     })\n    ///     .route(\"/index.html\", web::get().to(index));\n    /// ```\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap_fn<F, R, B>(\n        self,\n        mw: F,\n    ) -> App<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,\n        R: Future<Output = Result<ServiceResponse<B>, Error>>,\n        B: MessageBody,\n    {\n        App {\n            endpoint: apply_fn_factory(self.endpoint, mw),\n            data_factories: self.data_factories,\n            services: self.services,\n            default: self.default,\n            factory_ref: self.factory_ref,\n            external: self.external,\n            extensions: self.extensions,\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: self.introspector,\n        }\n    }\n}\n\nimpl<T, B> IntoServiceFactory<AppInit<T, B>, Request> for App<T>\nwhere\n    T: ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        > + 'static,\n    B: MessageBody,\n{\n    fn into_factory(self) -> AppInit<T, B> {\n        AppInit {\n            async_data_factories: self.data_factories.into_boxed_slice().into(),\n            endpoint: self.endpoint,\n            services: Rc::new(RefCell::new(self.services)),\n            external: RefCell::new(self.external),\n            default: self.default,\n            factory_ref: self.factory_ref,\n            extensions: RefCell::new(Some(self.extensions)),\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: Rc::clone(&self.introspector),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::Service as _;\n    use actix_utils::future::{err, ok};\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::{\n        http::{\n            header::{self, HeaderValue},\n            Method, StatusCode,\n        },\n        middleware::DefaultHeaders,\n        test::{call_service, init_service, read_body, try_init_service, TestRequest},\n        web, HttpRequest, HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn test_default_resource() {\n        let srv =\n            init_service(App::new().service(web::resource(\"/test\").to(HttpResponse::Ok))).await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/blah\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let srv = init_service(\n            App::new()\n                .service(web::resource(\"/test\").to(HttpResponse::Ok))\n                .service(\n                    web::resource(\"/test2\")\n                        .default_service(|r: ServiceRequest| {\n                            ok(r.into_response(HttpResponse::Created()))\n                        })\n                        .route(web::get().to(HttpResponse::Ok)),\n                )\n                .default_service(|r: ServiceRequest| {\n                    ok(r.into_response(HttpResponse::MethodNotAllowed()))\n                }),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/blah\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n\n        let req = TestRequest::with_uri(\"/test2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test2\")\n            .method(Method::POST)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::CREATED);\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data_factory() {\n        let srv = init_service(\n            App::new()\n                .data_factory(|| ok::<_, ()>(10usize))\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let srv = init_service(\n            App::new()\n                .data_factory(|| ok::<_, ()>(10u32))\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data_factory_errors() {\n        let srv = try_init_service(\n            App::new()\n                .data_factory(|| err::<u32, _>(()))\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n\n        assert!(srv.is_err());\n    }\n\n    #[actix_rt::test]\n    async fn test_extension() {\n        let srv = init_service(App::new().app_data(10usize).service(web::resource(\"/\").to(\n            |req: HttpRequest| {\n                assert_eq!(*req.app_data::<usize>().unwrap(), 10);\n                HttpResponse::Ok()\n            },\n        )))\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_wrap() {\n        let srv = init_service(\n            App::new()\n                .wrap(\n                    DefaultHeaders::new()\n                        .add((header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"))),\n                )\n                .route(\"/test\", web::get().to(HttpResponse::Ok)),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_router_wrap() {\n        let srv = init_service(\n            App::new()\n                .route(\"/test\", web::get().to(HttpResponse::Ok))\n                .wrap(\n                    DefaultHeaders::new()\n                        .add((header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"))),\n                ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_wrap_fn() {\n        let srv = init_service(\n            App::new()\n                .wrap_fn(|req, srv| {\n                    let fut = srv.call(req);\n                    async move {\n                        let mut res = fut.await?;\n                        res.headers_mut()\n                            .insert(header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n                        Ok(res)\n                    }\n                })\n                .service(web::resource(\"/test\").to(HttpResponse::Ok)),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_router_wrap_fn() {\n        let srv = init_service(\n            App::new()\n                .route(\"/test\", web::get().to(HttpResponse::Ok))\n                .wrap_fn(|req, srv| {\n                    let fut = srv.call(req);\n                    async {\n                        let mut res = fut.await?;\n                        res.headers_mut()\n                            .insert(header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n                        Ok(res)\n                    }\n                }),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_external_resource() {\n        let srv = init_service(\n            App::new()\n                .external_resource(\"youtube\", \"https://youtube.com/watch/{video_id}\")\n                .route(\n                    \"/test\",\n                    web::get().to(|req: HttpRequest| {\n                        HttpResponse::Ok()\n                            .body(req.url_for(\"youtube\", [\"12345\"]).unwrap().to_string())\n                    }),\n                ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"https://youtube.com/watch/12345\"));\n    }\n\n    #[test]\n    fn can_be_returned_from_fn() {\n        /// compile-only test for returning app type from function\n        pub fn my_app() -> App<\n            impl ServiceFactory<\n                ServiceRequest,\n                Response = ServiceResponse<impl MessageBody>,\n                Config = (),\n                InitError = (),\n                Error = Error,\n            >,\n        > {\n            App::new()\n                // logger can be removed without affecting the return type\n                .wrap(crate::middleware::Logger::default())\n                .route(\"/\", web::to(|| async { \"hello\" }))\n        }\n\n        #[allow(clippy::let_underscore_future)]\n        let _ = init_service(my_app());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/app_service.rs",
    "content": "use std::{cell::RefCell, mem, rc::Rc};\n\nuse actix_http::Request;\nuse actix_router::{Path, ResourceDef, Router, Url};\nuse actix_service::{boxed, fn_service, Service, ServiceFactory};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::future::join_all;\n\nuse crate::{\n    body::BoxBody,\n    config::{AppConfig, AppService},\n    data::FnDataFactory,\n    dev::Extensions,\n    guard::Guard,\n    request::{HttpRequest, HttpRequestPool},\n    rmap::ResourceMap,\n    service::{\n        AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, ServiceRequest,\n        ServiceResponse,\n    },\n    Error, HttpResponse,\n};\n\n/// Service factory to convert [`Request`] to a [`ServiceRequest<S>`].\n///\n/// It also executes data factories.\npub struct AppInit<T, B>\nwhere\n    T: ServiceFactory<\n        ServiceRequest,\n        Config = (),\n        Response = ServiceResponse<B>,\n        Error = Error,\n        InitError = (),\n    >,\n{\n    pub(crate) endpoint: T,\n    pub(crate) extensions: RefCell<Option<Extensions>>,\n    pub(crate) async_data_factories: Rc<[FnDataFactory]>,\n    pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,\n    pub(crate) default: Option<Rc<BoxedHttpServiceFactory>>,\n    pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,\n    pub(crate) external: RefCell<Vec<ResourceDef>>,\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) introspector: Rc<RefCell<crate::introspection::IntrospectionCollector>>,\n}\n\nimpl<T, B> ServiceFactory<Request> for AppInit<T, B>\nwhere\n    T: ServiceFactory<\n        ServiceRequest,\n        Config = (),\n        Response = ServiceResponse<B>,\n        Error = Error,\n        InitError = (),\n    >,\n    T::Future: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = T::Error;\n    type Config = AppConfig;\n    type Service = AppInitService<T::Service, B>;\n    type InitError = T::InitError;\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, config: AppConfig) -> Self::Future {\n        // set AppService's default service to 404 NotFound\n        // if no user defined default service exists.\n        let default = self.default.clone().unwrap_or_else(|| {\n            Rc::new(boxed::factory(fn_service(|req: ServiceRequest| async {\n                Ok(req.into_response(HttpResponse::NotFound()))\n            })))\n        });\n\n        // create App config to pass to child services\n        let mut config = AppService::new(config, Rc::clone(&default));\n        #[cfg(feature = \"experimental-introspection\")]\n        {\n            config.introspector = Rc::clone(&self.introspector);\n        }\n\n        // register services\n        mem::take(&mut *self.services.borrow_mut())\n            .into_iter()\n            .for_each(|mut srv| srv.register(&mut config));\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        #[cfg(feature = \"experimental-introspection\")]\n        let (config, services, _) = config.into_services();\n        #[cfg(not(feature = \"experimental-introspection\"))]\n        let (config, services) = config.into_services();\n\n        // complete pipeline creation.\n        *self.factory_ref.borrow_mut() = Some(AppRoutingFactory {\n            default,\n            services: services\n                .into_iter()\n                .map(|(mut rdef, srv, guards, nested)| {\n                    rmap.add(&mut rdef, nested);\n                    (rdef, srv, RefCell::new(guards))\n                })\n                .collect::<Vec<_>>()\n                .into_boxed_slice()\n                .into(),\n        });\n\n        // external resources\n        for mut rdef in mem::take(&mut *self.external.borrow_mut()) {\n            #[cfg(feature = \"experimental-introspection\")]\n            {\n                self.introspector.borrow_mut().register_external(&rdef, \"/\");\n            }\n            rmap.add(&mut rdef, None);\n        }\n\n        // complete ResourceMap tree creation\n        let rmap = Rc::new(rmap);\n        ResourceMap::finish(&rmap);\n\n        // construct all async data factory futures\n        let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));\n\n        // construct app service and middleware service factory future.\n        let endpoint_fut = self.endpoint.new_service(());\n        #[cfg(feature = \"experimental-introspection\")]\n        let introspector = Rc::clone(&self.introspector);\n\n        // take extensions or create new one as app data container.\n        let mut app_data = self.extensions.borrow_mut().take().unwrap_or_default();\n\n        Box::pin(async move {\n            // async data factories\n            let async_data_factories = factory_futs\n                .await\n                .into_iter()\n                .collect::<Result<Vec<_>, _>>()\n                .map_err(|_| ())?;\n\n            // app service and middleware\n            let service = endpoint_fut.await?;\n\n            // populate app data container from (async) data factories.\n            for factory in &async_data_factories {\n                factory.create(&mut app_data);\n            }\n\n            #[cfg(feature = \"experimental-introspection\")]\n            {\n                let tree = introspector.borrow_mut().finalize();\n                app_data.insert(crate::web::Data::new(tree));\n            }\n\n            Ok(AppInitService {\n                service,\n                app_data: Rc::new(app_data),\n                app_state: AppInitServiceState::new(rmap, config),\n            })\n        })\n    }\n}\n\n/// The [`Service`] that is passed to `actix-http`'s server builder.\n///\n/// Wraps a service receiving a [`ServiceRequest`] into one receiving a [`Request`].\npub struct AppInitService<T, B>\nwhere\n    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    service: T,\n    app_data: Rc<Extensions>,\n    app_state: Rc<AppInitServiceState>,\n}\n\n/// A collection of state for [`AppInitService`] that is shared across [`HttpRequest`]s.\npub(crate) struct AppInitServiceState {\n    rmap: Rc<ResourceMap>,\n    config: AppConfig,\n    pool: HttpRequestPool,\n}\n\nimpl AppInitServiceState {\n    /// Constructs state collection from resource map and app config.\n    pub(crate) fn new(rmap: Rc<ResourceMap>, config: AppConfig) -> Rc<Self> {\n        Rc::new(AppInitServiceState {\n            rmap,\n            config,\n            pool: HttpRequestPool::default(),\n        })\n    }\n\n    /// Returns a reference to the application's resource map.\n    #[inline]\n    pub(crate) fn rmap(&self) -> &ResourceMap {\n        &self.rmap\n    }\n\n    /// Returns a reference to the application's configuration.\n    #[inline]\n    pub(crate) fn config(&self) -> &AppConfig {\n        &self.config\n    }\n\n    /// Returns a reference to the application's request pool.\n    #[inline]\n    pub(crate) fn pool(&self) -> &HttpRequestPool {\n        &self.pool\n    }\n}\n\nimpl<T, B> Service<Request> for AppInitService<T, B>\nwhere\n    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    type Response = ServiceResponse<B>;\n    type Error = T::Error;\n    type Future = T::Future;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, mut req: Request) -> Self::Future {\n        let extensions = Rc::new(RefCell::new(req.take_req_data()));\n        let conn_data = req.take_conn_data();\n        let (head, payload) = req.into_parts();\n\n        let req = match self.app_state.pool().pop() {\n            Some(mut req) => {\n                let inner = Rc::get_mut(&mut req.inner).unwrap();\n                inner.path.get_mut().update(&head.uri);\n                inner.path.reset();\n                inner.resource_path.clear();\n                inner.resource_path_matched = false;\n                inner.head = head;\n                inner.conn_data = conn_data;\n                inner.extensions = extensions;\n                req\n            }\n\n            None => HttpRequest::new(\n                Path::new(Url::new(head.uri.clone())),\n                head,\n                Rc::clone(&self.app_state),\n                Rc::clone(&self.app_data),\n                conn_data,\n                extensions,\n            ),\n        };\n\n        self.service.call(ServiceRequest::new(req, payload))\n    }\n}\n\nimpl<T, B> Drop for AppInitService<T, B>\nwhere\n    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    fn drop(&mut self) {\n        self.app_state.pool().clear();\n    }\n}\n\npub struct AppRoutingFactory {\n    #[allow(clippy::type_complexity)]\n    services: Rc<\n        [(\n            ResourceDef,\n            BoxedHttpServiceFactory,\n            RefCell<Option<Vec<Box<dyn Guard>>>>,\n        )],\n    >,\n    default: Rc<BoxedHttpServiceFactory>,\n}\n\nimpl ServiceFactory<ServiceRequest> for AppRoutingFactory {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = AppRouting;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        // construct all services factory future with its resource def and guards.\n        let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {\n            let path = path.clone();\n            let guards = guards.borrow_mut().take().unwrap_or_default();\n            let factory_fut = factory.new_service(());\n            async move {\n                factory_fut\n                    .await\n                    .map(move |service| (path, guards, service))\n            }\n        }));\n\n        // construct default service factory future\n        let default_fut = self.default.new_service(());\n\n        Box::pin(async move {\n            let default = default_fut.await?;\n\n            // build router from the factory future result.\n            let router = factory_fut\n                .await\n                .into_iter()\n                .collect::<Result<Vec<_>, _>>()?\n                .drain(..)\n                .fold(Router::build(), |mut router, (path, guards, service)| {\n                    router.push(path, service, guards);\n                    router\n                })\n                .finish();\n\n            Ok(AppRouting { router, default })\n        })\n    }\n}\n\n/// The Actix Web router default entry point.\npub struct AppRouting {\n    router: Router<BoxedHttpService, Vec<Box<dyn Guard>>>,\n    default: BoxedHttpService,\n}\n\nimpl Service<ServiceRequest> for AppRouting {\n    type Response = ServiceResponse<BoxBody>;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, mut req: ServiceRequest) -> Self::Future {\n        let res = self.router.recognize_fn(&mut req, |req, guards| {\n            let guard_ctx = req.guard_ctx();\n            guards.iter().all(|guard| guard.check(&guard_ctx))\n        });\n\n        if let Some((srv, info)) = res {\n            req.push_resource_id(info.0);\n\n            let matched = req\n                .resource_map()\n                .is_resource_path_match(req.resource_id_path());\n\n            req.mark_resource_path(matched);\n\n            srv.call(req)\n        } else {\n            self.default.call(req)\n        }\n    }\n}\n\n/// Wrapper service for routing\npub struct AppEntry {\n    factory: Rc<RefCell<Option<AppRoutingFactory>>>,\n}\n\nimpl AppEntry {\n    pub fn new(factory: Rc<RefCell<Option<AppRoutingFactory>>>) -> Self {\n        AppEntry { factory }\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for AppEntry {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = AppRouting;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        self.factory.borrow_mut().as_mut().unwrap().new_service(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc,\n    };\n\n    use actix_service::Service;\n\n    use crate::{\n        test::{init_service, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    struct DropData(Arc<AtomicBool>);\n\n    impl Drop for DropData {\n        fn drop(&mut self) {\n            self.0.store(true, Ordering::Relaxed);\n        }\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_drop_data() {\n        let data = Arc::new(AtomicBool::new(false));\n\n        {\n            let app = init_service(\n                App::new()\n                    .data(DropData(data.clone()))\n                    .service(web::resource(\"/test\").to(HttpResponse::Ok)),\n            )\n            .await;\n            let req = TestRequest::with_uri(\"/test\").to_request();\n            let _ = app.call(req).await.unwrap();\n        }\n        assert!(data.load(Ordering::Relaxed));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/config.rs",
    "content": "use std::{net::SocketAddr, rc::Rc};\n\nuse actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};\n\nuse crate::{\n    data::Data,\n    dev::{Extensions, ResourceDef},\n    error::Error,\n    guard::Guard,\n    resource::Resource,\n    rmap::ResourceMap,\n    route::Route,\n    service::{\n        AppServiceFactory, BoxedHttpServiceFactory, HttpServiceFactory, ServiceFactoryWrapper,\n        ServiceRequest, ServiceResponse,\n    },\n};\n\ntype Guards = Vec<Box<dyn Guard>>;\n\n/// Application configuration\npub struct AppService {\n    config: AppConfig,\n    root: bool,\n    default: Rc<BoxedHttpServiceFactory>,\n    #[allow(clippy::type_complexity)]\n    services: Vec<(\n        ResourceDef,\n        BoxedHttpServiceFactory,\n        Option<Guards>,\n        Option<Rc<ResourceMap>>,\n    )>,\n    #[cfg(feature = \"experimental-introspection\")]\n    pub current_prefix: String,\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) introspector:\n        std::rc::Rc<std::cell::RefCell<crate::introspection::IntrospectionCollector>>,\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) scope_id_stack: Vec<usize>,\n    #[cfg(feature = \"experimental-introspection\")]\n    pending_scope_id: Option<usize>,\n}\n\nimpl AppService {\n    /// Crate server settings instance.\n    pub(crate) fn new(config: AppConfig, default: Rc<BoxedHttpServiceFactory>) -> Self {\n        AppService {\n            config,\n            default,\n            root: true,\n            services: Vec::new(),\n            #[cfg(feature = \"experimental-introspection\")]\n            current_prefix: \"\".to_string(),\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: std::rc::Rc::new(std::cell::RefCell::new(\n                crate::introspection::IntrospectionCollector::new(),\n            )),\n            #[cfg(feature = \"experimental-introspection\")]\n            scope_id_stack: Vec::new(),\n            #[cfg(feature = \"experimental-introspection\")]\n            pending_scope_id: None,\n        }\n    }\n\n    /// Check if root is being configured\n    pub fn is_root(&self) -> bool {\n        self.root\n    }\n\n    #[allow(clippy::type_complexity)]\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) fn into_services(\n        self,\n    ) -> (\n        AppConfig,\n        Vec<(\n            ResourceDef,\n            BoxedHttpServiceFactory,\n            Option<Guards>,\n            Option<Rc<ResourceMap>>,\n        )>,\n        std::rc::Rc<std::cell::RefCell<crate::introspection::IntrospectionCollector>>,\n    ) {\n        (self.config, self.services, self.introspector)\n    }\n\n    #[allow(clippy::type_complexity)]\n    #[cfg(not(feature = \"experimental-introspection\"))]\n    pub(crate) fn into_services(\n        self,\n    ) -> (\n        AppConfig,\n        Vec<(\n            ResourceDef,\n            BoxedHttpServiceFactory,\n            Option<Guards>,\n            Option<Rc<ResourceMap>>,\n        )>,\n    ) {\n        (self.config, self.services)\n    }\n\n    /// Clones inner config and default service, returning new `AppService` with empty service list\n    /// marked as non-root.\n    pub(crate) fn clone_config(&self) -> Self {\n        AppService {\n            config: self.config.clone(),\n            default: Rc::clone(&self.default),\n            services: Vec::new(),\n            root: false,\n            #[cfg(feature = \"experimental-introspection\")]\n            current_prefix: self.current_prefix.clone(),\n            #[cfg(feature = \"experimental-introspection\")]\n            introspector: std::rc::Rc::clone(&self.introspector),\n            #[cfg(feature = \"experimental-introspection\")]\n            scope_id_stack: self.scope_id_stack.clone(),\n            #[cfg(feature = \"experimental-introspection\")]\n            pending_scope_id: None,\n        }\n    }\n\n    /// Returns reference to configuration.\n    pub fn config(&self) -> &AppConfig {\n        &self.config\n    }\n\n    /// Returns default handler factory.\n    pub fn default_service(&self) -> Rc<BoxedHttpServiceFactory> {\n        Rc::clone(&self.default)\n    }\n\n    /// Register HTTP service.\n    pub fn register_service<F, S>(\n        &mut self,\n        rdef: ResourceDef,\n        guards: Option<Vec<Box<dyn Guard>>>,\n        factory: F,\n        nested: Option<Rc<ResourceMap>>,\n    ) where\n        F: IntoServiceFactory<S, ServiceRequest>,\n        S: ServiceFactory<\n                ServiceRequest,\n                Response = ServiceResponse,\n                Error = Error,\n                Config = (),\n                InitError = (),\n            > + 'static,\n    {\n        #[cfg(feature = \"experimental-introspection\")]\n        {\n            use std::borrow::Borrow;\n\n            // Extract methods and guards for introspection\n            let guard_list: &[Box<dyn Guard>] = guards.borrow().as_ref().map_or(&[], |v| &v[..]);\n            let methods = guard_list\n                .iter()\n                .flat_map(|g| g.details().unwrap_or_default())\n                .flat_map(|d| {\n                    if let crate::guard::GuardDetail::HttpMethods(v) = d {\n                        v.into_iter()\n                            .filter_map(|s| s.parse().ok())\n                            .collect::<Vec<_>>()\n                    } else {\n                        Vec::new()\n                    }\n                })\n                .collect::<Vec<_>>();\n            let guard_names = guard_list.iter().map(|g| g.name()).collect::<Vec<_>>();\n            let guard_details = crate::introspection::guard_reports_from_iter(guard_list.iter());\n\n            let is_resource = nested.is_none();\n            let full_paths = crate::introspection::expand_patterns(&self.current_prefix, &rdef);\n            let patterns = rdef\n                .pattern_iter()\n                .map(|pattern| pattern.to_string())\n                .collect::<Vec<_>>();\n            let resource_name = rdef.name().map(|name| name.to_string());\n            let is_prefix = rdef.is_prefix();\n            let scope_id = if nested.is_some() {\n                self.pending_scope_id.take()\n            } else {\n                None\n            };\n            let parent_scope_id = self.scope_id_stack.last().copied();\n\n            for full_path in full_paths {\n                let info = crate::introspection::RouteInfo::new(\n                    full_path,\n                    methods.clone(),\n                    guard_names.clone(),\n                    guard_details.clone(),\n                    patterns.clone(),\n                    resource_name.clone(),\n                );\n                self.introspector.borrow_mut().register_service(\n                    info,\n                    is_resource,\n                    is_prefix,\n                    scope_id,\n                    parent_scope_id,\n                );\n            }\n        }\n\n        self.services\n            .push((rdef, boxed::factory(factory.into_factory()), guards, nested));\n    }\n\n    /// Update the current path prefix.\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) fn update_prefix(&mut self, prefix: &str) {\n        let next = ResourceDef::root_prefix(prefix);\n\n        if self.current_prefix.is_empty() {\n            self.current_prefix = next.pattern().unwrap_or(\"\").to_string();\n            return;\n        }\n\n        let current = ResourceDef::root_prefix(&self.current_prefix);\n        let joined = current.join(&next);\n        self.current_prefix = joined.pattern().unwrap_or(\"\").to_string();\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) fn prepare_scope_id(&mut self) -> usize {\n        let scope_id = self.introspector.borrow_mut().next_scope_id();\n        self.pending_scope_id = Some(scope_id);\n        scope_id\n    }\n}\n\n/// Application connection config.\n#[derive(Debug, Clone)]\npub struct AppConfig {\n    secure: bool,\n    host: String,\n    addr: SocketAddr,\n}\n\nimpl AppConfig {\n    pub(crate) fn new(secure: bool, host: String, addr: SocketAddr) -> Self {\n        AppConfig { secure, host, addr }\n    }\n\n    /// Needed in actix-test crate. Semver exempt.\n    #[doc(hidden)]\n    pub fn __priv_test_new(secure: bool, host: String, addr: SocketAddr) -> Self {\n        AppConfig::new(secure, host, addr)\n    }\n\n    /// Server host name.\n    ///\n    /// Host name is used by application router as a hostname for URL generation.\n    /// Check [ConnectionInfo](super::dev::ConnectionInfo::host())\n    /// documentation for more information.\n    ///\n    /// By default host name is set to a \"localhost\" value.\n    pub fn host(&self) -> &str {\n        &self.host\n    }\n\n    /// Returns true if connection is secure (i.e., running over `https:`).\n    pub fn secure(&self) -> bool {\n        self.secure\n    }\n\n    /// Returns the socket address of the local half of this TCP connection.\n    pub fn local_addr(&self) -> SocketAddr {\n        self.addr\n    }\n\n    #[cfg(test)]\n    pub(crate) fn set_host(&mut self, host: &str) {\n        host.clone_into(&mut self.host);\n    }\n}\n\nimpl Default for AppConfig {\n    /// Returns the default AppConfig.\n    /// Note: The included socket address is \"127.0.0.1\".\n    ///\n    /// 127.0.0.1: non-routable meta address that denotes an unknown, invalid or non-applicable target.\n    /// If you need a service only accessed by itself, use a loopback address.\n    /// A loopback address for IPv4 is any loopback address that begins with \"127\".\n    /// Loopback addresses should be only used to test your application locally.\n    /// The default configuration provides a loopback address.\n    ///\n    /// 0.0.0.0: if configured to use this special address, the application will listen to any IP address configured on the machine.\n    fn default() -> Self {\n        AppConfig::new(\n            false,\n            \"localhost:8080\".to_owned(),\n            \"127.0.0.1:8080\".parse().unwrap(),\n        )\n    }\n}\n\n/// Enables parts of app configuration to be declared separately from the app itself. Helpful for\n/// modularizing large applications.\n///\n/// Merge a `ServiceConfig` into an app using [`App::configure`](crate::App::configure). Scope and\n/// resources services have similar methods.\n///\n/// ```\n/// use actix_web::{web, App, HttpResponse};\n///\n/// // this function could be located in different module\n/// fn config(cfg: &mut web::ServiceConfig) {\n///     cfg.service(web::resource(\"/test\")\n///         .route(web::get().to(|| HttpResponse::Ok()))\n///         .route(web::head().to(|| HttpResponse::MethodNotAllowed()))\n///     );\n/// }\n///\n/// // merge `/test` routes from config function to App\n/// App::new().configure(config);\n/// ```\npub struct ServiceConfig {\n    pub(crate) services: Vec<Box<dyn AppServiceFactory>>,\n    pub(crate) external: Vec<ResourceDef>,\n    pub(crate) app_data: Extensions,\n    pub(crate) default: Option<Rc<BoxedHttpServiceFactory>>,\n}\n\nimpl ServiceConfig {\n    pub(crate) fn new() -> Self {\n        Self {\n            services: Vec::new(),\n            external: Vec::new(),\n            app_data: Extensions::new(),\n            default: None,\n        }\n    }\n\n    /// Add shared app data item.\n    ///\n    /// Counterpart to [`App::data()`](crate::App::data).\n    #[deprecated(since = \"4.0.0\", note = \"Use `.app_data(Data::new(val))` instead.\")]\n    pub fn data<U: 'static>(&mut self, data: U) -> &mut Self {\n        self.app_data(Data::new(data));\n        self\n    }\n\n    /// Add arbitrary app data item.\n    ///\n    /// Counterpart to [`App::app_data()`](crate::App::app_data).\n    pub fn app_data<U: 'static>(&mut self, ext: U) -> &mut Self {\n        self.app_data.insert(ext);\n        self\n    }\n\n    /// Default service to be used if no matching resource could be found.\n    ///\n    /// Counterpart to [`App::default_service()`](crate::App::default_service).\n    pub fn default_service<F, U>(&mut self, f: F) -> &mut Self\n    where\n        F: IntoServiceFactory<U, ServiceRequest>,\n        U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>\n            + 'static,\n        U::InitError: std::fmt::Debug,\n    {\n        let svc = f\n            .into_factory()\n            .map_init_err(|err| log::error!(\"Can not construct default service: {:?}\", err));\n\n        self.default = Some(Rc::new(boxed::factory(svc)));\n\n        self\n    }\n\n    /// Run external configuration as part of the application building process\n    ///\n    /// Counterpart to [`App::configure()`](crate::App::configure) that allows for easy nesting.\n    pub fn configure<F>(&mut self, f: F) -> &mut Self\n    where\n        F: FnOnce(&mut ServiceConfig),\n    {\n        f(self);\n        self\n    }\n\n    /// Configure route for a specific path.\n    ///\n    /// Counterpart to [`App::route()`](crate::App::route).\n    pub fn route(&mut self, path: &str, mut route: Route) -> &mut Self {\n        self.service(\n            Resource::new(path)\n                .add_guards(route.take_guards())\n                .route(route),\n        )\n    }\n\n    /// Register HTTP service factory.\n    ///\n    /// Counterpart to [`App::service()`](crate::App::service).\n    pub fn service<F>(&mut self, factory: F) -> &mut Self\n    where\n        F: HttpServiceFactory + 'static,\n    {\n        self.services\n            .push(Box::new(ServiceFactoryWrapper::new(factory)));\n        self\n    }\n\n    /// Register an external resource.\n    ///\n    /// External resources are useful for URL generation purposes only and are never considered for\n    /// matching at request time. Calls to [`HttpRequest::url_for()`](crate::HttpRequest::url_for)\n    /// will work as expected.\n    ///\n    /// Counterpart to [`App::external_resource()`](crate::App::external_resource).\n    pub fn external_resource<N, U>(&mut self, name: N, url: U) -> &mut Self\n    where\n        N: AsRef<str>,\n        U: AsRef<str>,\n    {\n        let mut rdef = ResourceDef::new(url.as_ref());\n        rdef.set_name(name.as_ref());\n        self.external.push(rdef);\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::Service;\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::{\n        http::{Method, StatusCode},\n        test::{assert_body_eq, call_service, init_service, read_body, TestRequest},\n        web, App, HttpRequest, HttpResponse,\n    };\n\n    // allow deprecated `ServiceConfig::data`\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data() {\n        let cfg = |cfg: &mut ServiceConfig| {\n            cfg.data(10usize);\n            cfg.app_data(15u8);\n        };\n\n        let srv = init_service(App::new().configure(cfg).service(web::resource(\"/\").to(\n            |_: web::Data<usize>, req: HttpRequest| {\n                assert_eq!(*req.app_data::<u8>().unwrap(), 15u8);\n                HttpResponse::Ok()\n            },\n        )))\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_external_resource() {\n        let srv = init_service(\n            App::new()\n                .configure(|cfg| {\n                    cfg.external_resource(\"youtube\", \"https://youtube.com/watch/{video_id}\");\n                })\n                .route(\n                    \"/test\",\n                    web::get().to(|req: HttpRequest| {\n                        HttpResponse::Ok()\n                            .body(req.url_for(\"youtube\", [\"12345\"]).unwrap().to_string())\n                    }),\n                ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"https://youtube.com/watch/12345\"));\n    }\n\n    #[actix_rt::test]\n    async fn registers_default_service() {\n        let srv = init_service(\n            App::new()\n                .configure(|cfg| {\n                    cfg.default_service(\n                        web::get().to(|| HttpResponse::NotFound().body(\"four oh four\")),\n                    );\n                })\n                .service(web::scope(\"/scoped\").configure(|cfg| {\n                    cfg.default_service(\n                        web::get().to(|| HttpResponse::NotFound().body(\"scoped four oh four\")),\n                    );\n                })),\n        )\n        .await;\n\n        // app registers default service\n        let req = TestRequest::with_uri(\"/path/i/did/not-configure\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"four oh four\"));\n\n        // scope registers default service\n        let req = TestRequest::with_uri(\"/scoped/path/i/did/not-configure\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"scoped four oh four\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_service() {\n        let srv = init_service(App::new().configure(|cfg| {\n            cfg.service(web::resource(\"/test\").route(web::get().to(HttpResponse::Created)))\n                .route(\"/index.html\", web::get().to(HttpResponse::Ok));\n        }))\n        .await;\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::GET)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::CREATED);\n\n        let req = TestRequest::with_uri(\"/index.html\")\n            .method(Method::GET)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn nested_service_configure() {\n        fn cfg_root(cfg: &mut ServiceConfig) {\n            cfg.configure(cfg_sub);\n        }\n\n        fn cfg_sub(cfg: &mut ServiceConfig) {\n            cfg.route(\"/\", web::get().to(|| async { \"hello world\" }));\n        }\n\n        let srv = init_service(App::new().configure(cfg_root)).await;\n\n        let req = TestRequest::with_uri(\"/\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_body_eq!(res, b\"hello world\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/data.rs",
    "content": "use std::{any::type_name, ops::Deref, sync::Arc};\n\nuse actix_http::Extensions;\nuse actix_utils::future::{err, ok, Ready};\nuse futures_core::future::LocalBoxFuture;\nuse serde::{de, Serialize};\n\nuse crate::{dev::Payload, error, Error, FromRequest, HttpRequest};\n\n/// Data factory.\npub(crate) trait DataFactory {\n    /// Return true if modifications were made to extensions map.\n    fn create(&self, extensions: &mut Extensions) -> bool;\n}\n\npub(crate) type FnDataFactory =\n    Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>;\n\n/// Application data wrapper and extractor.\n///\n/// # Setting Data\n/// Data is set using the `app_data` methods on `App`, `Scope`, and `Resource`. If data is wrapped\n/// in this `Data` type for those calls, it can be used as an extractor.\n///\n/// Note that `Data` should be constructed _outside_ the `HttpServer::new` closure if shared,\n/// potentially mutable state is desired. `Data` is cheap to clone; internally, it uses an `Arc`.\n///\n/// See also [`App::app_data`](crate::App::app_data), [`Scope::app_data`](crate::Scope::app_data),\n/// and [`Resource::app_data`](crate::Resource::app_data).\n///\n/// # Extracting `Data`\n/// Since the Actix Web router layers application data, the returned object will reference the\n/// \"closest\" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`\n/// also stores a `u32`, and the delegated request handler falls within that `Scope`, then\n/// extracting a `web::Data<u32>` for that handler will return the `Scope`'s instance. However,\n/// using the same router set up and a request that does not get captured by the `Scope`,\n/// `web::<Data<u32>>` would return the `App`'s instance.\n///\n/// If route data is not set for a handler, using `Data<T>` extractor would cause a `500 Internal\n/// Server Error` response.\n///\n/// See also [`HttpRequest::app_data`]\n/// and [`ServiceRequest::app_data`](crate::dev::ServiceRequest::app_data).\n///\n/// # Unsized Data\n/// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first\n/// constructing an `Arc<dyn T>` and using the `From` implementation to convert it.\n///\n/// ```\n/// # use std::{fmt::Display, sync::Arc};\n/// # use actix_web::web::Data;\n/// let displayable_arc: Arc<dyn Display> = Arc::new(42usize);\n/// let displayable_data: Data<dyn Display> = Data::from(displayable_arc);\n/// ```\n///\n/// # Examples\n/// ```\n/// use std::sync::Mutex;\n/// use actix_web::{App, HttpRequest, HttpResponse, Responder, web::{self, Data}};\n///\n/// struct MyData {\n///     counter: usize,\n/// }\n///\n/// /// Use the `Data<T>` extractor to access data in a handler.\n/// async fn index(data: Data<Mutex<MyData>>) -> impl Responder {\n///     let mut my_data = data.lock().unwrap();\n///     my_data.counter += 1;\n///     HttpResponse::Ok()\n/// }\n///\n/// /// Alternatively, use the `HttpRequest::app_data` method to access data in a handler.\n/// async fn index_alt(req: HttpRequest) -> impl Responder {\n///     let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();\n///     let mut my_data = data.lock().unwrap();\n///     my_data.counter += 1;\n///     HttpResponse::Ok()\n/// }\n///\n/// let data = Data::new(Mutex::new(MyData { counter: 0 }));\n///\n/// let app = App::new()\n///     // Store `MyData` in application storage.\n///     .app_data(Data::clone(&data))\n///     .route(\"/index.html\", web::get().to(index))\n///     .route(\"/index-alt.html\", web::get().to(index_alt));\n/// ```\n#[doc(alias = \"state\")]\n#[derive(Debug)]\npub struct Data<T: ?Sized>(Arc<T>);\n\nimpl<T> Data<T> {\n    /// Create new `Data` instance.\n    pub fn new(state: T) -> Data<T> {\n        Data(Arc::new(state))\n    }\n}\n\nimpl<T: ?Sized> Data<T> {\n    /// Returns reference to inner `T`.\n    pub fn get_ref(&self) -> &T {\n        self.0.as_ref()\n    }\n\n    /// Unwraps to the internal `Arc<T>`\n    pub fn into_inner(self) -> Arc<T> {\n        self.0\n    }\n}\n\nimpl<T: ?Sized> Deref for Data<T> {\n    type Target = Arc<T>;\n\n    fn deref(&self) -> &Arc<T> {\n        &self.0\n    }\n}\n\nimpl<T: ?Sized> Clone for Data<T> {\n    fn clone(&self) -> Data<T> {\n        Data(Arc::clone(&self.0))\n    }\n}\n\nimpl<T: ?Sized> From<Arc<T>> for Data<T> {\n    fn from(arc: Arc<T>) -> Self {\n        Data(arc)\n    }\n}\n\nimpl<T: Default> Default for Data<T> {\n    fn default() -> Self {\n        Data::new(T::default())\n    }\n}\n\nimpl<T> Serialize for Data<T>\nwhere\n    T: Serialize,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.0.serialize(serializer)\n    }\n}\nimpl<'de, T> de::Deserialize<'de> for Data<T>\nwhere\n    T: de::Deserialize<'de>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: de::Deserializer<'de>,\n    {\n        Ok(Data::new(T::deserialize(deserializer)?))\n    }\n}\n\nimpl<T: ?Sized + 'static> FromRequest for Data<T> {\n    type Error = Error;\n    type Future = Ready<Result<Self, Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        if let Some(st) = req.app_data::<Data<T>>() {\n            ok(st.clone())\n        } else {\n            log::debug!(\n                \"Failed to extract `Data<{}>` for `{}` handler. For the Data extractor to work \\\n                correctly, wrap the data with `Data::new()` and pass it to `App::app_data()`. \\\n                Ensure that types align in both the set and retrieve calls.\",\n                type_name::<T>(),\n                req.match_name().unwrap_or_else(|| req.path())\n            );\n\n            err(error::ErrorInternalServerError(\n                \"Requested application data is not configured correctly. \\\n                View/enable debug logs for more details.\",\n            ))\n        }\n    }\n}\n\nimpl<T: ?Sized + 'static> DataFactory for Data<T> {\n    fn create(&self, extensions: &mut Extensions) -> bool {\n        extensions.insert(Data(Arc::clone(&self.0)));\n        true\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        dev::Service,\n        http::StatusCode,\n        test::{init_service, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data_extractor() {\n        let srv = init_service(\n            App::new()\n                .data(\"TEST\".to_string())\n                .service(web::resource(\"/\").to(|data: web::Data<String>| {\n                    assert_eq!(data.to_lowercase(), \"test\");\n                    HttpResponse::Ok()\n                })),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let srv = init_service(\n            App::new()\n                .data(10u32)\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\n        let srv = init_service(\n            App::new()\n                .data(10u32)\n                .data(13u32)\n                .app_data(12u64)\n                .app_data(15u64)\n                .default_service(web::to(|n: web::Data<u32>, req: HttpRequest| {\n                    // in each case, the latter insertion should be preserved\n                    assert_eq!(*req.app_data::<u64>().unwrap(), 15);\n                    assert_eq!(*n.into_inner(), 13);\n                    HttpResponse::Ok()\n                })),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_app_data_extractor() {\n        let srv = init_service(\n            App::new()\n                .app_data(Data::new(10usize))\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let srv = init_service(\n            App::new()\n                .app_data(Data::new(10u32))\n                .service(web::resource(\"/\").to(|_: web::Data<usize>| HttpResponse::Ok())),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_route_data_extractor() {\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/\")\n                    .data(10usize)\n                    .route(web::get().to(|_data: web::Data<usize>| HttpResponse::Ok())),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        // different type\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/\")\n                    .data(10u32)\n                    .route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),\n            ),\n        )\n        .await;\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_override_data() {\n        let srv = init_service(\n            App::new()\n                .data(1usize)\n                .service(web::resource(\"/\").data(10usize).route(web::get().to(\n                    |data: web::Data<usize>| {\n                        assert_eq!(**data, 10);\n                        HttpResponse::Ok()\n                    },\n                ))),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_data_from_arc() {\n        let data_new = Data::new(String::from(\"test-123\"));\n        let data_from_arc = Data::from(Arc::new(String::from(\"test-123\")));\n        assert_eq!(data_new.0, data_from_arc.0);\n    }\n\n    #[actix_rt::test]\n    async fn test_data_from_dyn_arc() {\n        trait TestTrait {\n            fn get_num(&self) -> i32;\n        }\n        struct A {}\n        impl TestTrait for A {\n            fn get_num(&self) -> i32 {\n                42\n            }\n        }\n        // This works when Sized is required\n        let dyn_arc_box: Arc<Box<dyn TestTrait>> = Arc::new(Box::new(A {}));\n        let data_arc_box = Data::from(dyn_arc_box);\n        // This works when Data Sized Bound is removed\n        let dyn_arc: Arc<dyn TestTrait> = Arc::new(A {});\n        let data_arc = Data::from(dyn_arc);\n        assert_eq!(data_arc_box.get_num(), data_arc.get_num())\n    }\n\n    #[actix_rt::test]\n    async fn test_dyn_data_into_arc() {\n        trait TestTrait {\n            fn get_num(&self) -> i32;\n        }\n        struct A {}\n        impl TestTrait for A {\n            fn get_num(&self) -> i32 {\n                42\n            }\n        }\n        let dyn_arc: Arc<dyn TestTrait> = Arc::new(A {});\n        let data_arc = Data::from(dyn_arc);\n        let arc_from_data = data_arc.clone().into_inner();\n        assert_eq!(data_arc.get_num(), arc_from_data.get_num())\n    }\n\n    #[actix_rt::test]\n    async fn test_get_ref_from_dyn_data() {\n        trait TestTrait {\n            fn get_num(&self) -> i32;\n        }\n        struct A {}\n        impl TestTrait for A {\n            fn get_num(&self) -> i32 {\n                42\n            }\n        }\n        let dyn_arc: Arc<dyn TestTrait> = Arc::new(A {});\n        let data_arc = Data::from(dyn_arc);\n        let ref_data = data_arc.get_ref();\n        assert_eq!(data_arc.get_num(), ref_data.get_num())\n    }\n}\n"
  },
  {
    "path": "actix-web/src/dev.rs",
    "content": "//! Lower-level types and re-exports.\n//!\n//! Most users will not have to interact with the types in this module, but it is useful for those\n//! writing extractors, middleware, libraries, or interacting with the service API directly.\n//!\n//! # Request Extractors\n//! - [`ConnectionInfo`]: Connection information\n//! - [`PeerAddr`]: Connection information\n\n#[cfg(feature = \"__compress\")]\npub use actix_http::encoding::Decoder as Decompress;\npub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead};\nuse actix_router::Patterns;\npub use actix_router::{Path, ResourceDef, ResourcePath, Url};\npub use actix_server::{Server, ServerHandle};\npub use actix_service::{\n    always_ready, fn_factory, fn_service, forward_ready, Service, ServiceFactory, Transform,\n};\n\n#[doc(hidden)]\npub use crate::handler::Handler;\npub use crate::{\n    config::{AppConfig, AppService},\n    info::{ConnectionInfo, PeerAddr},\n    rmap::ResourceMap,\n    service::{HttpServiceFactory, ServiceRequest, ServiceResponse, WebService},\n    types::{JsonBody, Readlines, UrlEncoded},\n};\n\npub(crate) fn ensure_leading_slash(mut patterns: Patterns) -> Patterns {\n    match &mut patterns {\n        Patterns::Single(pat) => {\n            if !pat.is_empty() && !pat.starts_with('/') {\n                pat.insert(0, '/');\n            };\n        }\n        Patterns::List(pats) => {\n            for pat in pats {\n                if !pat.is_empty() && !pat.starts_with('/') {\n                    pat.insert(0, '/');\n                };\n            }\n        }\n    }\n\n    patterns\n}\n"
  },
  {
    "path": "actix-web/src/error/error.rs",
    "content": "use std::{error::Error as StdError, fmt};\n\nuse actix_http::{body::BoxBody, Response};\n\nuse crate::{HttpResponse, ResponseError};\n\n/// General purpose Actix Web error.\n///\n/// An Actix Web error is used to carry errors from `std::error` through actix in a convenient way.\n/// It can be created through converting errors with `into()`.\n///\n/// Whenever it is created from an external object a response error is created for it that can be\n/// used to create an HTTP response from it this means that if you have access to an actix `Error`\n/// you can always get a `ResponseError` reference from it.\npub struct Error {\n    cause: Box<dyn ResponseError>,\n}\n\nimpl Error {\n    /// Returns the reference to the underlying `ResponseError`.\n    pub fn as_response_error(&self) -> &dyn ResponseError {\n        self.cause.as_ref()\n    }\n\n    /// Similar to `as_response_error` but downcasts.\n    pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {\n        <dyn ResponseError>::downcast_ref(self.cause.as_ref())\n    }\n\n    /// Shortcut for creating an `HttpResponse`.\n    pub fn error_response(&self) -> HttpResponse {\n        self.cause.error_response()\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.cause, f)\n    }\n}\n\nimpl fmt::Debug for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{:?}\", &self.cause)\n    }\n}\n\nimpl StdError for Error {\n    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n        None\n    }\n}\n\n/// `Error` for any error that implements `ResponseError`\nimpl<T: ResponseError + 'static> From<T> for Error {\n    fn from(err: T) -> Error {\n        Error {\n            cause: Box::new(err),\n        }\n    }\n}\n\nimpl From<Box<dyn ResponseError>> for Error {\n    fn from(value: Box<dyn ResponseError>) -> Self {\n        Error { cause: value }\n    }\n}\n\nimpl From<Error> for Response<BoxBody> {\n    fn from(err: Error) -> Response<BoxBody> {\n        err.error_response().into()\n    }\n}\n"
  },
  {
    "path": "actix-web/src/error/internal.rs",
    "content": "use std::{cell::RefCell, fmt, io::Write as _};\n\nuse actix_http::{\n    body::BoxBody,\n    header::{self, TryIntoHeaderValue as _},\n    StatusCode,\n};\nuse bytes::{BufMut as _, BytesMut};\n\nuse crate::{Error, HttpRequest, HttpResponse, Responder, ResponseError};\n\n/// Wraps errors to alter the generated response status code.\n///\n/// In following example, the `io::Error` is wrapped into `ErrorBadRequest` which will generate a\n/// response with the 400 Bad Request status code instead of the usual status code generated by\n/// an `io::Error`.\n///\n/// # Examples\n/// ```\n/// # use std::io;\n/// # use actix_web::{error, HttpRequest};\n/// async fn handler_error() -> Result<String, actix_web::Error> {\n///     let err = io::Error::new(io::ErrorKind::Other, \"error\");\n///     Err(error::ErrorBadRequest(err))\n/// }\n/// ```\npub struct InternalError<T> {\n    cause: T,\n    status: InternalErrorType,\n}\n\nenum InternalErrorType {\n    Status(StatusCode),\n    Response(RefCell<Option<HttpResponse>>),\n}\n\nimpl<T> InternalError<T> {\n    /// Constructs an `InternalError` with given status code.\n    pub fn new(cause: T, status: StatusCode) -> Self {\n        InternalError {\n            cause,\n            status: InternalErrorType::Status(status),\n        }\n    }\n\n    /// Constructs an `InternalError` with pre-defined response.\n    pub fn from_response(cause: T, response: HttpResponse) -> Self {\n        InternalError {\n            cause,\n            status: InternalErrorType::Response(RefCell::new(Some(response))),\n        }\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for InternalError<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.cause.fmt(f)\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for InternalError<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.cause.fmt(f)\n    }\n}\n\nimpl<T> ResponseError for InternalError<T>\nwhere\n    T: fmt::Debug + fmt::Display,\n{\n    fn status_code(&self) -> StatusCode {\n        match self.status {\n            InternalErrorType::Status(st) => st,\n            InternalErrorType::Response(ref resp) => {\n                if let Some(resp) = resp.borrow().as_ref() {\n                    resp.head().status\n                } else {\n                    StatusCode::INTERNAL_SERVER_ERROR\n                }\n            }\n        }\n    }\n\n    fn error_response(&self) -> HttpResponse {\n        match self.status {\n            InternalErrorType::Status(status) => {\n                let mut res = HttpResponse::new(status);\n                let mut buf = BytesMut::new().writer();\n                let _ = write!(buf, \"{}\", self);\n\n                let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n                res.headers_mut().insert(header::CONTENT_TYPE, mime);\n\n                res.set_body(BoxBody::new(buf.into_inner()))\n            }\n\n            InternalErrorType::Response(ref resp) => {\n                if let Some(resp) = resp.borrow_mut().take() {\n                    resp\n                } else {\n                    HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)\n                }\n            }\n        }\n    }\n}\n\nimpl<T> Responder for InternalError<T>\nwhere\n    T: fmt::Debug + fmt::Display + 'static,\n{\n    type Body = BoxBody;\n\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        HttpResponse::from_error(self)\n    }\n}\n\nmacro_rules! error_helper {\n    ($name:ident, $status:ident) => {\n        #[doc = concat!(\"Helper function that wraps any error and generates a `\", stringify!($status), \"` response.\")]\n        #[allow(non_snake_case)]\n        pub fn $name<T>(err: T) -> Error\n        where\n            T: fmt::Debug + fmt::Display + 'static,\n        {\n            InternalError::new(err, StatusCode::$status).into()\n        }\n    };\n}\n\nerror_helper!(ErrorBadRequest, BAD_REQUEST);\nerror_helper!(ErrorUnauthorized, UNAUTHORIZED);\nerror_helper!(ErrorPaymentRequired, PAYMENT_REQUIRED);\nerror_helper!(ErrorForbidden, FORBIDDEN);\nerror_helper!(ErrorNotFound, NOT_FOUND);\nerror_helper!(ErrorMethodNotAllowed, METHOD_NOT_ALLOWED);\nerror_helper!(ErrorNotAcceptable, NOT_ACCEPTABLE);\nerror_helper!(\n    ErrorProxyAuthenticationRequired,\n    PROXY_AUTHENTICATION_REQUIRED\n);\nerror_helper!(ErrorRequestTimeout, REQUEST_TIMEOUT);\nerror_helper!(ErrorConflict, CONFLICT);\nerror_helper!(ErrorGone, GONE);\nerror_helper!(ErrorLengthRequired, LENGTH_REQUIRED);\nerror_helper!(ErrorPayloadTooLarge, PAYLOAD_TOO_LARGE);\nerror_helper!(ErrorUriTooLong, URI_TOO_LONG);\nerror_helper!(ErrorUnsupportedMediaType, UNSUPPORTED_MEDIA_TYPE);\nerror_helper!(ErrorRangeNotSatisfiable, RANGE_NOT_SATISFIABLE);\nerror_helper!(ErrorImATeapot, IM_A_TEAPOT);\nerror_helper!(ErrorMisdirectedRequest, MISDIRECTED_REQUEST);\nerror_helper!(ErrorUnprocessableEntity, UNPROCESSABLE_ENTITY);\nerror_helper!(ErrorLocked, LOCKED);\nerror_helper!(ErrorFailedDependency, FAILED_DEPENDENCY);\nerror_helper!(ErrorUpgradeRequired, UPGRADE_REQUIRED);\nerror_helper!(ErrorPreconditionFailed, PRECONDITION_FAILED);\nerror_helper!(ErrorPreconditionRequired, PRECONDITION_REQUIRED);\nerror_helper!(ErrorTooManyRequests, TOO_MANY_REQUESTS);\nerror_helper!(\n    ErrorRequestHeaderFieldsTooLarge,\n    REQUEST_HEADER_FIELDS_TOO_LARGE\n);\nerror_helper!(\n    ErrorUnavailableForLegalReasons,\n    UNAVAILABLE_FOR_LEGAL_REASONS\n);\nerror_helper!(ErrorExpectationFailed, EXPECTATION_FAILED);\nerror_helper!(ErrorInternalServerError, INTERNAL_SERVER_ERROR);\nerror_helper!(ErrorNotImplemented, NOT_IMPLEMENTED);\nerror_helper!(ErrorBadGateway, BAD_GATEWAY);\nerror_helper!(ErrorServiceUnavailable, SERVICE_UNAVAILABLE);\nerror_helper!(ErrorGatewayTimeout, GATEWAY_TIMEOUT);\nerror_helper!(ErrorHttpVersionNotSupported, HTTP_VERSION_NOT_SUPPORTED);\nerror_helper!(ErrorVariantAlsoNegotiates, VARIANT_ALSO_NEGOTIATES);\nerror_helper!(ErrorInsufficientStorage, INSUFFICIENT_STORAGE);\nerror_helper!(ErrorLoopDetected, LOOP_DETECTED);\nerror_helper!(ErrorNotExtended, NOT_EXTENDED);\nerror_helper!(\n    ErrorNetworkAuthenticationRequired,\n    NETWORK_AUTHENTICATION_REQUIRED\n);\n\n#[cfg(test)]\nmod tests {\n    use actix_http::error::ParseError;\n\n    use super::*;\n\n    #[test]\n    fn test_internal_error() {\n        let err = InternalError::from_response(ParseError::Method, HttpResponse::Ok().finish());\n        let resp: HttpResponse = err.error_response();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[test]\n    fn test_error_helpers() {\n        let res: HttpResponse = ErrorBadRequest(\"err\").into();\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n\n        let res: HttpResponse = ErrorUnauthorized(\"err\").into();\n        assert_eq!(res.status(), StatusCode::UNAUTHORIZED);\n\n        let res: HttpResponse = ErrorPaymentRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::PAYMENT_REQUIRED);\n\n        let res: HttpResponse = ErrorForbidden(\"err\").into();\n        assert_eq!(res.status(), StatusCode::FORBIDDEN);\n\n        let res: HttpResponse = ErrorNotFound(\"err\").into();\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n\n        let res: HttpResponse = ErrorMethodNotAllowed(\"err\").into();\n        assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);\n\n        let res: HttpResponse = ErrorNotAcceptable(\"err\").into();\n        assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);\n\n        let res: HttpResponse = ErrorProxyAuthenticationRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::PROXY_AUTHENTICATION_REQUIRED);\n\n        let res: HttpResponse = ErrorRequestTimeout(\"err\").into();\n        assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);\n\n        let res: HttpResponse = ErrorConflict(\"err\").into();\n        assert_eq!(res.status(), StatusCode::CONFLICT);\n\n        let res: HttpResponse = ErrorGone(\"err\").into();\n        assert_eq!(res.status(), StatusCode::GONE);\n\n        let res: HttpResponse = ErrorLengthRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);\n\n        let res: HttpResponse = ErrorPreconditionFailed(\"err\").into();\n        assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED);\n\n        let res: HttpResponse = ErrorPayloadTooLarge(\"err\").into();\n        assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);\n\n        let res: HttpResponse = ErrorUriTooLong(\"err\").into();\n        assert_eq!(res.status(), StatusCode::URI_TOO_LONG);\n\n        let res: HttpResponse = ErrorUnsupportedMediaType(\"err\").into();\n        assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);\n\n        let res: HttpResponse = ErrorRangeNotSatisfiable(\"err\").into();\n        assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);\n\n        let res: HttpResponse = ErrorExpectationFailed(\"err\").into();\n        assert_eq!(res.status(), StatusCode::EXPECTATION_FAILED);\n\n        let res: HttpResponse = ErrorImATeapot(\"err\").into();\n        assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);\n\n        let res: HttpResponse = ErrorMisdirectedRequest(\"err\").into();\n        assert_eq!(res.status(), StatusCode::MISDIRECTED_REQUEST);\n\n        let res: HttpResponse = ErrorUnprocessableEntity(\"err\").into();\n        assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);\n\n        let res: HttpResponse = ErrorLocked(\"err\").into();\n        assert_eq!(res.status(), StatusCode::LOCKED);\n\n        let res: HttpResponse = ErrorFailedDependency(\"err\").into();\n        assert_eq!(res.status(), StatusCode::FAILED_DEPENDENCY);\n\n        let res: HttpResponse = ErrorUpgradeRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::UPGRADE_REQUIRED);\n\n        let res: HttpResponse = ErrorPreconditionRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::PRECONDITION_REQUIRED);\n\n        let res: HttpResponse = ErrorTooManyRequests(\"err\").into();\n        assert_eq!(res.status(), StatusCode::TOO_MANY_REQUESTS);\n\n        let res: HttpResponse = ErrorRequestHeaderFieldsTooLarge(\"err\").into();\n        assert_eq!(res.status(), StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE);\n\n        let res: HttpResponse = ErrorUnavailableForLegalReasons(\"err\").into();\n        assert_eq!(res.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);\n\n        let res: HttpResponse = ErrorInternalServerError(\"err\").into();\n        assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\n        let res: HttpResponse = ErrorNotImplemented(\"err\").into();\n        assert_eq!(res.status(), StatusCode::NOT_IMPLEMENTED);\n\n        let res: HttpResponse = ErrorBadGateway(\"err\").into();\n        assert_eq!(res.status(), StatusCode::BAD_GATEWAY);\n\n        let res: HttpResponse = ErrorServiceUnavailable(\"err\").into();\n        assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);\n\n        let res: HttpResponse = ErrorGatewayTimeout(\"err\").into();\n        assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);\n\n        let res: HttpResponse = ErrorHttpVersionNotSupported(\"err\").into();\n        assert_eq!(res.status(), StatusCode::HTTP_VERSION_NOT_SUPPORTED);\n\n        let res: HttpResponse = ErrorVariantAlsoNegotiates(\"err\").into();\n        assert_eq!(res.status(), StatusCode::VARIANT_ALSO_NEGOTIATES);\n\n        let res: HttpResponse = ErrorInsufficientStorage(\"err\").into();\n        assert_eq!(res.status(), StatusCode::INSUFFICIENT_STORAGE);\n\n        let res: HttpResponse = ErrorLoopDetected(\"err\").into();\n        assert_eq!(res.status(), StatusCode::LOOP_DETECTED);\n\n        let res: HttpResponse = ErrorNotExtended(\"err\").into();\n        assert_eq!(res.status(), StatusCode::NOT_EXTENDED);\n\n        let res: HttpResponse = ErrorNetworkAuthenticationRequired(\"err\").into();\n        assert_eq!(res.status(), StatusCode::NETWORK_AUTHENTICATION_REQUIRED);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/error/macros.rs",
    "content": "macro_rules! downcast_get_type_id {\n    () => {\n        /// A helper method to get the type ID of the type\n        /// this trait is implemented on.\n        /// This method is unsafe to *implement*, since `downcast_ref` relies\n        /// on the returned `TypeId` to perform a cast.\n        ///\n        /// Unfortunately, Rust has no notion of a trait method that is\n        /// unsafe to implement (marking it as `unsafe` makes it unsafe\n        /// to *call*). As a workaround, we require this method\n        /// to return a private type along with the `TypeId`. This\n        /// private type (`PrivateHelper`) has a private constructor,\n        /// making it impossible for safe code to construct outside of\n        /// this module. This ensures that safe code cannot violate\n        /// type-safety by implementing this method.\n        ///\n        /// We also take `PrivateHelper` as a parameter, to ensure that\n        /// safe code cannot obtain a `PrivateHelper` instance by\n        /// delegating to an existing implementation of `__private_get_type_id__`\n        #[doc(hidden)]\n        #[allow(dead_code)]\n        fn __private_get_type_id__(&self, _: PrivateHelper) -> (std::any::TypeId, PrivateHelper)\n        where\n            Self: 'static,\n        {\n            (std::any::TypeId::of::<Self>(), PrivateHelper(()))\n        }\n    };\n}\n\n// Generate implementation for dyn $name\nmacro_rules! downcast_dyn {\n    ($name:ident) => {\n        /// A struct with a private constructor, for use with\n        /// `__private_get_type_id__`. Its single field is private,\n        /// ensuring that it can only be constructed from this module\n        #[doc(hidden)]\n        #[allow(dead_code)]\n        pub struct PrivateHelper(());\n\n        impl dyn $name + 'static {\n            /// Downcasts generic body to a specific type.\n            #[allow(dead_code)]\n            pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {\n                if self.__private_get_type_id__(PrivateHelper(())).0 == std::any::TypeId::of::<T>()\n                {\n                    // SAFETY: external crates cannot override the default\n                    // implementation of `__private_get_type_id__`, since\n                    // it requires returning a private type. We can therefore\n                    // rely on the returned `TypeId`, which ensures that this\n                    // case is correct.\n                    unsafe { Some(&*(self as *const dyn $name as *const T)) }\n                } else {\n                    None\n                }\n            }\n\n            /// Downcasts a generic body to a mutable specific type.\n            #[allow(dead_code)]\n            pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {\n                if self.__private_get_type_id__(PrivateHelper(())).0 == std::any::TypeId::of::<T>()\n                {\n                    // SAFETY: external crates cannot override the default\n                    // implementation of `__private_get_type_id__`, since\n                    // it requires returning a private type. We can therefore\n                    // rely on the returned `TypeId`, which ensures that this\n                    // case is correct.\n                    unsafe { Some(&mut *(self as *const dyn $name as *const T as *mut T)) }\n                } else {\n                    None\n                }\n            }\n        }\n    };\n}\n\npub(crate) use downcast_dyn;\npub(crate) use downcast_get_type_id;\n\n#[cfg(test)]\nmod tests {\n    #![allow(clippy::upper_case_acronyms)]\n\n    trait MB {\n        downcast_get_type_id!();\n    }\n\n    downcast_dyn!(MB);\n\n    impl MB for String {}\n    impl MB for () {}\n\n    #[actix_rt::test]\n    async fn test_any_casting() {\n        let mut body = String::from(\"hello cast\");\n        let resp_body: &mut dyn MB = &mut body;\n        let body = resp_body.downcast_ref::<String>().unwrap();\n        assert_eq!(body, \"hello cast\");\n        let body = resp_body.downcast_mut::<String>().unwrap();\n        body.push('!');\n        let body = resp_body.downcast_ref::<String>().unwrap();\n        assert_eq!(body, \"hello cast!\");\n        let not_body = resp_body.downcast_ref::<()>();\n        assert!(not_body.is_none());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/error/mod.rs",
    "content": "//! Error and Result module\n\n// This is meant to be a glob import of the whole error module except for `Error`. Rustdoc can't yet\n// correctly resolve the conflicting `Error` type defined in this module, so these re-exports are\n// expanded manually.\n//\n// See <https://github.com/rust-lang/rust/issues/83375>\npub use actix_http::error::{ContentTypeError, DispatchError, HttpError, ParseError, PayloadError};\nuse derive_more::{Display, Error, From};\nuse serde_json::error::Error as JsonError;\nuse serde_urlencoded::{de::Error as FormDeError, ser::Error as FormError};\nuse url::ParseError as UrlParseError;\n\nuse crate::http::StatusCode;\n\n#[allow(clippy::module_inception)]\nmod error;\nmod internal;\nmod macros;\nmod response_error;\n\npub(crate) use self::macros::{downcast_dyn, downcast_get_type_id};\npub use self::{error::Error, internal::*, response_error::ResponseError};\npub use crate::types::EitherExtractError;\n\n/// A convenience [`Result`](std::result::Result) for Actix Web operations.\n///\n/// This type alias is generally used to avoid writing out `actix_http::Error` directly.\npub type Result<T, E = Error> = std::result::Result<T, E>;\n\n/// An error representing a problem running a blocking task on a thread pool.\n#[derive(Debug, Display, Error)]\n#[display(\"Blocking thread pool is shut down unexpectedly\")]\n#[non_exhaustive]\npub struct BlockingError;\n\nimpl ResponseError for crate::error::BlockingError {}\n\n/// Errors which can occur when attempting to generate resource uri.\n#[derive(Debug, PartialEq, Eq, Display, Error, From)]\n#[non_exhaustive]\npub enum UrlGenerationError {\n    /// Resource not found.\n    #[display(\"Resource not found\")]\n    ResourceNotFound,\n\n    /// Not all URL parameters covered.\n    #[display(\"Not all URL parameters covered\")]\n    NotEnoughElements,\n\n    /// URL parse error.\n    #[display(\"{}\", _0)]\n    ParseError(UrlParseError),\n}\n\nimpl ResponseError for UrlGenerationError {}\n\n/// A set of errors that can occur during parsing urlencoded payloads\n#[derive(Debug, Display, Error, From)]\n#[non_exhaustive]\npub enum UrlencodedError {\n    /// Can not decode chunked transfer encoding.\n    #[display(\"Can not decode chunked transfer encoding.\")]\n    Chunked,\n\n    /// Payload size is larger than allowed. (default limit: 256kB).\n    #[display(\n        \"URL encoded payload is larger ({} bytes) than allowed (limit: {} bytes).\",\n        size,\n        limit\n    )]\n    Overflow { size: usize, limit: usize },\n\n    /// Payload size is now known.\n    #[display(\"Payload size is now known.\")]\n    UnknownLength,\n\n    /// Content type error.\n    #[display(\"Content type error.\")]\n    ContentType,\n\n    /// Parse error.\n    #[display(\"Parse error: {}.\", _0)]\n    Parse(FormDeError),\n\n    /// Encoding error.\n    #[display(\"Encoding error.\")]\n    Encoding,\n\n    /// Serialize error.\n    #[display(\"Serialize error: {}.\", _0)]\n    Serialize(FormError),\n\n    /// Payload error.\n    #[display(\"Error that occur during reading payload: {}.\", _0)]\n    Payload(PayloadError),\n}\n\nimpl ResponseError for UrlencodedError {\n    fn status_code(&self) -> StatusCode {\n        match self {\n            Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,\n            Self::UnknownLength => StatusCode::LENGTH_REQUIRED,\n            Self::ContentType => StatusCode::UNSUPPORTED_MEDIA_TYPE,\n            Self::Payload(err) => err.status_code(),\n            _ => StatusCode::BAD_REQUEST,\n        }\n    }\n}\n\n/// A set of errors that can occur during parsing json payloads\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum JsonPayloadError {\n    /// Payload size is bigger than allowed & content length header set. (default: 2MB)\n    #[display(\n        \"JSON payload ({} bytes) is larger than allowed (limit: {} bytes).\",\n        length,\n        limit\n    )]\n    OverflowKnownLength { length: usize, limit: usize },\n\n    /// Payload size is bigger than allowed but no content length header set. (default: 2MB)\n    #[display(\"JSON payload has exceeded limit ({} bytes).\", limit)]\n    Overflow { limit: usize },\n\n    /// Content type error\n    #[display(\"Content type error\")]\n    ContentType,\n\n    /// Deserialize error\n    #[display(\"Json deserialize error: {}\", _0)]\n    Deserialize(JsonError),\n\n    /// Serialize error\n    #[display(\"Json serialize error: {}\", _0)]\n    Serialize(JsonError),\n\n    /// Payload error\n    #[display(\"Error that occur during reading payload: {}\", _0)]\n    Payload(PayloadError),\n}\n\nimpl From<PayloadError> for JsonPayloadError {\n    fn from(err: PayloadError) -> Self {\n        Self::Payload(err)\n    }\n}\n\nimpl ResponseError for JsonPayloadError {\n    fn status_code(&self) -> StatusCode {\n        match self {\n            Self::OverflowKnownLength {\n                length: _,\n                limit: _,\n            } => StatusCode::PAYLOAD_TOO_LARGE,\n            Self::Overflow { limit: _ } => StatusCode::PAYLOAD_TOO_LARGE,\n            Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,\n            Self::Payload(err) => err.status_code(),\n            _ => StatusCode::BAD_REQUEST,\n        }\n    }\n}\n\n/// A set of errors that can occur during parsing request paths\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\npub enum PathError {\n    /// Deserialize error\n    #[display(\"Path deserialize error: {}\", _0)]\n    Deserialize(serde::de::value::Error),\n}\n\n/// Return `BadRequest` for `PathError`\nimpl ResponseError for PathError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\n/// A set of errors that can occur during parsing query strings.\n#[derive(Debug, Display, Error, From)]\n#[non_exhaustive]\npub enum QueryPayloadError {\n    /// Query deserialize error.\n    #[display(\"Query deserialize error: {}\", _0)]\n    Deserialize(serde::de::value::Error),\n}\n\nimpl ResponseError for QueryPayloadError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\n/// Error type returned when reading body as lines.\n#[derive(Debug, Display, Error, From)]\n#[non_exhaustive]\npub enum ReadlinesError {\n    #[display(\"Encoding error\")]\n    /// Payload size is bigger than allowed. (default: 256kB)\n    EncodingError,\n\n    /// Payload error.\n    #[display(\"Error that occur during reading payload: {}\", _0)]\n    Payload(PayloadError),\n\n    /// Line limit exceeded.\n    #[display(\"Line limit exceeded\")]\n    LimitOverflow,\n\n    /// ContentType error.\n    #[display(\"Content-type error\")]\n    ContentTypeError(ContentTypeError),\n}\n\nimpl ResponseError for ReadlinesError {\n    fn status_code(&self) -> StatusCode {\n        match *self {\n            ReadlinesError::LimitOverflow => StatusCode::PAYLOAD_TOO_LARGE,\n            _ => StatusCode::BAD_REQUEST,\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_urlencoded_error() {\n        let resp = UrlencodedError::Overflow { size: 0, limit: 0 }.error_response();\n        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);\n        let resp = UrlencodedError::UnknownLength.error_response();\n        assert_eq!(resp.status(), StatusCode::LENGTH_REQUIRED);\n        let resp = UrlencodedError::ContentType.error_response();\n        assert_eq!(resp.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);\n    }\n\n    #[test]\n    fn test_json_payload_error() {\n        let resp = JsonPayloadError::OverflowKnownLength {\n            length: 0,\n            limit: 0,\n        }\n        .error_response();\n        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);\n        let resp = JsonPayloadError::Overflow { limit: 0 }.error_response();\n        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);\n        let resp = JsonPayloadError::ContentType.error_response();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[test]\n    fn test_query_payload_error() {\n        let resp = QueryPayloadError::Deserialize(\n            serde_urlencoded::from_str::<i32>(\"bad query\").unwrap_err(),\n        )\n        .error_response();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[test]\n    fn test_readlines_error() {\n        let resp = ReadlinesError::LimitOverflow.error_response();\n        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);\n        let resp = ReadlinesError::EncodingError.error_response();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/error/response_error.rs",
    "content": "//! `ResponseError` trait and foreign impls.\n\nuse std::{\n    convert::Infallible,\n    error::Error as StdError,\n    fmt,\n    io::{self, Write as _},\n};\n\nuse bytes::BytesMut;\n\nuse crate::{\n    body::BoxBody,\n    error::{downcast_dyn, downcast_get_type_id},\n    helpers,\n    http::{\n        header::{self, TryIntoHeaderValue},\n        StatusCode,\n    },\n    HttpResponse,\n};\n\n/// Errors that can generate responses.\n// TODO: flesh out documentation\npub trait ResponseError: fmt::Debug + fmt::Display {\n    /// Returns appropriate status code for error.\n    ///\n    /// A 500 Internal Server Error is used by default. If [error_response](Self::error_response) is\n    /// also implemented and does not call `self.status_code()`, then this will not be used.\n    fn status_code(&self) -> StatusCode {\n        StatusCode::INTERNAL_SERVER_ERROR\n    }\n\n    /// Creates full response for error.\n    ///\n    /// By default, the generated response uses a 500 Internal Server Error status code, a\n    /// `Content-Type` of `text/plain`, and the body is set to `Self`'s `Display` impl.\n    fn error_response(&self) -> HttpResponse<BoxBody> {\n        let mut res = HttpResponse::new(self.status_code());\n\n        let mut buf = BytesMut::new();\n        let _ = write!(helpers::MutWriter(&mut buf), \"{}\", self);\n\n        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();\n        res.headers_mut().insert(header::CONTENT_TYPE, mime);\n\n        res.set_body(BoxBody::new(buf))\n    }\n\n    downcast_get_type_id!();\n}\n\ndowncast_dyn!(ResponseError);\n\nimpl ResponseError for Box<dyn StdError + 'static> {}\n\nimpl ResponseError for Infallible {\n    fn status_code(&self) -> StatusCode {\n        match *self {}\n    }\n    fn error_response(&self) -> HttpResponse<BoxBody> {\n        match *self {}\n    }\n}\n\n#[cfg(feature = \"openssl\")]\nimpl ResponseError for actix_tls::accept::openssl::reexports::Error {}\n\nimpl ResponseError for serde::de::value::Error {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\nimpl ResponseError for serde_json::Error {}\n\nimpl ResponseError for serde_urlencoded::ser::Error {}\n\nimpl ResponseError for std::str::Utf8Error {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\nimpl ResponseError for std::io::Error {\n    fn status_code(&self) -> StatusCode {\n        match self.kind() {\n            io::ErrorKind::NotFound => StatusCode::NOT_FOUND,\n            io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,\n            _ => StatusCode::INTERNAL_SERVER_ERROR,\n        }\n    }\n}\n\nimpl ResponseError for actix_http::error::HttpError {}\n\nimpl ResponseError for actix_http::Error {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::INTERNAL_SERVER_ERROR\n    }\n\n    fn error_response(&self) -> HttpResponse<BoxBody> {\n        HttpResponse::with_body(self.status_code(), self.to_string()).map_into_boxed_body()\n    }\n}\n\nimpl ResponseError for actix_http::header::InvalidHeaderValue {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\nimpl ResponseError for actix_http::error::ParseError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\nimpl ResponseError for actix_http::error::PayloadError {\n    fn status_code(&self) -> StatusCode {\n        match *self {\n            actix_http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,\n            _ => StatusCode::BAD_REQUEST,\n        }\n    }\n}\n\nimpl ResponseError for actix_http::error::ContentTypeError {\n    fn status_code(&self) -> StatusCode {\n        StatusCode::BAD_REQUEST\n    }\n}\n\n#[cfg(feature = \"ws\")]\nimpl ResponseError for actix_http::ws::HandshakeError {\n    fn error_response(&self) -> HttpResponse<BoxBody> {\n        actix_http::Response::from(self)\n            .map_into_boxed_body()\n            .into()\n    }\n}\n\n#[cfg(feature = \"ws\")]\nimpl ResponseError for actix_http::ws::ProtocolError {}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_error_casting() {\n        use actix_http::error::{ContentTypeError, PayloadError};\n\n        let err = PayloadError::Overflow;\n        let resp_err: &dyn ResponseError = &err;\n\n        let err = resp_err.downcast_ref::<PayloadError>().unwrap();\n        assert_eq!(err.to_string(), \"payload reached size limit\");\n\n        let not_err = resp_err.downcast_ref::<ContentTypeError>();\n        assert!(not_err.is_none());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/extract.rs",
    "content": "//! Request extractors\n\nuse std::{\n    convert::Infallible,\n    future::Future,\n    marker::PhantomData,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::{Method, Uri};\nuse actix_utils::future::{ok, Ready};\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\n\nuse crate::{dev::Payload, Error, HttpRequest};\n\n/// A type that implements [`FromRequest`] is called an **extractor** and can extract data from\n/// the request. Some types that implement this trait are: [`Json`], [`Header`], and [`Path`].\n///\n/// Check out [`ServiceRequest::extract`](crate::dev::ServiceRequest::extract) if you want to\n/// leverage extractors when implementing middlewares.\n///\n/// # Configuration\n/// An extractor can be customized by injecting the corresponding configuration with one of:\n/// - [`App::app_data()`][crate::App::app_data]\n/// - [`Scope::app_data()`][crate::Scope::app_data]\n/// - [`Resource::app_data()`][crate::Resource::app_data]\n///\n/// Here are some built-in extractors and their corresponding configuration.\n/// Please refer to the respective documentation for details.\n///\n/// | Extractor   | Configuration     |\n/// |-------------|-------------------|\n/// | [`Header`]  | _None_            |\n/// | [`Path`]    | [`PathConfig`]    |\n/// | [`Json`]    | [`JsonConfig`]    |\n/// | [`Form`]    | [`FormConfig`]    |\n/// | [`Query`]   | [`QueryConfig`]   |\n/// | [`Bytes`]   | [`PayloadConfig`] |\n/// | [`String`]  | [`PayloadConfig`] |\n/// | [`Payload`] | [`PayloadConfig`] |\n///\n/// # Implementing An Extractor\n/// To reduce duplicate code in handlers where extracting certain parts of a request has a common\n/// structure, you can implement `FromRequest` for your own types.\n///\n/// Note that the request payload can only be consumed by one extractor.\n///\n/// [`Header`]: crate::web::Header\n/// [`Json`]: crate::web::Json\n/// [`JsonConfig`]: crate::web::JsonConfig\n/// [`Form`]: crate::web::Form\n/// [`FormConfig`]: crate::web::FormConfig\n/// [`Path`]: crate::web::Path\n/// [`PathConfig`]: crate::web::PathConfig\n/// [`Query`]: crate::web::Query\n/// [`QueryConfig`]: crate::web::QueryConfig\n/// [`Payload`]: crate::web::Payload\n/// [`PayloadConfig`]: crate::web::PayloadConfig\n/// [`String`]: FromRequest#impl-FromRequest-for-String\n/// [`Bytes`]: crate::web::Bytes#impl-FromRequest\n/// [`Either`]: crate::web::Either\n#[doc(alias = \"extract\", alias = \"extractor\")]\npub trait FromRequest: Sized {\n    /// The associated error which can be returned.\n    type Error: Into<Error>;\n\n    /// Future that resolves to a `Self`.\n    ///\n    /// To use an async function or block, the futures must be boxed. The following snippet will be\n    /// common when creating async/await extractors (that do not consume the body).\n    ///\n    /// ```ignore\n    /// type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;\n    /// // or\n    /// type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;\n    ///\n    /// fn from_request(req: HttpRequest, ...) -> Self::Future {\n    ///     let req = req.clone();\n    ///     Box::pin(async move {\n    ///         ...\n    ///     })\n    /// }\n    /// ```\n    type Future: Future<Output = Result<Self, Self::Error>>;\n\n    /// Create a `Self` from request parts asynchronously.\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future;\n\n    /// Create a `Self` from request head asynchronously.\n    ///\n    /// This method is short for `T::from_request(req, &mut Payload::None)`.\n    fn extract(req: &HttpRequest) -> Self::Future {\n        Self::from_request(req, &mut Payload::None)\n    }\n}\n\n/// Optionally extract from the request.\n///\n/// If the inner `T::from_request` returns an error, handler will receive `None` instead.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};\n/// use actix_web::error::ErrorBadRequest;\n/// use futures_util::future::{ok, err, Ready};\n/// use serde::Deserialize;\n/// use rand;\n///\n/// #[derive(Debug, Deserialize)]\n/// struct Thing {\n///     name: String\n/// }\n///\n/// impl FromRequest for Thing {\n///     type Error = Error;\n///     type Future = Ready<Result<Self, Self::Error>>;\n///\n///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n///         if rand::random() {\n///             ok(Thing { name: \"thingy\".into() })\n///         } else {\n///             err(ErrorBadRequest(\"no luck\"))\n///         }\n///\n///     }\n/// }\n///\n/// /// extract `Thing` from request\n/// async fn index(supplied_thing: Option<Thing>) -> String {\n///     match supplied_thing {\n///         // Puns not intended\n///         Some(thing) => format!(\"Got something: {:?}\", thing),\n///         None => format!(\"No thing!\")\n///     }\n/// }\n///\n/// let app = App::new().service(\n///     web::resource(\"/users/:first\").route(\n///         web::post().to(index))\n/// );\n/// ```\nimpl<T> FromRequest for Option<T>\nwhere\n    T: FromRequest,\n{\n    type Error = Infallible;\n    type Future = FromRequestOptFuture<T::Future>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n        FromRequestOptFuture {\n            fut: T::from_request(req, payload),\n        }\n    }\n}\n\npin_project! {\n    pub struct FromRequestOptFuture<Fut> {\n        #[pin]\n        fut: Fut,\n    }\n}\n\nimpl<Fut, T, E> Future for FromRequestOptFuture<Fut>\nwhere\n    Fut: Future<Output = Result<T, E>>,\n    E: Into<Error>,\n{\n    type Output = Result<Option<T>, Infallible>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let res = ready!(this.fut.poll(cx));\n        match res {\n            Ok(t) => Poll::Ready(Ok(Some(t))),\n            Err(err) => {\n                log::debug!(\"Error for Option<T> extractor: {}\", err.into());\n                Poll::Ready(Ok(None))\n            }\n        }\n    }\n}\n\n/// Extract from the request, passing error type through to handler.\n///\n/// If the inner `T::from_request` returns an error, allow handler to receive the error rather than\n/// immediately returning an error response.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};\n/// use actix_web::error::ErrorBadRequest;\n/// use futures_util::future::{ok, err, Ready};\n/// use serde::Deserialize;\n/// use rand;\n///\n/// #[derive(Debug, Deserialize)]\n/// struct Thing {\n///     name: String\n/// }\n///\n/// impl FromRequest for Thing {\n///     type Error = Error;\n///     type Future = Ready<Result<Thing, Error>>;\n///\n///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n///         if rand::random() {\n///             ok(Thing { name: \"thingy\".into() })\n///         } else {\n///             err(ErrorBadRequest(\"no luck\"))\n///         }\n///     }\n/// }\n///\n/// /// extract `Thing` from request\n/// async fn index(supplied_thing: Result<Thing>) -> String {\n///     match supplied_thing {\n///         Ok(thing) => format!(\"Got thing: {thing:?}\"),\n///         Err(err) => format!(\"Error extracting thing: {err}\"),\n///     }\n/// }\n///\n/// let app = App::new().service(\n///     web::resource(\"/users/:first\").route(web::post().to(index))\n/// );\n/// ```\nimpl<T, E> FromRequest for Result<T, E>\nwhere\n    T: FromRequest,\n    T::Error: Into<E>,\n{\n    type Error = Infallible;\n    type Future = FromRequestResFuture<T::Future, E>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n        FromRequestResFuture {\n            fut: T::from_request(req, payload),\n            _phantom: PhantomData,\n        }\n    }\n}\n\npin_project! {\n    pub struct FromRequestResFuture<Fut, E> {\n        #[pin]\n        fut: Fut,\n        _phantom: PhantomData<E>,\n    }\n}\n\nimpl<Fut, T, Ei, E> Future for FromRequestResFuture<Fut, E>\nwhere\n    Fut: Future<Output = Result<T, Ei>>,\n    Ei: Into<E>,\n{\n    type Output = Result<Result<T, E>, Infallible>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let res = ready!(this.fut.poll(cx));\n        Poll::Ready(Ok(res.map_err(Into::into)))\n    }\n}\n\n/// Extract the request's URI.\n///\n/// # Examples\n/// ```\n/// use actix_web::{http::Uri, web, App, Responder};\n///\n/// async fn handler(uri: Uri) -> impl Responder {\n///     format!(\"Requested path: {}\", uri.path())\n/// }\n///\n/// let app = App::new().default_service(web::to(handler));\n/// ```\nimpl FromRequest for Uri {\n    type Error = Infallible;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        ok(req.uri().clone())\n    }\n}\n\n/// Extract the request's method.\n///\n/// # Examples\n/// ```\n/// use actix_web::{http::Method, web, App, Responder};\n///\n/// async fn handler(method: Method) -> impl Responder {\n///     format!(\"Request method: {}\", method)\n/// }\n///\n/// let app = App::new().default_service(web::to(handler));\n/// ```\nimpl FromRequest for Method {\n    type Error = Infallible;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        ok(req.method().clone())\n    }\n}\n\n#[doc(hidden)]\n#[allow(non_snake_case)]\nmod tuple_from_req {\n    use super::*;\n\n    macro_rules! tuple_from_req {\n        ($fut: ident; $($T: ident),*) => {\n            /// FromRequest implementation for tuple\n            #[allow(unused_parens)]\n            impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)\n            {\n                type Error = Error;\n                type Future = $fut<$($T),+>;\n\n                fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n                    $fut {\n                        $(\n                            $T: ExtractFuture::Future {\n                                fut: $T::from_request(req, payload)\n                            },\n                        )+\n                    }\n                }\n            }\n\n            pin_project! {\n                pub struct $fut<$($T: FromRequest),+> {\n                    $(\n                        #[pin]\n                        $T: ExtractFuture<$T::Future, $T>,\n                    )+\n                }\n            }\n\n            impl<$($T: FromRequest),+> Future for $fut<$($T),+>\n            {\n                type Output = Result<($($T,)+), Error>;\n\n                fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n                    let mut this = self.project();\n\n                    let mut ready = true;\n                    $(\n                        match this.$T.as_mut().project() {\n                            ExtractProj::Future { fut } => match fut.poll(cx) {\n                                Poll::Ready(Ok(output)) => {\n                                    let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output });\n                                },\n                                Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())),\n                                Poll::Pending => ready = false,\n                            },\n                            ExtractProj::Done { .. } => {},\n                            ExtractProj::Empty => unreachable!(\"FromRequest polled after finished\"),\n                        }\n                    )+\n\n                    if ready {\n                        Poll::Ready(Ok(\n                            ($(\n                                match this.$T.project_replace(ExtractFuture::Empty) {\n                                    ExtractReplaceProj::Done { output } => output,\n                                    _ => unreachable!(\"FromRequest polled after finished\"),\n                                },\n                            )+)\n                        ))\n                    } else {\n                        Poll::Pending\n                    }\n                }\n            }\n        };\n    }\n\n    pin_project! {\n        #[project = ExtractProj]\n        #[project_replace = ExtractReplaceProj]\n        enum ExtractFuture<Fut, Res> {\n            Future {\n                #[pin]\n                fut: Fut\n            },\n            Done {\n                output: Res,\n            },\n            Empty\n        }\n    }\n\n    impl FromRequest for () {\n        type Error = Infallible;\n        type Future = Ready<Result<Self, Self::Error>>;\n\n        fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {\n            ok(())\n        }\n    }\n\n    tuple_from_req! { TupleFromRequest1; A }\n    tuple_from_req! { TupleFromRequest2; A, B }\n    tuple_from_req! { TupleFromRequest3; A, B, C }\n    tuple_from_req! { TupleFromRequest4; A, B, C, D }\n    tuple_from_req! { TupleFromRequest5; A, B, C, D, E }\n    tuple_from_req! { TupleFromRequest6; A, B, C, D, E, F }\n    tuple_from_req! { TupleFromRequest7; A, B, C, D, E, F, G }\n    tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }\n    tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }\n    tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }\n    tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }\n    tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }\n    tuple_from_req! { TupleFromRequest13; A, B, C, D, E, F, G, H, I, J, K, L, M }\n    tuple_from_req! { TupleFromRequest14; A, B, C, D, E, F, G, H, I, J, K, L, M, N }\n    tuple_from_req! { TupleFromRequest15; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O }\n    tuple_from_req! { TupleFromRequest16; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::header;\n    use bytes::Bytes;\n    use serde::Deserialize;\n\n    use super::*;\n    use crate::{\n        test::TestRequest,\n        types::{Form, FormConfig},\n    };\n\n    #[derive(Deserialize, Debug, PartialEq)]\n    struct Info {\n        hello: String,\n    }\n\n    #[actix_rt::test]\n    async fn test_option() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .data(FormConfig::default().limit(4096))\n            .to_http_parts();\n\n        let r = Option::<Form<Info>>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(r, None);\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((header::CONTENT_LENGTH, \"9\"))\n            .set_payload(Bytes::from_static(b\"hello=world\"))\n            .to_http_parts();\n\n        let r = Option::<Form<Info>>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(\n            r,\n            Some(Form(Info {\n                hello: \"world\".into()\n            }))\n        );\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((header::CONTENT_LENGTH, \"9\"))\n            .set_payload(Bytes::from_static(b\"bye=world\"))\n            .to_http_parts();\n\n        let r = Option::<Form<Info>>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(r, None);\n    }\n\n    #[actix_rt::test]\n    async fn test_result() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((header::CONTENT_LENGTH, \"11\"))\n            .set_payload(Bytes::from_static(b\"hello=world\"))\n            .to_http_parts();\n\n        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)\n            .await\n            .unwrap()\n            .unwrap();\n        assert_eq!(\n            r,\n            Form(Info {\n                hello: \"world\".into()\n            })\n        );\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((header::CONTENT_LENGTH, 9))\n            .set_payload(Bytes::from_static(b\"bye=world\"))\n            .to_http_parts();\n\n        struct MyError;\n        impl From<Error> for MyError {\n            fn from(_: Error) -> Self {\n                Self\n            }\n        }\n\n        let r = Result::<Form<Info>, MyError>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert!(r.is_err());\n    }\n\n    #[actix_rt::test]\n    async fn test_uri() {\n        let req = TestRequest::default().uri(\"/foo/bar\").to_http_request();\n        let uri = Uri::extract(&req).await.unwrap();\n        assert_eq!(uri.path(), \"/foo/bar\");\n    }\n\n    #[actix_rt::test]\n    async fn test_method() {\n        let req = TestRequest::default().method(Method::GET).to_http_request();\n        let method = Method::extract(&req).await.unwrap();\n        assert_eq!(method, Method::GET);\n    }\n\n    #[actix_rt::test]\n    async fn test_concurrent() {\n        let (req, mut pl) = TestRequest::default()\n            .uri(\"/foo/bar\")\n            .method(Method::GET)\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((header::CONTENT_LENGTH, \"11\"))\n            .set_payload(Bytes::from_static(b\"hello=world\"))\n            .to_http_parts();\n        let (method, uri, form) = <(Method, Uri, Form<Info>)>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(method, Method::GET);\n        assert_eq!(uri.path(), \"/foo/bar\");\n        assert_eq!(\n            form,\n            Form(Info {\n                hello: \"world\".into()\n            })\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/guard/acceptable.rs",
    "content": "use super::{Guard, GuardContext};\nuse crate::http::header::Accept;\n\n/// A guard that verifies that an `Accept` header is present and it contains a compatible MIME type.\n///\n/// An exception is that matching `*/*` must be explicitly enabled because most browsers send this\n/// as part of their `Accept` header for almost every request.\n///\n/// # Examples\n/// ```\n/// use actix_web::{guard::Acceptable, web, HttpResponse};\n///\n/// web::resource(\"/images\")\n///     .guard(Acceptable::new(mime::IMAGE_STAR))\n///     .default_service(web::to(|| async {\n///         HttpResponse::Ok().body(\"only called when images responses are acceptable\")\n///     }));\n/// ```\n#[derive(Debug, Clone)]\npub struct Acceptable {\n    mime: mime::Mime,\n\n    /// Whether to match `*/*` mime type.\n    ///\n    /// Defaults to false because it's not very useful otherwise.\n    match_star_star: bool,\n}\n\nimpl Acceptable {\n    /// Constructs new `Acceptable` guard with the given `mime` type/pattern.\n    pub fn new(mime: mime::Mime) -> Self {\n        Self {\n            mime,\n            match_star_star: false,\n        }\n    }\n\n    /// Allows `*/*` in the `Accept` header to pass the guard check.\n    pub fn match_star_star(mut self) -> Self {\n        self.match_star_star = true;\n        self\n    }\n}\n\nimpl Guard for Acceptable {\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        let accept = match ctx.header::<Accept>() {\n            Some(hdr) => hdr,\n            None => return false,\n        };\n\n        let target_type = self.mime.type_();\n        let target_subtype = self.mime.subtype();\n\n        for mime in accept.0.into_iter().map(|q| q.item) {\n            return match (mime.type_(), mime.subtype()) {\n                (typ, subtype) if typ == target_type && subtype == target_subtype => true,\n                (typ, mime::STAR) if typ == target_type => true,\n                (mime::STAR, mime::STAR) if self.match_star_star => true,\n                _ => continue,\n            };\n        }\n\n        false\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        if self.match_star_star {\n            format!(\"Acceptable({}, match_star_star=true)\", self.mime)\n        } else {\n            format!(\"Acceptable({})\", self.mime)\n        }\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<super::GuardDetail>> {\n        let mut details = Vec::new();\n        details.push(super::GuardDetail::Generic(format!(\"mime={}\", self.mime)));\n        if self.match_star_star {\n            details.push(super::GuardDetail::Generic(\n                \"match_star_star=true\".to_string(),\n            ));\n        }\n        Some(details)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{http::header, test::TestRequest};\n\n    #[test]\n    fn test_acceptable() {\n        let req = TestRequest::default().to_srv_request();\n        assert!(!Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .insert_header((header::ACCEPT, \"application/json\"))\n            .to_srv_request();\n        assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .insert_header((header::ACCEPT, \"text/html, application/json\"))\n            .to_srv_request();\n        assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn test_acceptable_star() {\n        let req = TestRequest::default()\n            .insert_header((header::ACCEPT, \"text/html, */*;q=0.8\"))\n            .to_srv_request();\n\n        assert!(Acceptable::new(mime::APPLICATION_JSON)\n            .match_star_star()\n            .check(&req.guard_ctx()));\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    #[test]\n    fn acceptable_guard_details_include_mime() {\n        let guard = Acceptable::new(mime::APPLICATION_JSON).match_star_star();\n        let details = guard.details().expect(\"missing guard details\");\n\n        assert!(details.iter().any(|detail| match detail {\n            crate::guard::GuardDetail::Generic(value) => value == \"match_star_star=true\",\n            _ => false,\n        }));\n        let expected = format!(\"mime={}\", mime::APPLICATION_JSON);\n        assert!(details.iter().any(|detail| match detail {\n            crate::guard::GuardDetail::Generic(value) => value == &expected,\n            _ => false,\n        }));\n        assert_eq!(\n            guard.name(),\n            format!(\n                \"Acceptable({}, match_star_star=true)\",\n                mime::APPLICATION_JSON\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/guard/host.rs",
    "content": "use actix_http::{header, uri::Uri, RequestHead, Version};\n\nuse super::{Guard, GuardContext};\n\n/// Creates a guard that matches requests targeting a specific host.\n///\n/// # Matching Host\n/// This guard will:\n/// - match against the `Host` header, if present;\n/// - fall-back to matching against the request target's host, if present;\n/// - return false if host cannot be determined;\n///\n/// # Matching Scheme\n/// Optionally, this guard can match against the host's scheme. Set the scheme for matching using\n/// `Host(host).scheme(protocol)`. If the request's scheme cannot be determined, it will not prevent\n/// the guard from matching successfully.\n///\n/// # Examples\n/// The `Host` guard can be used to set up a form of [virtual hosting] within a single app.\n/// Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard\n/// definitions they become safe to use in this way. Without these host guards, only routes under\n/// the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1`\n/// and `localhost` as the `Host` guards.\n/// ```\n/// use actix_web::{web, http::Method, guard, App, HttpResponse};\n///\n/// App::new()\n///     .service(\n///         web::scope(\"\")\n///             .guard(guard::Host(\"www.rust-lang.org\"))\n///             .default_service(web::to(|| async {\n///                 HttpResponse::Ok().body(\"marketing site\")\n///             })),\n///     )\n///     .service(\n///         web::scope(\"\")\n///             .guard(guard::Host(\"play.rust-lang.org\"))\n///             .default_service(web::to(|| async {\n///                 HttpResponse::Ok().body(\"playground frontend\")\n///             })),\n///     );\n/// ```\n///\n/// The example below additionally guards on the host URI's scheme. This could allow routing to\n/// different handlers for `http:` vs `https:` visitors; to redirect, for example.\n/// ```\n/// use actix_web::{web, guard::Host, HttpResponse};\n///\n/// web::scope(\"/admin\")\n///     .guard(Host(\"admin.rust-lang.org\").scheme(\"https\"))\n///     .default_service(web::to(|| async {\n///         HttpResponse::Ok().body(\"admin connection is secure\")\n///     }));\n/// ```\n///\n/// [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting\n#[allow(non_snake_case)]\npub fn Host(host: impl AsRef<str>) -> HostGuard {\n    HostGuard {\n        host: host.as_ref().to_string(),\n        scheme: None,\n    }\n}\n\nfn get_host_uri(req: &RequestHead) -> Option<Uri> {\n    req.headers\n        .get(header::HOST)\n        .and_then(|host_value| host_value.to_str().ok())\n        .filter(|_| req.version < Version::HTTP_2)\n        .or_else(|| req.uri.host())\n        .and_then(|host| host.parse().ok())\n}\n\n#[doc(hidden)]\npub struct HostGuard {\n    host: String,\n    scheme: Option<String>,\n}\n\nimpl HostGuard {\n    /// Set request scheme to match\n    pub fn scheme<H: AsRef<str>>(mut self, scheme: H) -> HostGuard {\n        self.scheme = Some(scheme.as_ref().to_string());\n        self\n    }\n}\n\nimpl Guard for HostGuard {\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        // parse host URI from header or request target\n        let req_host_uri = match get_host_uri(ctx.head()) {\n            Some(uri) => uri,\n\n            // no match if host cannot be determined\n            None => return false,\n        };\n\n        match req_host_uri.host() {\n            // fall through to scheme checks\n            Some(uri_host) if self.host == uri_host => {}\n\n            // Either:\n            // - request's host does not match guard's host;\n            // - It was possible that the parsed URI from request target did not contain a host.\n            _ => return false,\n        }\n\n        if let Some(ref scheme) = self.scheme {\n            if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() {\n                return scheme == req_host_uri_scheme;\n            }\n\n            // TODO: is this the correct behavior?\n            // falls through if scheme cannot be determined\n        }\n\n        // all conditions passed\n        true\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        if let Some(ref scheme) = self.scheme {\n            format!(\"Host({}, scheme={})\", self.host, scheme)\n        } else {\n            format!(\"Host({})\", self.host)\n        }\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<super::GuardDetail>> {\n        let mut details = vec![super::GuardDetail::Headers(vec![(\n            \"host\".to_string(),\n            self.host.clone(),\n        )])];\n\n        if let Some(ref scheme) = self.scheme {\n            details.push(super::GuardDetail::Generic(format!(\"scheme={scheme}\")));\n        }\n\n        Some(details)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test::TestRequest;\n\n    #[test]\n    fn host_not_from_header_if_http2() {\n        let req = TestRequest::default()\n            .uri(\"www.rust-lang.org\")\n            .insert_header((\n                header::HOST,\n                header::HeaderValue::from_static(\"www.example.com\"),\n            ))\n            .to_srv_request();\n\n        let host = Host(\"www.example.com\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .version(actix_http::Version::HTTP_2)\n            .uri(\"www.rust-lang.org\")\n            .insert_header((\n                header::HOST,\n                header::HeaderValue::from_static(\"www.example.com\"),\n            ))\n            .to_srv_request();\n\n        let host = Host(\"www.example.com\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\");\n        assert!(host.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn host_from_header() {\n        let req = TestRequest::default()\n            .insert_header((\n                header::HOST,\n                header::HeaderValue::from_static(\"www.rust-lang.org\"),\n            ))\n            .to_srv_request();\n\n        let host = Host(\"www.rust-lang.org\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\").scheme(\"https\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\").scheme(\"https\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"crates.io\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"localhost\");\n        assert!(!host.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn host_without_header() {\n        let req = TestRequest::default()\n            .uri(\"www.rust-lang.org\")\n            .to_srv_request();\n\n        let host = Host(\"www.rust-lang.org\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\").scheme(\"https\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\").scheme(\"https\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"crates.io\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"localhost\");\n        assert!(!host.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn host_scheme() {\n        let req = TestRequest::default()\n            .insert_header((\n                header::HOST,\n                header::HeaderValue::from_static(\"https://www.rust-lang.org\"),\n            ))\n            .to_srv_request();\n\n        let host = Host(\"www.rust-lang.org\").scheme(\"https\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\");\n        assert!(host.check(&req.guard_ctx()));\n\n        let host = Host(\"www.rust-lang.org\").scheme(\"http\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"blog.rust-lang.org\").scheme(\"https\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"crates.io\").scheme(\"https\");\n        assert!(!host.check(&req.guard_ctx()));\n\n        let host = Host(\"localhost\");\n        assert!(!host.check(&req.guard_ctx()));\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    #[test]\n    fn host_guard_details_include_host_and_scheme() {\n        let host = Host(\"example.com\").scheme(\"https\");\n        let details = host.details().expect(\"missing guard details\");\n\n        assert!(details.iter().any(|detail| match detail {\n            crate::guard::GuardDetail::Headers(headers) => headers\n                .iter()\n                .any(|(name, value)| name == \"host\" && value == \"example.com\"),\n            _ => false,\n        }));\n        assert!(details.iter().any(|detail| match detail {\n            crate::guard::GuardDetail::Generic(value) => value == \"scheme=https\",\n            _ => false,\n        }));\n        assert_eq!(host.name(), \"Host(example.com, scheme=https)\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/guard/mod.rs",
    "content": "//! Route guards.\n//!\n//! Guards are used during routing to help select a matching service or handler using some aspect of\n//! the request; though guards should not be used for path matching since it is a built-in function\n//! of the Actix Web router.\n//!\n//! Guards can be used on [`Scope`]s, [`Resource`]s, [`Route`]s, and other custom services.\n//!\n//! Fundamentally, a guard is a predicate function that receives a reference to a request context\n//! object and returns a boolean; true if the request _should_ be handled by the guarded service\n//! or handler. This interface is defined by the [`Guard`] trait.\n//!\n//! Commonly-used guards are provided in this module as well as a way of creating a guard from a\n//! closure ([`fn_guard`]). The [`Not`], [`Any()`], and [`All()`] guards are noteworthy, as they can be\n//! used to compose other guards in a more flexible and semantic way than calling `.guard(...)` on\n//! services multiple times (which might have different combining behavior than you want).\n//!\n//! There are shortcuts for routes with method guards in the [`web`](crate::web) module:\n//! [`web::get()`](crate::web::get), [`web::post()`](crate::web::post), etc. The routes created by\n//! the following calls are equivalent:\n//!\n//! - `web::get()` (recommended form)\n//! - `web::route().guard(guard::Get())`\n//!\n//! Guards can not modify anything about the request. However, it is possible to store extra\n//! attributes in the request-local data container obtained with [`GuardContext::req_data_mut`].\n//!\n//! Guards can prevent resource definitions from overlapping which, when only considering paths,\n//! would result in inaccessible routes. See the [`Host`] guard for an example of virtual hosting.\n//!\n//! # Examples\n//!\n//! In the following code, the `/guarded` resource has one defined route whose handler will only be\n//! called if the request method is GET or POST and there is a `x-guarded` request header with value\n//! equal to `secret`.\n//!\n//! ```\n//! use actix_web::{web, http::Method, guard, HttpResponse};\n//!\n//! web::resource(\"/guarded\").route(\n//!     web::route()\n//!         .guard(guard::Any(guard::Get()).or(guard::Post()))\n//!         .guard(guard::Header(\"x-guarded\", \"secret\"))\n//!         .to(|| HttpResponse::Ok())\n//! );\n//! ```\n//!\n//! [`Scope`]: crate::Scope::guard()\n//! [`Resource`]: crate::Resource::guard()\n//! [`Route`]: crate::Route::guard()\n\nuse std::{\n    cell::{Ref, RefMut},\n    rc::Rc,\n};\n\nuse actix_http::{header, Extensions, Method as HttpMethod, RequestHead};\n\nuse crate::{http::header::Header, service::ServiceRequest, HttpMessage as _};\n\nmod acceptable;\nmod host;\n\npub use self::{\n    acceptable::Acceptable,\n    host::{Host, HostGuard},\n};\n\n/// Enum to encapsulate various introspection details of a guard.\n#[cfg(feature = \"experimental-introspection\")]\n#[non_exhaustive]\n#[derive(Debug, Clone)]\npub enum GuardDetail {\n    /// Detail associated with explicit HTTP method guards.\n    HttpMethods(Vec<String>),\n    /// Detail associated with headers (header, value).\n    Headers(Vec<(String, String)>),\n    /// Generic detail, typically used for compound guard representations.\n    Generic(String),\n}\n\n/// Provides access to request parts that are useful during routing.\n#[derive(Debug)]\npub struct GuardContext<'a> {\n    pub(crate) req: &'a ServiceRequest,\n}\n\nimpl<'a> GuardContext<'a> {\n    /// Returns reference to the request head.\n    #[inline]\n    pub fn head(&self) -> &RequestHead {\n        self.req.head()\n    }\n\n    /// Returns reference to the request-local data/extensions container.\n    #[inline]\n    pub fn req_data(&self) -> Ref<'a, Extensions> {\n        self.req.extensions()\n    }\n\n    /// Returns mutable reference to the request-local data/extensions container.\n    #[inline]\n    pub fn req_data_mut(&self) -> RefMut<'a, Extensions> {\n        self.req.extensions_mut()\n    }\n\n    /// Extracts a typed header from the request.\n    ///\n    /// Returns `None` if parsing `H` fails.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{guard::fn_guard, http::header};\n    ///\n    /// let image_accept_guard = fn_guard(|ctx| {\n    ///     match ctx.header::<header::Accept>() {\n    ///         Some(hdr) => hdr.preference() == \"image/*\",\n    ///         None => false,\n    ///     }\n    /// });\n    /// ```\n    #[inline]\n    pub fn header<H: Header>(&self) -> Option<H> {\n        H::parse(self.req).ok()\n    }\n\n    /// Counterpart to [HttpRequest::app_data](crate::HttpRequest::app_data).\n    #[inline]\n    pub fn app_data<T: 'static>(&self) -> Option<&T> {\n        self.req.app_data()\n    }\n}\n\n/// Interface for routing guards.\n///\n/// See [module level documentation](self) for more.\npub trait Guard {\n    /// Returns true if predicate condition is met for a given request.\n    fn check(&self, ctx: &GuardContext<'_>) -> bool;\n\n    /// Returns a nominal representation of the guard.\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        std::any::type_name::<Self>().to_string()\n    }\n\n    /// Returns detailed introspection information, when available.\n    ///\n    /// This is best-effort and may omit complex guard logic.\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        None\n    }\n}\n\nimpl Guard for Rc<dyn Guard> {\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        (**self).check(ctx)\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        (**self).name()\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        (**self).details()\n    }\n}\n\n/// Creates a guard using the given function.\n///\n/// # Examples\n/// ```\n/// use actix_web::{guard, web, HttpResponse};\n///\n/// web::route()\n///     .guard(guard::fn_guard(|ctx| {\n///         ctx.head().headers().contains_key(\"content-type\")\n///     }))\n///     .to(|| HttpResponse::Ok());\n/// ```\npub fn fn_guard<F>(f: F) -> impl Guard\nwhere\n    F: Fn(&GuardContext<'_>) -> bool,\n{\n    FnGuard(f)\n}\n\nstruct FnGuard<F: Fn(&GuardContext<'_>) -> bool>(F);\n\nimpl<F> Guard for FnGuard<F>\nwhere\n    F: Fn(&GuardContext<'_>) -> bool,\n{\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        (self.0)(ctx)\n    }\n}\n\nimpl<F> Guard for F\nwhere\n    F: Fn(&GuardContext<'_>) -> bool,\n{\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        (self)(ctx)\n    }\n}\n\n/// Creates a guard that matches if any added guards match.\n///\n/// # Examples\n/// The handler below will be called for either request method `GET` or `POST`.\n/// ```\n/// use actix_web::{web, guard, HttpResponse};\n///\n/// web::route()\n///     .guard(\n///         guard::Any(guard::Get())\n///             .or(guard::Post()))\n///     .to(|| HttpResponse::Ok());\n/// ```\n#[allow(non_snake_case)]\npub fn Any<F: Guard + 'static>(guard: F) -> AnyGuard {\n    AnyGuard {\n        guards: vec![Box::new(guard)],\n    }\n}\n\n/// A collection of guards that match if the disjunction of their `check` outcomes is true.\n///\n/// That is, only one contained guard needs to match in order for the aggregate guard to match.\n///\n/// Construct an `AnyGuard` using [`Any()`].\npub struct AnyGuard {\n    guards: Vec<Box<dyn Guard>>,\n}\n\nimpl AnyGuard {\n    /// Adds new guard to the collection of guards to check.\n    pub fn or<F: Guard + 'static>(mut self, guard: F) -> Self {\n        self.guards.push(Box::new(guard));\n        self\n    }\n}\n\nimpl Guard for AnyGuard {\n    #[inline]\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        for guard in &self.guards {\n            if guard.check(ctx) {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        format!(\n            \"AnyGuard({})\",\n            self.guards\n                .iter()\n                .map(|g| g.name())\n                .collect::<Vec<_>>()\n                .join(\", \")\n        )\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        Some(\n            self.guards\n                .iter()\n                .flat_map(|g| g.details().unwrap_or_default())\n                .collect(),\n        )\n    }\n}\n\n/// Creates a guard that matches if all added guards match.\n///\n/// # Examples\n/// The handler below will only be called if the request method is `GET` **and** the specified\n/// header name and value match exactly.\n/// ```\n/// use actix_web::{guard, web, HttpResponse};\n///\n/// web::route()\n///     .guard(\n///         guard::All(guard::Get())\n///             .and(guard::Header(\"accept\", \"text/plain\"))\n///     )\n///     .to(|| HttpResponse::Ok());\n/// ```\n#[allow(non_snake_case)]\npub fn All<F: Guard + 'static>(guard: F) -> AllGuard {\n    AllGuard {\n        guards: vec![Box::new(guard)],\n    }\n}\n\n/// A collection of guards that match if the conjunction of their `check` outcomes is true.\n///\n/// That is, **all** contained guard needs to match in order for the aggregate guard to match.\n///\n/// Construct an `AllGuard` using [`All()`].\npub struct AllGuard {\n    guards: Vec<Box<dyn Guard>>,\n}\n\nimpl AllGuard {\n    /// Adds new guard to the collection of guards to check.\n    pub fn and<F: Guard + 'static>(mut self, guard: F) -> Self {\n        self.guards.push(Box::new(guard));\n        self\n    }\n}\n\nimpl Guard for AllGuard {\n    #[inline]\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        for guard in &self.guards {\n            if !guard.check(ctx) {\n                return false;\n            }\n        }\n\n        true\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        format!(\n            \"AllGuard({})\",\n            self.guards\n                .iter()\n                .map(|g| g.name())\n                .collect::<Vec<_>>()\n                .join(\", \")\n        )\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        Some(\n            self.guards\n                .iter()\n                .flat_map(|g| g.details().unwrap_or_default())\n                .collect(),\n        )\n    }\n}\n\n/// Wraps a guard and inverts the outcome of its `Guard` implementation.\n///\n/// # Examples\n/// The handler below will be called for any request method apart from `GET`.\n/// ```\n/// use actix_web::{guard, web, HttpResponse};\n///\n/// web::route()\n///     .guard(guard::Not(guard::Get()))\n///     .to(|| HttpResponse::Ok());\n/// ```\npub struct Not<G>(pub G);\n\nimpl<G: Guard> Guard for Not<G> {\n    #[inline]\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        !self.0.check(ctx)\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        format!(\"Not({})\", self.0.name())\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        Some(vec![GuardDetail::Generic(self.name())])\n    }\n}\n\n/// Creates a guard that matches a specified HTTP method.\n#[allow(non_snake_case)]\npub fn Method(method: HttpMethod) -> impl Guard {\n    MethodGuard(method)\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct RegisteredMethods(pub(crate) Vec<HttpMethod>);\n\n/// HTTP method guard.\n#[derive(Debug)]\npub(crate) struct MethodGuard(HttpMethod);\n\nimpl Guard for MethodGuard {\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        let registered = ctx.req_data_mut().remove::<RegisteredMethods>();\n\n        if let Some(mut methods) = registered {\n            methods.0.push(self.0.clone());\n            ctx.req_data_mut().insert(methods);\n        } else {\n            ctx.req_data_mut()\n                .insert(RegisteredMethods(vec![self.0.clone()]));\n        }\n\n        ctx.head().method == self.0\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        self.0.to_string()\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        Some(vec![GuardDetail::HttpMethods(vec![self.0.to_string()])])\n    }\n}\n\nmacro_rules! method_guard {\n    ($method_fn:ident, $method_const:ident) => {\n        #[doc = concat!(\"Creates a guard that matches the `\", stringify!($method_const), \"` request method.\")]\n        ///\n        /// # Examples\n        #[doc = concat!(\"The route in this example will only respond to `\", stringify!($method_const), \"` requests.\")]\n        /// ```\n        /// use actix_web::{guard, web, HttpResponse};\n        ///\n        /// web::route()\n        #[doc = concat!(\"    .guard(guard::\", stringify!($method_fn), \"())\")]\n        ///     .to(|| HttpResponse::Ok());\n        /// ```\n        #[allow(non_snake_case)]\n        pub fn $method_fn() -> impl Guard {\n            MethodGuard(HttpMethod::$method_const)\n        }\n    };\n}\n\nmethod_guard!(Get, GET);\nmethod_guard!(Post, POST);\nmethod_guard!(Put, PUT);\nmethod_guard!(Delete, DELETE);\nmethod_guard!(Head, HEAD);\nmethod_guard!(Options, OPTIONS);\nmethod_guard!(Connect, CONNECT);\nmethod_guard!(Patch, PATCH);\nmethod_guard!(Trace, TRACE);\n\n/// Creates a guard that matches if request contains given header name and value.\n///\n/// # Examples\n/// The handler below will be called when the request contains an `x-guarded` header with value\n/// equal to `secret`.\n/// ```\n/// use actix_web::{guard, web, HttpResponse};\n///\n/// web::route()\n///     .guard(guard::Header(\"x-guarded\", \"secret\"))\n///     .to(|| HttpResponse::Ok());\n/// ```\n#[allow(non_snake_case)]\npub fn Header(name: &'static str, value: &'static str) -> impl Guard {\n    HeaderGuard(\n        header::HeaderName::try_from(name).unwrap(),\n        header::HeaderValue::from_static(value),\n    )\n}\n\nstruct HeaderGuard(header::HeaderName, header::HeaderValue);\n\nimpl Guard for HeaderGuard {\n    fn check(&self, ctx: &GuardContext<'_>) -> bool {\n        if let Some(val) = ctx.head().headers.get(&self.0) {\n            return val == self.1;\n        }\n\n        false\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn name(&self) -> String {\n        format!(\"Header({}, {})\", self.0, self.1.to_str().unwrap_or(\"\"))\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    fn details(&self) -> Option<Vec<GuardDetail>> {\n        Some(vec![GuardDetail::Headers(vec![(\n            self.0.to_string(),\n            self.1.to_str().unwrap_or(\"\").to_string(),\n        )])])\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::Method;\n\n    use super::*;\n    use crate::test::TestRequest;\n\n    #[test]\n    fn header_match() {\n        let req = TestRequest::default()\n            .insert_header((header::TRANSFER_ENCODING, \"chunked\"))\n            .to_srv_request();\n\n        let hdr = Header(\"transfer-encoding\", \"chunked\");\n        assert!(hdr.check(&req.guard_ctx()));\n\n        let hdr = Header(\"transfer-encoding\", \"other\");\n        assert!(!hdr.check(&req.guard_ctx()));\n\n        let hdr = Header(\"content-type\", \"chunked\");\n        assert!(!hdr.check(&req.guard_ctx()));\n\n        let hdr = Header(\"content-type\", \"other\");\n        assert!(!hdr.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn method_guards() {\n        let get_req = TestRequest::get().to_srv_request();\n        let post_req = TestRequest::post().to_srv_request();\n\n        assert!(Get().check(&get_req.guard_ctx()));\n        assert!(!Get().check(&post_req.guard_ctx()));\n\n        assert!(Post().check(&post_req.guard_ctx()));\n        assert!(!Post().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::put().to_srv_request();\n        assert!(Put().check(&req.guard_ctx()));\n        assert!(!Put().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::patch().to_srv_request();\n        assert!(Patch().check(&req.guard_ctx()));\n        assert!(!Patch().check(&get_req.guard_ctx()));\n\n        let r = TestRequest::delete().to_srv_request();\n        assert!(Delete().check(&r.guard_ctx()));\n        assert!(!Delete().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::default().method(Method::HEAD).to_srv_request();\n        assert!(Head().check(&req.guard_ctx()));\n        assert!(!Head().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .method(Method::OPTIONS)\n            .to_srv_request();\n        assert!(Options().check(&req.guard_ctx()));\n        assert!(!Options().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .method(Method::CONNECT)\n            .to_srv_request();\n        assert!(Connect().check(&req.guard_ctx()));\n        assert!(!Connect().check(&get_req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .method(Method::TRACE)\n            .to_srv_request();\n        assert!(Trace().check(&req.guard_ctx()));\n        assert!(!Trace().check(&get_req.guard_ctx()));\n    }\n\n    #[test]\n    fn aggregate_any() {\n        let req = TestRequest::default()\n            .method(Method::TRACE)\n            .to_srv_request();\n\n        assert!(Any(Trace()).check(&req.guard_ctx()));\n        assert!(Any(Trace()).or(Get()).check(&req.guard_ctx()));\n        assert!(!Any(Get()).or(Get()).check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn aggregate_all() {\n        let req = TestRequest::default()\n            .method(Method::TRACE)\n            .to_srv_request();\n\n        assert!(All(Trace()).check(&req.guard_ctx()));\n        assert!(All(Trace()).and(Trace()).check(&req.guard_ctx()));\n        assert!(!All(Trace()).and(Get()).check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn nested_not() {\n        let req = TestRequest::default().to_srv_request();\n\n        let get = Get();\n        assert!(get.check(&req.guard_ctx()));\n\n        let not_get = Not(get);\n        assert!(!not_get.check(&req.guard_ctx()));\n\n        let not_not_get = Not(not_get);\n        assert!(not_not_get.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn function_guard() {\n        let domain = \"rust-lang.org\".to_owned();\n        let guard = fn_guard(|ctx| ctx.head().uri.host().unwrap().ends_with(&domain));\n\n        let req = TestRequest::default()\n            .uri(\"blog.rust-lang.org\")\n            .to_srv_request();\n        assert!(guard.check(&req.guard_ctx()));\n\n        let req = TestRequest::default().uri(\"crates.io\").to_srv_request();\n        assert!(!guard.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn mega_nesting() {\n        let guard = fn_guard(|ctx| All(Not(Any(Not(Trace())))).check(ctx));\n\n        let req = TestRequest::default().to_srv_request();\n        assert!(!guard.check(&req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .method(Method::TRACE)\n            .to_srv_request();\n        assert!(guard.check(&req.guard_ctx()));\n    }\n\n    #[test]\n    fn app_data() {\n        const TEST_VALUE: u32 = 42;\n        let guard = fn_guard(|ctx| ctx.app_data::<u32>() == Some(&TEST_VALUE));\n\n        let req = TestRequest::default().app_data(TEST_VALUE).to_srv_request();\n        assert!(guard.check(&req.guard_ctx()));\n\n        let req = TestRequest::default()\n            .app_data(TEST_VALUE * 2)\n            .to_srv_request();\n        assert!(!guard.check(&req.guard_ctx()));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/handler.rs",
    "content": "use std::future::Future;\n\nuse actix_service::{boxed, fn_service};\n\nuse crate::{\n    service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse},\n    FromRequest, HttpResponse, Responder,\n};\n\n/// The interface for request handlers.\n///\n/// # What Is A Request Handler\n///\n/// In short, a handler is just an async function that receives request-based arguments, in any\n/// order, and returns something that can be converted to a response.\n///\n/// In particular, a request handler has three requirements:\n///\n/// 1. It is an async function (or a function/closure that returns an appropriate future);\n/// 1. The function parameters (up to 12) implement [`FromRequest`];\n/// 1. The async function (or future) resolves to a type that can be converted into an\n///    [`HttpResponse`] (i.e., it implements the [`Responder`] trait).\n///\n///\n/// # Compiler Errors\n///\n/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not\n/// fulfill the _first_ of the above requirements. (It could also mean that you're attempting to use\n/// a macro-routed handler in a manual routing context like `web::get().to(handler)`, which is not\n/// supported). Breaking the other requirements manifests as errors on implementing [`FromRequest`]\n/// and [`Responder`], respectively.\n///\n/// # How Do Handlers Receive Variable Numbers Of Arguments\n///\n/// Rest assured there is no macro magic here; it's just traits.\n///\n/// The first thing to note is that [`FromRequest`] is implemented for tuples (up to 12 in length).\n///\n/// Secondly, the `Handler` trait is implemented for functions (up to an [arity] of 12) in a way\n/// that aligns their parameter positions with a corresponding tuple of types (becoming the `Args`\n/// type parameter for this trait).\n///\n/// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the\n/// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on\n/// that tuple, and the `Handler::call` implementation for that particular function arity\n/// destructures the tuple into its component types and calls your handler function with them.\n///\n/// In pseudo-code the process looks something like this:\n///\n/// ```ignore\n/// async fn my_handler(body: String, state: web::Data<MyState>) -> impl Responder {\n///     ...\n/// }\n///\n/// // the function params above described as a tuple, names do not matter, only position\n/// type InferredMyHandlerArgs = (String, web::Data<MyState>);\n///\n/// // create tuple of arguments to be passed to handler\n/// let args = InferredMyHandlerArgs::from_request(&request, &payload).await;\n///\n/// // call handler with argument tuple\n/// let response = Handler::call(&my_handler, args).await;\n///\n/// // which is effectively...\n///\n/// let (body, state) = args;\n/// let response = my_handler(body, state).await;\n/// ```\n///\n/// This is the source code for the 2-parameter implementation of `Handler` to help illustrate the\n/// bounds of the handler call after argument extraction:\n/// ```ignore\n/// impl<Func, Fut, Arg1, Arg2> Handler<(Arg1, Arg2)> for Func\n/// where\n///     Func: Fn(Arg1, Arg2) -> Fut + Clone + 'static,\n///     Fut: Future,\n/// {\n///     type Output = Fut::Output;\n///     type Future = Fut;\n///\n///     fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> Self::Future {\n///         (self)(arg1, arg2)\n///     }\n/// }\n/// ```\n///\n/// [arity]: https://en.wikipedia.org/wiki/Arity\n/// [`from_request`]: FromRequest::from_request\npub trait Handler<Args>: Clone + 'static {\n    type Output;\n    type Future: Future<Output = Self::Output>;\n\n    fn call(&self, args: Args) -> Self::Future;\n}\n\npub(crate) fn handler_service<F, Args>(handler: F) -> BoxedHttpServiceFactory\nwhere\n    F: Handler<Args>,\n    Args: FromRequest,\n    F::Output: Responder,\n{\n    boxed::factory(fn_service(move |req: ServiceRequest| {\n        let handler = handler.clone();\n\n        async move {\n            let (req, mut payload) = req.into_parts();\n\n            let res = match Args::from_request(&req, &mut payload).await {\n                Err(err) => HttpResponse::from_error(err),\n\n                Ok(data) => handler\n                    .call(data)\n                    .await\n                    .respond_to(&req)\n                    .map_into_boxed_body(),\n            };\n\n            Ok(ServiceResponse::new(req, res))\n        }\n    }))\n}\n\n/// Generates a [`Handler`] trait impl for N-ary functions where N is specified with a sequence of\n/// space separated type parameters.\n///\n/// # Examples\n/// ```ignore\n/// factory_tuple! {}         // implements Handler for types: fn() -> R\n/// factory_tuple! { A B C }  // implements Handler for types: fn(A, B, C) -> R\n/// ```\nmacro_rules! factory_tuple ({ $($param:ident)* } => {\n    impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func\n    where\n        Func: Fn($($param),*) -> Fut + Clone + 'static,\n        Fut: Future,\n    {\n        type Output = Fut::Output;\n        type Future = Fut;\n\n        #[inline]\n        #[allow(non_snake_case)]\n        fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future {\n            (self)($($param,)*)\n        }\n    }\n});\n\nfactory_tuple! {}\nfactory_tuple! { A }\nfactory_tuple! { A B }\nfactory_tuple! { A B C }\nfactory_tuple! { A B C D }\nfactory_tuple! { A B C D E }\nfactory_tuple! { A B C D E F }\nfactory_tuple! { A B C D E F G }\nfactory_tuple! { A B C D E F G H }\nfactory_tuple! { A B C D E F G H I }\nfactory_tuple! { A B C D E F G H I J }\nfactory_tuple! { A B C D E F G H I J K }\nfactory_tuple! { A B C D E F G H I J K L }\nfactory_tuple! { A B C D E F G H I J K L M }\nfactory_tuple! { A B C D E F G H I J K L M N }\nfactory_tuple! { A B C D E F G H I J K L M N O }\nfactory_tuple! { A B C D E F G H I J K L M N O P }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn assert_impl_handler<T: FromRequest>(_: impl Handler<T>) {}\n\n    #[test]\n    fn arg_number() {\n        async fn handler_min() {}\n\n        #[rustfmt::skip]\n        #[allow(clippy::too_many_arguments, clippy::just_underscores_and_digits, clippy::let_unit_value)]\n        async fn handler_max(\n            _01: (), _02: (), _03: (), _04: (), _05: (), _06: (),\n            _07: (), _08: (), _09: (), _10: (), _11: (), _12: (),\n            _13: (), _14: (), _15: (), _16: (),\n        ) {}\n\n        assert_impl_handler(handler_min);\n        assert_impl_handler(handler_max);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/helpers.rs",
    "content": "use std::io;\n\nuse bytes::BufMut;\n\n/// An `io::Write`r that only requires mutable reference and assumes that there is space available\n/// in the buffer for every write operation or that it can be extended implicitly (like\n/// `bytes::BytesMut`, for example).\n///\n/// This is slightly faster (~10%) than `bytes::buf::Writer` in such cases because it does not\n/// perform a remaining length check before writing.\npub(crate) struct MutWriter<'a, B>(pub(crate) &'a mut B);\n\nimpl<B> io::Write for MutWriter<'_, B>\nwhere\n    B: BufMut,\n{\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.0.put_slice(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/accept.rs",
    "content": "use std::cmp::Ordering;\n\nuse mime::Mime;\n\nuse super::{common_header, QualityItem};\nuse crate::http::header;\n\ncommon_header! {\n    /// `Accept` header, defined in [RFC 7231 §5.3.2].\n    ///\n    /// The `Accept` header field can be used by user agents to specify\n    /// response media types that are acceptable. Accept header fields can\n    /// be used to indicate that the request is specifically limited to a\n    /// small set of desired types, as in the case of a request for an\n    /// in-line image\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Accept = #( media-range [ accept-params ] )\n    ///\n    /// media-range    = ( \"*/*\"\n    ///                  / ( type \"/\" \"*\" )\n    ///                  / ( type \"/\" subtype )\n    ///                  ) *( OWS \";\" OWS parameter )\n    /// accept-params  = weight *( accept-ext )\n    /// accept-ext = OWS \";\" OWS token [ \"=\" ( token / quoted-string ) ]\n    /// ```\n    ///\n    /// # Note\n    /// This is a request header. Servers should not send `Accept` in responses; to describe the\n    /// response body media type, use [`ContentType`](super::ContentType) / the `Content-Type`\n    /// header instead.\n    ///\n    /// # Example Values\n    /// * `audio/*; q=0.2, audio/basic`\n    /// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::{Accept, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(Accept(vec![QualityItem::max(mime::TEXT_HTML)]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{Accept, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(Accept(vec![QualityItem::max(mime::APPLICATION_JSON)]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{Accept, Header as _, QualityItem, q}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(Accept(vec![\n    ///         QualityItem::max(mime::TEXT_HTML),\n    ///         QualityItem::max(\"application/xhtml+xml\".parse().unwrap()),\n    ///         QualityItem::new(mime::TEXT_XML, q(0.9)),\n    ///         QualityItem::max(\"image/webp\".parse().unwrap()),\n    ///         QualityItem::new(mime::STAR_STAR, q(0.8)),\n    ///     ]))\n    ///     .to_http_request();\n    ///\n    /// let accept = Accept::parse(&req).unwrap();\n    /// assert_eq!(accept.preference(), mime::TEXT_HTML);\n    /// ```\n    ///\n    /// [RFC 7231 §5.3.2]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    (Accept, header::ACCEPT) => (QualityItem<Mime>)*\n\n    test_parse_and_format {\n        // Tests from the RFC\n         crate::http::header::common_header_test!(\n            test1,\n            [b\"audio/*; q=0.2, audio/basic\"],\n            Some(Accept(vec![\n                QualityItem::new(\"audio/*\".parse().unwrap(), q(0.2)),\n                QualityItem::max(\"audio/basic\".parse().unwrap()),\n                ])));\n\n        crate::http::header::common_header_test!(\n            test2,\n            [b\"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c\"],\n            Some(Accept(vec![\n                QualityItem::new(mime::TEXT_PLAIN, q(0.5)),\n                QualityItem::max(mime::TEXT_HTML),\n                QualityItem::new(\n                    \"text/x-dvi\".parse().unwrap(),\n                    q(0.8)),\n                QualityItem::max(\"text/x-c\".parse().unwrap()),\n                ])));\n\n        // Custom tests\n        crate::http::header::common_header_test!(\n            test3,\n            [b\"text/plain; charset=utf-8\"],\n            Some(Accept(vec![\n                QualityItem::max(mime::TEXT_PLAIN_UTF_8),\n            ])));\n        crate::http::header::common_header_test!(\n            test4,\n            [b\"text/plain; charset=utf-8; q=0.5\"],\n            Some(Accept(vec![\n                QualityItem::new(mime::TEXT_PLAIN_UTF_8, q(0.5)),\n            ])));\n\n        #[test]\n        fn test_fuzzing1() {\n            let req = test::TestRequest::default()\n                .insert_header((header::ACCEPT, \"chunk#;e\"))\n                .finish();\n            let header = Accept::parse(&req);\n            assert!(header.is_ok());\n        }\n    }\n}\n\nimpl Accept {\n    /// Construct `Accept: */*`.\n    pub fn star() -> Accept {\n        Accept(vec![QualityItem::max(mime::STAR_STAR)])\n    }\n\n    /// Construct `Accept: application/json`.\n    pub fn json() -> Accept {\n        Accept(vec![QualityItem::max(mime::APPLICATION_JSON)])\n    }\n\n    /// Construct `Accept: text/*`.\n    pub fn text() -> Accept {\n        Accept(vec![QualityItem::max(mime::TEXT_STAR)])\n    }\n\n    /// Construct `Accept: image/*`.\n    pub fn image() -> Accept {\n        Accept(vec![QualityItem::max(mime::IMAGE_STAR)])\n    }\n\n    /// Construct `Accept: text/html`.\n    pub fn html() -> Accept {\n        Accept(vec![QualityItem::max(mime::TEXT_HTML)])\n    }\n\n    // TODO: method for getting best content encoding based on q-factors, available from server side\n    // and if none are acceptable return None\n\n    /// Extracts the most preferable mime type, accounting for [q-factor weighting].\n    ///\n    /// If no q-factors are provided, the first mime type is chosen. Note that items without\n    /// q-factors are given the maximum preference value.\n    ///\n    /// As per the spec, will return [`mime::STAR_STAR`] (indicating no preference) if the contained\n    /// list is empty.\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn preference(&self) -> Mime {\n        use actix_http::header::Quality;\n\n        let mut max_item = None;\n        let mut max_pref = Quality::ZERO;\n\n        // uses manual max lookup loop since we want the first occurrence in the case of same\n        // preference but `Iterator::max_by_key` would give us the last occurrence\n\n        for pref in &self.0 {\n            // only change if strictly greater\n            // equal items, even while unsorted, still have higher preference if they appear first\n            if pref.quality > max_pref {\n                max_pref = pref.quality;\n                max_item = Some(pref.item.clone());\n            }\n        }\n\n        max_item.unwrap_or(mime::STAR_STAR)\n    }\n\n    /// Returns a sorted list of mime types from highest to lowest preference, accounting for\n    /// [q-factor weighting] and specificity.\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn ranked(&self) -> Vec<Mime> {\n        if self.is_empty() {\n            return vec![];\n        }\n\n        let mut types = self.0.clone();\n\n        // use stable sort so items with equal q-factor and specificity retain listed order\n        types.sort_by(|a, b| {\n            // sort by q-factor descending\n            b.quality.cmp(&a.quality).then_with(|| {\n                // use specificity rules on mime types with\n                // same q-factor (eg. text/html > text/* > */*)\n\n                // subtypes are not comparable if main type is star, so return\n                match (a.item.type_(), b.item.type_()) {\n                    (mime::STAR, mime::STAR) => return Ordering::Equal,\n\n                    // a is sorted after b\n                    (mime::STAR, _) => return Ordering::Greater,\n\n                    // a is sorted before b\n                    (_, mime::STAR) => return Ordering::Less,\n\n                    _ => {}\n                }\n\n                // in both these match expressions, the returned ordering appears\n                // inverted because sort is high-to-low (\"descending\") precedence\n                match (a.item.subtype(), b.item.subtype()) {\n                    (mime::STAR, mime::STAR) => Ordering::Equal,\n\n                    // a is sorted after b\n                    (mime::STAR, _) => Ordering::Greater,\n\n                    // a is sorted before b\n                    (_, mime::STAR) => Ordering::Less,\n\n                    _ => Ordering::Equal,\n                }\n            })\n        });\n\n        types.into_iter().map(|qitem| qitem.item).collect()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::http::header::q;\n\n    #[test]\n    fn ranking_precedence() {\n        let test = Accept(vec![]);\n        assert!(test.ranked().is_empty());\n\n        let test = Accept(vec![QualityItem::max(mime::APPLICATION_JSON)]);\n        assert_eq!(test.ranked(), vec![mime::APPLICATION_JSON]);\n\n        let test = Accept(vec![\n            QualityItem::max(mime::TEXT_HTML),\n            \"application/xhtml+xml\".parse().unwrap(),\n            QualityItem::new(\"application/xml\".parse().unwrap(), q(0.9)),\n            QualityItem::new(mime::STAR_STAR, q(0.8)),\n        ]);\n        assert_eq!(\n            test.ranked(),\n            vec![\n                mime::TEXT_HTML,\n                \"application/xhtml+xml\".parse().unwrap(),\n                \"application/xml\".parse().unwrap(),\n                mime::STAR_STAR,\n            ]\n        );\n\n        let test = Accept(vec![\n            QualityItem::max(mime::STAR_STAR),\n            QualityItem::max(mime::IMAGE_STAR),\n            QualityItem::max(mime::IMAGE_PNG),\n        ]);\n        assert_eq!(\n            test.ranked(),\n            vec![mime::IMAGE_PNG, mime::IMAGE_STAR, mime::STAR_STAR]\n        );\n    }\n\n    #[test]\n    fn preference_selection() {\n        let test = Accept(vec![\n            QualityItem::max(mime::TEXT_HTML),\n            \"application/xhtml+xml\".parse().unwrap(),\n            QualityItem::new(\"application/xml\".parse().unwrap(), q(0.9)),\n            QualityItem::new(mime::STAR_STAR, q(0.8)),\n        ]);\n        assert_eq!(test.preference(), mime::TEXT_HTML);\n\n        let test = Accept(vec![\n            QualityItem::new(\"video/*\".parse().unwrap(), q(0.8)),\n            QualityItem::max(mime::IMAGE_PNG),\n            QualityItem::new(mime::STAR_STAR, q(0.5)),\n            QualityItem::max(mime::IMAGE_SVG),\n            QualityItem::new(mime::IMAGE_STAR, q(0.8)),\n        ]);\n        assert_eq!(test.preference(), mime::IMAGE_PNG);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/accept_charset.rs",
    "content": "use super::{common_header, Charset, QualityItem, ACCEPT_CHARSET};\n\ncommon_header! {\n    /// `Accept-Charset` header, defined in [RFC 7231 §5.3.3].\n    ///\n    /// The `Accept-Charset` header field can be sent by a user agent to\n    /// indicate what charsets are acceptable in textual response content.\n    /// This field allows user agents capable of understanding more\n    /// comprehensive or special-purpose charsets to signal that capability\n    /// to an origin server that is capable of representing information in\n    /// those charsets.\n    ///\n    /// # Note\n    /// This is a request header. Servers should not send `Accept-Charset` in responses; to\n    /// describe the response body's charset, set an appropriate `Content-Type` header instead.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )\n    /// ```\n    ///\n    /// # Example Values\n    /// * `iso-8859-5, unicode-1-1;q=0.8`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::{AcceptCharset, Charset, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptCharset(vec![QualityItem::max(Charset::Us_Ascii)]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{AcceptCharset, Charset, q, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptCharset(vec![\n    ///         QualityItem::new(Charset::Us_Ascii, q(0.9)),\n    ///         QualityItem::new(Charset::Iso_8859_10, q(0.2)),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{AcceptCharset, Charset, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptCharset(vec![QualityItem::max(Charset::Ext(\"utf-8\".to_owned()))]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// [RFC 7231 §5.3.3]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.3\n    (AcceptCharset, ACCEPT_CHARSET) => (QualityItem<Charset>)*\n\n    test_parse_and_format {\n        // Test case from RFC\n        common_header_test!(test1, [b\"iso-8859-5, unicode-1-1;q=0.8\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/accept_encoding.rs",
    "content": "use std::collections::HashSet;\n\nuse super::{common_header, ContentEncoding, Encoding, Preference, Quality, QualityItem};\nuse crate::http::header;\n\ncommon_header! {\n    /// `Accept-Encoding` header, defined\n    /// in [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.4)\n    ///\n    /// The `Accept-Encoding` header field can be used by user agents to indicate what response\n    /// content-codings are acceptable in the response. An `identity` token is used as a synonym\n    /// for \"no encoding\" in order to communicate when no encoding is preferred.\n    ///\n    /// # Note\n    /// This is a request header. Servers should not send `Accept-Encoding` in responses; use the\n    /// `Content-Encoding` header (or middleware like compression) to describe any content-coding\n    /// applied to the response body.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Accept-Encoding  = #( codings [ weight ] )\n    /// codings          = content-coding / \"identity\" / \"*\"\n    /// ```\n    ///\n    /// # Example Values\n    /// * `compress, gzip`\n    /// * ``\n    /// * `*`\n    /// * `compress;q=0.5, gzip;q=1`\n    /// * `gzip;q=1.0, identity; q=0.5, *;q=0`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::{AcceptEncoding, Encoding, Preference, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptEncoding(vec![\n    ///         QualityItem::max(Preference::Specific(Encoding::gzip())),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{AcceptEncoding, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptEncoding(vec![\n    ///         \"gzip\".parse().unwrap(),\n    ///         \"br\".parse().unwrap(),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (AcceptEncoding, header::ACCEPT_ENCODING) => (QualityItem<Preference<Encoding>>)*\n\n    test_parse_and_format {\n        common_header_test!(no_headers, [b\"\"; 0], Some(AcceptEncoding(vec![])));\n        common_header_test!(empty_header, [b\"\"; 1], Some(AcceptEncoding(vec![])));\n\n        common_header_test!(\n            order_of_appearance,\n            [b\"br, gzip\"],\n            Some(AcceptEncoding(vec![\n                QualityItem::max(Preference::Specific(Encoding::brotli())),\n                QualityItem::max(Preference::Specific(Encoding::gzip())),\n            ]))\n        );\n\n        common_header_test!(any, [b\"*\"], Some(AcceptEncoding(vec![\n            QualityItem::max(Preference::Any),\n        ])));\n\n        // Note: Removed quality 1 from gzip\n        common_header_test!(implicit_quality, [b\"gzip, identity; q=0.5, *;q=0\"]);\n\n        // Note: Removed quality 1 from gzip\n        common_header_test!(implicit_quality_out_of_order, [b\"compress;q=0.5, gzip\"]);\n\n        common_header_test!(\n            only_gzip_no_identity,\n            [b\"gzip, *; q=0\"],\n            Some(AcceptEncoding(vec![\n                QualityItem::max(Preference::Specific(Encoding::gzip())),\n                QualityItem::zero(Preference::Any),\n            ]))\n        );\n    }\n}\n\nimpl AcceptEncoding {\n    /// Selects the most acceptable encoding according to client preference and supported types.\n    ///\n    /// The \"identity\" encoding is not assumed and should be included in the `supported` iterator\n    /// if a non-encoded representation can be selected.\n    ///\n    /// If `None` is returned, this indicates that none of the supported encodings are acceptable to\n    /// the client. The caller should generate a 406 Not Acceptable response (unencoded) that\n    /// includes the server's supported encodings in the body plus a [`Vary`] header.\n    ///\n    /// [`Vary`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary\n    pub fn negotiate<'a>(&self, supported: impl Iterator<Item = &'a Encoding>) -> Option<Encoding> {\n        // 1. If no Accept-Encoding field is in the request, any content-coding is considered\n        // acceptable by the user agent.\n\n        let supported_set = supported.collect::<HashSet<_>>();\n\n        if supported_set.is_empty() {\n            return None;\n        }\n\n        if self.0.is_empty() {\n            // though it is not recommended to encode in this case, return identity encoding\n            return Some(Encoding::identity());\n        }\n\n        // 2. If the representation has no content-coding, then it is acceptable by default unless\n        // specifically excluded by the Accept-Encoding field stating either \"identity;q=0\" or\n        // \"*;q=0\" without a more specific entry for \"identity\".\n\n        let acceptable_items = self.ranked_items().collect::<Vec<_>>();\n\n        let identity_acceptable = is_identity_acceptable(&acceptable_items);\n        let identity_supported = supported_set.contains(&Encoding::identity());\n\n        if identity_acceptable && identity_supported && supported_set.len() == 1 {\n            return Some(Encoding::identity());\n        }\n\n        // 3. If the representation's content-coding is one of the content-codings listed in the\n        // Accept-Encoding field, then it is acceptable unless it is accompanied by a qvalue of 0.\n\n        // 4. If multiple content-codings are acceptable, then the acceptable content-coding with\n        // the highest non-zero qvalue is preferred.\n\n        let matched = acceptable_items\n            .into_iter()\n            .filter(|q| q.quality > Quality::ZERO)\n            // search relies on item list being in descending order of quality\n            .find(|q| {\n                let enc = &q.item;\n                matches!(enc, Preference::Specific(enc) if supported_set.contains(enc))\n            })\n            .map(|q| q.item);\n\n        match matched {\n            Some(Preference::Specific(enc)) => Some(enc),\n\n            _ if identity_acceptable => Some(Encoding::identity()),\n\n            _ => None,\n        }\n    }\n\n    /// Extracts the most preferable encoding, accounting for [q-factor weighting].\n    ///\n    /// If no q-factors are provided, we prefer brotli > zstd > gzip. Note that items without\n    /// q-factors are given the maximum preference value.\n    ///\n    /// As per the spec, returns [`Preference::Any`] if acceptable list is empty. Though, if this is\n    /// returned, it is recommended to use an un-encoded representation.\n    ///\n    /// If `None` is returned, it means that the client has signalled that no representations\n    /// are acceptable. This should never occur for a well behaved user-agent.\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn preference(&self) -> Option<Preference<Encoding>> {\n        // empty header indicates no preference\n        if self.0.is_empty() {\n            return Some(Preference::Any);\n        }\n\n        let mut max_item = None;\n        let mut max_pref = Quality::ZERO;\n        let mut max_rank = 0;\n\n        // uses manual max lookup loop since we want the first occurrence in the case of same\n        // preference but `Iterator::max_by_key` would give us the last occurrence\n\n        for pref in &self.0 {\n            // only change if strictly greater\n            // equal items, even while unsorted, still have higher preference if they appear first\n\n            let rank = encoding_rank(pref);\n\n            if (pref.quality, rank) > (max_pref, max_rank) {\n                max_pref = pref.quality;\n                max_item = Some(pref.item.clone());\n                max_rank = rank;\n            }\n        }\n\n        // Return max_item if any items were above 0 quality...\n        max_item.or_else(|| {\n            // ...or else check for \"*\" or \"identity\". We can elide quality checks since\n            // entering this block means all items had \"q=0\".\n            match self.0.iter().find(|pref| {\n                matches!(\n                    pref.item,\n                    Preference::Any\n                        | Preference::Specific(Encoding::Known(ContentEncoding::Identity))\n                )\n            }) {\n                // \"identity\" or \"*\" found so no representation is acceptable\n                Some(_) => None,\n\n                // implicit \"identity\" is acceptable\n                None => Some(Preference::Specific(Encoding::identity())),\n            }\n        })\n    }\n\n    /// Returns a sorted list of encodings from highest to lowest precedence, accounting\n    /// for [q-factor weighting].\n    ///\n    /// If no q-factors are provided, we prefer brotli > zstd > gzip.\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn ranked(&self) -> Vec<Preference<Encoding>> {\n        self.ranked_items().map(|q| q.item).collect()\n    }\n\n    fn ranked_items(&self) -> impl Iterator<Item = QualityItem<Preference<Encoding>>> {\n        if self.0.is_empty() {\n            return Vec::new().into_iter();\n        }\n\n        let mut types = self.0.clone();\n\n        // use stable sort so items with equal q-factor retain listed order\n        types.sort_by(|a, b| {\n            // sort by q-factor descending then server ranking descending\n\n            b.quality\n                .cmp(&a.quality)\n                .then(encoding_rank(b).cmp(&encoding_rank(a)))\n        });\n\n        types.into_iter()\n    }\n}\n\n/// Returns server-defined encoding ranking.\nfn encoding_rank(qv: &QualityItem<Preference<Encoding>>) -> u8 {\n    // ensure that q=0 items are never sorted above identity encoding\n    // invariant: sorting methods calling this fn use first-on-equal approach\n    if qv.quality == Quality::ZERO {\n        return 0;\n    }\n\n    match qv.item {\n        Preference::Specific(Encoding::Known(ContentEncoding::Brotli)) => 5,\n        Preference::Specific(Encoding::Known(ContentEncoding::Zstd)) => 4,\n        Preference::Specific(Encoding::Known(ContentEncoding::Gzip)) => 3,\n        Preference::Specific(Encoding::Known(ContentEncoding::Deflate)) => 2,\n        Preference::Any => 0,\n        Preference::Specific(Encoding::Known(ContentEncoding::Identity)) => 0,\n        Preference::Specific(Encoding::Known(_)) => 1,\n        Preference::Specific(Encoding::Unknown(_)) => 1,\n    }\n}\n\n/// Returns true if \"identity\" is an acceptable encoding.\n///\n/// Internal algorithm relies on item list being in descending order of quality.\nfn is_identity_acceptable(items: &'_ [QualityItem<Preference<Encoding>>]) -> bool {\n    if items.is_empty() {\n        return true;\n    }\n\n    // Loop algorithm depends on items being sorted in descending order of quality. As such, it\n    // is sufficient to return (q > 0) when reaching either an \"identity\" or \"*\" item.\n    for q in items {\n        match (q.quality, &q.item) {\n            // occurrence of \"identity;q=n\"; return true if quality is non-zero\n            (q, Preference::Specific(Encoding::Known(ContentEncoding::Identity))) => {\n                return q > Quality::ZERO\n            }\n\n            // occurrence of \"*;q=n\"; return true if quality is non-zero\n            (q, Preference::Any) => return q > Quality::ZERO,\n\n            _ => {}\n        }\n    }\n\n    // implicit acceptable identity\n    true\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::http::header::*;\n\n    macro_rules! accept_encoding {\n        () => { AcceptEncoding(vec![]) };\n        ($($q:expr),+ $(,)?) => { AcceptEncoding(vec![$($q.parse().unwrap()),+]) };\n    }\n\n    /// Parses an encoding string.\n    fn enc(enc: &str) -> Preference<Encoding> {\n        enc.parse().unwrap()\n    }\n\n    #[test]\n    fn detect_identity_acceptable() {\n        macro_rules! accept_encoding_ranked {\n            () => { accept_encoding!().ranked_items().collect::<Vec<_>>() };\n            ($($q:expr),+ $(,)?) => { accept_encoding!($($q),+).ranked_items().collect::<Vec<_>>() };\n        }\n\n        let test = accept_encoding_ranked!();\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\");\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"br\");\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"*;q=0.1\");\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"identity;q=0.1\");\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"identity;q=0.1\", \"*;q=0\");\n        assert!(is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"*;q=0\", \"identity;q=0.1\");\n        assert!(is_identity_acceptable(&test));\n\n        let test = accept_encoding_ranked!(\"gzip\", \"*;q=0\");\n        assert!(!is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"identity;q=0\");\n        assert!(!is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"identity;q=0\", \"*;q=0\");\n        assert!(!is_identity_acceptable(&test));\n        let test = accept_encoding_ranked!(\"gzip\", \"*;q=0\", \"identity;q=0\");\n        assert!(!is_identity_acceptable(&test));\n    }\n\n    #[test]\n    fn encoding_negotiation() {\n        // no preference\n        let test = accept_encoding!();\n        assert_eq!(test.negotiate([].iter()), None);\n\n        let test = accept_encoding!();\n        assert_eq!(\n            test.negotiate([Encoding::identity()].iter()),\n            Some(Encoding::identity()),\n        );\n\n        let test = accept_encoding!(\"identity;q=0\");\n        assert_eq!(test.negotiate([Encoding::identity()].iter()), None);\n\n        let test = accept_encoding!(\"*;q=0\");\n        assert_eq!(test.negotiate([Encoding::identity()].iter()), None);\n\n        let test = accept_encoding!();\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::identity()),\n        );\n\n        let test = accept_encoding!(\"gzip\");\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::gzip()),\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),\n            Some(Encoding::identity()),\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::gzip()),\n        );\n\n        let test = accept_encoding!(\"gzip\", \"identity;q=0\");\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::gzip()),\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),\n            None\n        );\n\n        let test = accept_encoding!(\"gzip\", \"*;q=0\");\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::gzip()),\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),\n            None\n        );\n\n        let test = accept_encoding!(\"gzip\", \"deflate\", \"br\");\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::gzip()),\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::identity()].iter()),\n            Some(Encoding::brotli())\n        );\n        assert_eq!(\n            test.negotiate([Encoding::deflate(), Encoding::identity()].iter()),\n            Some(Encoding::deflate())\n        );\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::deflate(), Encoding::identity()].iter()),\n            Some(Encoding::gzip())\n        );\n        assert_eq!(\n            test.negotiate([Encoding::gzip(), Encoding::brotli(), Encoding::identity()].iter()),\n            Some(Encoding::brotli())\n        );\n        assert_eq!(\n            test.negotiate([Encoding::brotli(), Encoding::gzip(), Encoding::identity()].iter()),\n            Some(Encoding::brotli())\n        );\n    }\n\n    #[test]\n    fn ranking_precedence() {\n        let test = accept_encoding!();\n        assert!(test.ranked().is_empty());\n\n        let test = accept_encoding!(\"gzip\");\n        assert_eq!(test.ranked(), vec![enc(\"gzip\")]);\n\n        let test = accept_encoding!(\"gzip;q=0.900\", \"*;q=0.700\", \"br;q=1.0\");\n        assert_eq!(test.ranked(), vec![enc(\"br\"), enc(\"gzip\"), enc(\"*\")]);\n\n        let test = accept_encoding!(\"br\", \"gzip\", \"*\");\n        assert_eq!(test.ranked(), vec![enc(\"br\"), enc(\"gzip\"), enc(\"*\")]);\n\n        let test = accept_encoding!(\"gzip\", \"br\", \"*\");\n        assert_eq!(test.ranked(), vec![enc(\"br\"), enc(\"gzip\"), enc(\"*\")]);\n    }\n\n    #[test]\n    fn preference_selection() {\n        assert_eq!(accept_encoding!().preference(), Some(Preference::Any));\n\n        assert_eq!(accept_encoding!(\"identity;q=0\").preference(), None);\n        assert_eq!(accept_encoding!(\"*;q=0\").preference(), None);\n        assert_eq!(accept_encoding!(\"compress;q=0\", \"*;q=0\").preference(), None);\n        assert_eq!(accept_encoding!(\"identity;q=0\", \"*;q=0\").preference(), None);\n\n        let test = accept_encoding!(\"*;q=0.5\");\n        assert_eq!(test.preference().unwrap(), enc(\"*\"));\n\n        let test = accept_encoding!(\"br;q=0\");\n        assert_eq!(test.preference().unwrap(), enc(\"identity\"));\n\n        let test = accept_encoding!(\"br;q=0.900\", \"gzip;q=1.0\", \"*;q=0.500\");\n        assert_eq!(test.preference().unwrap(), enc(\"gzip\"));\n\n        let test = accept_encoding!(\"br\", \"gzip\", \"*\");\n        assert_eq!(test.preference().unwrap(), enc(\"br\"));\n\n        let test = accept_encoding!(\"gzip\", \"br\", \"*\");\n        assert_eq!(test.preference().unwrap(), enc(\"br\"));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/accept_language.rs",
    "content": "use language_tags::LanguageTag;\n\nuse super::{common_header, Preference, Quality, QualityItem};\nuse crate::http::header;\n\ncommon_header! {\n    /// `Accept-Language` header, defined\n    /// in [RFC 7231 §5.3.5](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5)\n    ///\n    /// The `Accept-Language` header field can be used by user agents to indicate the set of natural\n    /// languages that are preferred in the response.\n    ///\n    /// The `Accept-Language` header is defined in\n    /// [RFC 7231 §5.3.5](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5) using language\n    /// ranges defined in [RFC 4647 §2.1](https://datatracker.ietf.org/doc/html/rfc4647#section-2.1).\n    ///\n    /// # Note\n    /// This is a request header. Servers should not send `Accept-Language` in responses; use\n    /// `Content-Language` to describe the language of the response body.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Accept-Language = 1#( language-range [ weight ] )\n    /// language-range  = (1*8ALPHA *(\"-\" 1*8alphanum)) / \"*\"\n    /// alphanum        = ALPHA / DIGIT\n    /// weight          = OWS \";\" OWS \"q=\" qvalue\n    /// qvalue          = ( \"0\" [ \".\" 0*3DIGIT ] )\n    ///                 / ( \"1\" [ \".\" 0*3(\"0\") ] )\n    /// ```\n    ///\n    /// # Example Values\n    /// - `da, en-gb;q=0.8, en;q=0.7`\n    /// - `en-us;q=1.0, en;q=0.5, fr`\n    /// - `fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::{AcceptLanguage, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptLanguage(vec![\"en-US\".parse().unwrap()]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{AcceptLanguage, q, QualityItem}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(AcceptLanguage(vec![\n    ///         \"da\".parse().unwrap(),\n    ///         \"en-GB;q=0.8\".parse().unwrap(),\n    ///         \"en;q=0.7\".parse().unwrap(),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (AcceptLanguage, header::ACCEPT_LANGUAGE) => (QualityItem<Preference<LanguageTag>>)*\n\n    test_parse_and_format {\n        common_header_test!(no_headers, [b\"\"; 0], Some(AcceptLanguage(vec![])));\n\n        common_header_test!(empty_header, [b\"\"; 1], Some(AcceptLanguage(vec![])));\n\n        common_header_test!(\n            example_from_rfc,\n            [b\"da, en-gb;q=0.8, en;q=0.7\"]\n        );\n\n\n        common_header_test!(\n            not_ordered_by_weight,\n            [b\"en-US, en; q=0.5, fr\"],\n            Some(AcceptLanguage(vec![\n                QualityItem::max(\"en-US\".parse().unwrap()),\n                QualityItem::new(\"en\".parse().unwrap(), q(0.5)),\n                QualityItem::max(\"fr\".parse().unwrap()),\n            ]))\n        );\n\n        common_header_test!(\n            has_wildcard,\n            [b\"fr-CH, fr; q=0.9, en; q=0.8, de; q=0.7, *; q=0.5\"],\n            Some(AcceptLanguage(vec![\n                QualityItem::max(\"fr-CH\".parse().unwrap()),\n                QualityItem::new(\"fr\".parse().unwrap(), q(0.9)),\n                QualityItem::new(\"en\".parse().unwrap(), q(0.8)),\n                QualityItem::new(\"de\".parse().unwrap(), q(0.7)),\n                QualityItem::new(\"*\".parse().unwrap(), q(0.5)),\n            ]))\n        );\n    }\n}\n\nimpl AcceptLanguage {\n    /// Extracts the most preferable language, accounting for [q-factor weighting].\n    ///\n    /// If no q-factors are provided, the first language is chosen. Note that items without\n    /// q-factors are given the maximum preference value.\n    ///\n    /// As per the spec, returns [`Preference::Any`] if contained list is empty.\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn preference(&self) -> Preference<LanguageTag> {\n        let mut max_item = None;\n        let mut max_pref = Quality::ZERO;\n\n        // uses manual max lookup loop since we want the first occurrence in the case of same\n        // preference but `Iterator::max_by_key` would give us the last occurrence\n\n        for pref in &self.0 {\n            // only change if strictly greater\n            // equal items, even while unsorted, still have higher preference if they appear first\n            if pref.quality > max_pref {\n                max_pref = pref.quality;\n                max_item = Some(pref.item.clone());\n            }\n        }\n\n        max_item.unwrap_or(Preference::Any)\n    }\n\n    /// Returns a sorted list of languages from highest to lowest precedence, accounting\n    /// for [q-factor weighting].\n    ///\n    /// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2\n    pub fn ranked(&self) -> Vec<Preference<LanguageTag>> {\n        if self.0.is_empty() {\n            return vec![];\n        }\n\n        let mut types = self.0.clone();\n\n        // use stable sort so items with equal q-factor retain listed order\n        types.sort_by(|a, b| {\n            // sort by q-factor descending\n            b.quality.cmp(&a.quality)\n        });\n\n        types.into_iter().map(|q_item| q_item.item).collect()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::http::header::*;\n\n    #[test]\n    fn ranking_precedence() {\n        let test = AcceptLanguage(vec![]);\n        assert!(test.ranked().is_empty());\n\n        let test = AcceptLanguage(vec![QualityItem::max(\"fr-CH\".parse().unwrap())]);\n        assert_eq!(test.ranked(), vec![\"fr-CH\".parse().unwrap()]);\n\n        let test = AcceptLanguage(vec![\n            QualityItem::new(\"fr\".parse().unwrap(), q(0.900)),\n            QualityItem::new(\"fr-CH\".parse().unwrap(), q(1.0)),\n            QualityItem::new(\"en\".parse().unwrap(), q(0.800)),\n            QualityItem::new(\"*\".parse().unwrap(), q(0.500)),\n            QualityItem::new(\"de\".parse().unwrap(), q(0.700)),\n        ]);\n        assert_eq!(\n            test.ranked(),\n            vec![\n                \"fr-CH\".parse().unwrap(),\n                \"fr\".parse().unwrap(),\n                \"en\".parse().unwrap(),\n                \"de\".parse().unwrap(),\n                \"*\".parse().unwrap(),\n            ]\n        );\n\n        let test = AcceptLanguage(vec![\n            QualityItem::max(\"fr\".parse().unwrap()),\n            QualityItem::max(\"fr-CH\".parse().unwrap()),\n            QualityItem::max(\"en\".parse().unwrap()),\n            QualityItem::max(\"*\".parse().unwrap()),\n            QualityItem::max(\"de\".parse().unwrap()),\n        ]);\n        assert_eq!(\n            test.ranked(),\n            vec![\n                \"fr\".parse().unwrap(),\n                \"fr-CH\".parse().unwrap(),\n                \"en\".parse().unwrap(),\n                \"*\".parse().unwrap(),\n                \"de\".parse().unwrap(),\n            ]\n        );\n    }\n\n    #[test]\n    fn preference_selection() {\n        let test = AcceptLanguage(vec![\n            QualityItem::new(\"fr\".parse().unwrap(), q(0.900)),\n            QualityItem::new(\"fr-CH\".parse().unwrap(), q(1.0)),\n            QualityItem::new(\"en\".parse().unwrap(), q(0.800)),\n            QualityItem::new(\"*\".parse().unwrap(), q(0.500)),\n            QualityItem::new(\"de\".parse().unwrap(), q(0.700)),\n        ]);\n        assert_eq!(\n            test.preference(),\n            Preference::Specific(\"fr-CH\".parse().unwrap())\n        );\n\n        let test = AcceptLanguage(vec![\n            QualityItem::max(\"fr\".parse().unwrap()),\n            QualityItem::max(\"fr-CH\".parse().unwrap()),\n            QualityItem::max(\"en\".parse().unwrap()),\n            QualityItem::max(\"*\".parse().unwrap()),\n            QualityItem::max(\"de\".parse().unwrap()),\n        ]);\n        assert_eq!(\n            test.preference(),\n            Preference::Specific(\"fr\".parse().unwrap())\n        );\n\n        let test = AcceptLanguage(vec![]);\n        assert_eq!(test.preference(), Preference::Any);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/allow.rs",
    "content": "use actix_http::Method;\n\nuse crate::http::header;\n\ncrate::http::header::common_header! {\n    /// `Allow` header, defined\n    /// in [RFC 7231 §7.4.1](https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1)\n    ///\n    /// The `Allow` header field lists the set of methods advertised as\n    /// supported by the target resource. The purpose of this field is\n    /// strictly to inform the recipient of valid request methods associated\n    /// with the resource.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Allow = #method\n    /// ```\n    ///\n    /// # Example Values\n    /// * `GET, HEAD, PUT`\n    /// * `OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH, fOObAr`\n    /// * ``\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::{header::Allow, Method};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     Allow(vec![Method::GET])\n    /// );\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::{header::Allow, Method};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     Allow(vec![\n    ///         Method::GET,\n    ///         Method::POST,\n    ///         Method::PATCH,\n    ///     ])\n    /// );\n    /// ```\n    (Allow, header::ALLOW) => (Method)*\n\n    test_parse_and_format {\n        // from the RFC\n\n        crate::http::header::common_header_test!(\n            test1,\n            [b\"GET, HEAD, PUT\"],\n            Some(HeaderField(vec![Method::GET, Method::HEAD, Method::PUT])));\n\n        // other tests\n\n        crate::http::header::common_header_test!(\n            test2,\n            [b\"OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH\"],\n            Some(HeaderField(vec![\n                Method::OPTIONS,\n                Method::GET,\n                Method::PUT,\n                Method::POST,\n                Method::DELETE,\n                Method::HEAD,\n                Method::TRACE,\n                Method::CONNECT,\n                Method::PATCH])));\n\n        crate::http::header::common_header_test!(\n            test3,\n            [b\"\"],\n            Some(HeaderField(Vec::<Method>::new())));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/any_or_some.rs",
    "content": "use std::{\n    fmt::{self, Write as _},\n    str,\n};\n\n/// A wrapper for types used in header values where wildcard (`*`) items are allowed but the\n/// underlying type does not support them.\n///\n/// For example, we use the `language-tags` crate for the [`AcceptLanguage`](super::AcceptLanguage)\n/// typed header but it does parse `*` successfully. On the other hand, the `mime` crate, used for\n/// [`Accept`](super::Accept), has first-party support for wildcard items so this wrapper is not\n/// used in those header types.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)]\npub enum AnyOrSome<T> {\n    /// A wildcard value.\n    Any,\n\n    /// A valid `T`.\n    Item(T),\n}\n\nimpl<T> AnyOrSome<T> {\n    /// Returns true if item is wildcard (`*`) variant.\n    pub fn is_any(&self) -> bool {\n        matches!(self, Self::Any)\n    }\n\n    /// Returns true if item is a valid item (`T`) variant.\n    pub fn is_item(&self) -> bool {\n        matches!(self, Self::Item(_))\n    }\n\n    /// Returns reference to value in `Item` variant, if it is set.\n    pub fn item(&self) -> Option<&T> {\n        match self {\n            AnyOrSome::Item(ref item) => Some(item),\n            AnyOrSome::Any => None,\n        }\n    }\n\n    /// Consumes the container, returning the value in the `Item` variant, if it is set.\n    pub fn into_item(self) -> Option<T> {\n        match self {\n            AnyOrSome::Item(item) => Some(item),\n            AnyOrSome::Any => None,\n        }\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for AnyOrSome<T> {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            AnyOrSome::Any => f.write_char('*'),\n            AnyOrSome::Item(item) => fmt::Display::fmt(item, f),\n        }\n    }\n}\n\nimpl<T: str::FromStr> str::FromStr for AnyOrSome<T> {\n    type Err = T::Err;\n\n    #[inline]\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s.trim() {\n            \"*\" => Ok(Self::Any),\n            other => other.parse().map(AnyOrSome::Item),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/cache_control.rs",
    "content": "use std::{fmt, str};\n\nuse super::common_header;\nuse crate::http::header;\n\ncommon_header! {\n    /// `Cache-Control` header, defined\n    /// in [RFC 7234 §5.2](https://datatracker.ietf.org/doc/html/rfc7234#section-5.2).\n    ///\n    /// The `Cache-Control` header field is used to specify directives for\n    /// caches along the request/response chain.  Such cache directives are\n    /// unidirectional in that the presence of a directive in a request does\n    /// not imply that the same directive is to be given in the response.\n    ///\n    /// # ABNF\n    /// ```text\n    /// Cache-Control   = 1#cache-directive\n    /// cache-directive = token [ \"=\" ( token / quoted-string ) ]\n    /// ```\n    ///\n    /// # Example Values\n    /// * `no-cache`\n    /// * `private, community=\"UCI\"`\n    /// * `max-age=30`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{CacheControl, CacheDirective};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)]));\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{CacheControl, CacheDirective};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(CacheControl(vec![\n    ///     CacheDirective::NoCache,\n    ///     CacheDirective::Private,\n    ///     CacheDirective::MaxAge(360u32),\n    ///     CacheDirective::Extension(\"foo\".to_owned(), Some(\"bar\".to_owned())),\n    /// ]));\n    /// ```\n    (CacheControl, header::CACHE_CONTROL) => (CacheDirective)+\n\n    test_parse_and_format {\n        common_header_test!(no_headers, [b\"\"; 0], None);\n        common_header_test!(empty_header, [b\"\"; 1], None);\n        common_header_test!(bad_syntax, [b\"foo=\"], None);\n\n        common_header_test!(\n            multiple_headers,\n            [&b\"no-cache\"[..], &b\"private\"[..]],\n            Some(CacheControl(vec![\n                CacheDirective::NoCache,\n                CacheDirective::Private,\n            ]))\n        );\n\n        common_header_test!(\n            argument,\n            [b\"max-age=100, private\"],\n            Some(CacheControl(vec![\n                CacheDirective::MaxAge(100),\n                CacheDirective::Private,\n            ]))\n        );\n\n        common_header_test!(\n            extension,\n            [b\"foo, bar=baz\"],\n            Some(CacheControl(vec![\n                CacheDirective::Extension(\"foo\".to_owned(), None),\n                CacheDirective::Extension(\"bar\".to_owned(), Some(\"baz\".to_owned())),\n            ]))\n        );\n\n        #[test]\n        fn parse_quote_form() {\n            let req = test::TestRequest::default()\n                .insert_header((header::CACHE_CONTROL, \"max-age=\\\"200\\\"\"))\n                .finish();\n\n            assert_eq!(\n                Header::parse(&req).ok(),\n                Some(CacheControl(vec![CacheDirective::MaxAge(200)]))\n            )\n        }\n    }\n}\n\n/// `CacheControl` contains a list of these directives.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum CacheDirective {\n    /// \"no-cache\"\n    NoCache,\n    /// \"no-store\"\n    NoStore,\n    /// \"no-transform\"\n    NoTransform,\n    /// \"only-if-cached\"\n    OnlyIfCached,\n\n    // request directives\n    /// \"max-age=delta\"\n    MaxAge(u32),\n    /// \"max-stale=delta\"\n    MaxStale(u32),\n    /// \"min-fresh=delta\"\n    MinFresh(u32),\n\n    // response directives\n    /// \"must-revalidate\"\n    MustRevalidate,\n    /// \"public\"\n    Public,\n    /// \"private\"\n    Private,\n    /// \"proxy-revalidate\"\n    ProxyRevalidate,\n    /// \"s-maxage=delta\"\n    SMaxAge(u32),\n\n    /// Extension directives. Optionally include an argument.\n    Extension(String, Option<String>),\n}\n\nimpl fmt::Display for CacheDirective {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        use self::CacheDirective::*;\n\n        let dir_str = match self {\n            NoCache => \"no-cache\",\n            NoStore => \"no-store\",\n            NoTransform => \"no-transform\",\n            OnlyIfCached => \"only-if-cached\",\n\n            MaxAge(secs) => return write!(f, \"max-age={}\", secs),\n            MaxStale(secs) => return write!(f, \"max-stale={}\", secs),\n            MinFresh(secs) => return write!(f, \"min-fresh={}\", secs),\n\n            MustRevalidate => \"must-revalidate\",\n            Public => \"public\",\n            Private => \"private\",\n            ProxyRevalidate => \"proxy-revalidate\",\n            SMaxAge(secs) => return write!(f, \"s-maxage={}\", secs),\n\n            Extension(name, None) => name.as_str(),\n            Extension(name, Some(arg)) => return write!(f, \"{}={}\", name, arg),\n        };\n\n        f.write_str(dir_str)\n    }\n}\n\nimpl str::FromStr for CacheDirective {\n    type Err = Option<<u32 as str::FromStr>::Err>;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use self::CacheDirective::*;\n\n        match s {\n            \"\" => Err(None),\n\n            \"no-cache\" => Ok(NoCache),\n            \"no-store\" => Ok(NoStore),\n            \"no-transform\" => Ok(NoTransform),\n            \"only-if-cached\" => Ok(OnlyIfCached),\n            \"must-revalidate\" => Ok(MustRevalidate),\n            \"public\" => Ok(Public),\n            \"private\" => Ok(Private),\n            \"proxy-revalidate\" => Ok(ProxyRevalidate),\n\n            _ => match s.find('=') {\n                Some(idx) if idx + 1 < s.len() => {\n                    match (&s[..idx], s[idx + 1..].trim_matches('\"')) {\n                        (\"max-age\", secs) => secs.parse().map(MaxAge).map_err(Some),\n                        (\"max-stale\", secs) => secs.parse().map(MaxStale).map_err(Some),\n                        (\"min-fresh\", secs) => secs.parse().map(MinFresh).map_err(Some),\n                        (\"s-maxage\", secs) => secs.parse().map(SMaxAge).map_err(Some),\n                        (left, right) => Ok(Extension(left.to_owned(), Some(right.to_owned()))),\n                    }\n                }\n                Some(_) => Err(None),\n                None => Ok(Extension(s.to_owned(), None)),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/content_disposition.rs",
    "content": "//! The `Content-Disposition` header and associated types.\n//!\n//! # References\n//! - \"The Content-Disposition Header Field\":\n//!   <https://datatracker.ietf.org/doc/html/rfc2183>\n//! - \"The Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)\":\n//!   <https://datatracker.ietf.org/doc/html/rfc6266>\n//! - \"Returning Values from Forms: multipart/form-data\":\n//!   <https://datatracker.ietf.org/doc/html/rfc7578>\n//! - Browser conformance tests at: <http://greenbytes.de/tech/tc2231/>\n//! - IANA assignment: <http://www.iana.org/assignments/cont-disp/cont-disp.xhtml>\n\nuse std::fmt::{self, Write};\n\nuse once_cell::sync::Lazy;\n#[cfg(feature = \"unicode\")]\nuse regex::Regex;\n#[cfg(not(feature = \"unicode\"))]\nuse regex_lite::Regex;\n\nuse super::{ExtendedValue, Header, TryIntoHeaderValue, Writer};\nuse crate::http::header;\n\n/// Split at the index of the first `needle` if it exists or at the end.\nfn split_once(haystack: &str, needle: char) -> (&str, &str) {\n    haystack.find(needle).map_or_else(\n        || (haystack, \"\"),\n        |sc| {\n            let (first, last) = haystack.split_at(sc);\n            (first, last.split_at(1).1)\n        },\n    )\n}\n\n/// Split at the index of the first `needle` if it exists or at the end, trim the right of the\n/// first part and the left of the last part.\nfn split_once_and_trim(haystack: &str, needle: char) -> (&str, &str) {\n    let (first, last) = split_once(haystack, needle);\n    (first.trim_end(), last.trim_start())\n}\n\n/// The implied disposition of the content of the HTTP body.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DispositionType {\n    /// Inline implies default processing.\n    Inline,\n\n    /// Attachment implies that the recipient should prompt the user to save the response locally,\n    /// rather than process it normally (as per its media type).\n    Attachment,\n\n    /// Used in *multipart/form-data* as defined in\n    /// [RFC 7578](https://datatracker.ietf.org/doc/html/rfc7578) to carry the field name and\n    /// optional filename.\n    FormData,\n\n    /// Extension type. Should be handled by recipients the same way as Attachment.\n    Ext(String),\n}\n\nimpl<'a> From<&'a str> for DispositionType {\n    fn from(origin: &'a str) -> DispositionType {\n        if origin.eq_ignore_ascii_case(\"inline\") {\n            DispositionType::Inline\n        } else if origin.eq_ignore_ascii_case(\"attachment\") {\n            DispositionType::Attachment\n        } else if origin.eq_ignore_ascii_case(\"form-data\") {\n            DispositionType::FormData\n        } else {\n            DispositionType::Ext(origin.to_owned())\n        }\n    }\n}\n\n/// Parameter in [`ContentDisposition`].\n///\n/// # Examples\n/// ```\n/// use actix_web::http::header::DispositionParam;\n///\n/// let param = DispositionParam::Filename(String::from(\"sample.txt\"));\n/// assert!(param.is_filename());\n/// assert_eq!(param.as_filename().unwrap(), \"sample.txt\");\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[allow(clippy::large_enum_variant)]\npub enum DispositionParam {\n    /// For [`DispositionType::FormData`] (i.e. *multipart/form-data*), the name of an field from\n    /// the form.\n    Name(String),\n\n    /// A plain file name.\n    ///\n    /// It is [not supposed](https://datatracker.ietf.org/doc/html/rfc6266#appendix-D) to contain\n    /// any non-ASCII characters when used in a *Content-Disposition* HTTP response header, where\n    /// [`FilenameExt`](DispositionParam::FilenameExt) with charset UTF-8 may be used instead\n    /// in case there are Unicode characters in file names.\n    Filename(String),\n\n    /// An extended file name. It must not exist for `ContentType::Formdata` according to\n    /// [RFC 7578 §4.2](https://datatracker.ietf.org/doc/html/rfc7578#section-4.2).\n    FilenameExt(ExtendedValue),\n\n    /// An unrecognized regular parameter as defined in\n    /// [RFC 5987 §3.2.1](https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1) as\n    /// `reg-parameter`, in\n    /// [RFC 6266 §4.1](https://datatracker.ietf.org/doc/html/rfc6266#section-4.1) as\n    /// `token \"=\" value`. Recipients should ignore unrecognizable parameters.\n    Unknown(String, String),\n\n    /// An unrecognized extended parameter as defined in\n    /// [RFC 5987 §3.2.1](https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1) as\n    /// `ext-parameter`, in\n    /// [RFC 6266 §4.1](https://datatracker.ietf.org/doc/html/rfc6266#section-4.1) as\n    /// `ext-token \"=\" ext-value`. The single trailing asterisk is not included. Recipients should\n    /// ignore unrecognizable parameters.\n    UnknownExt(String, ExtendedValue),\n}\n\nimpl DispositionParam {\n    /// Returns `true` if the parameter is [`Name`](DispositionParam::Name).\n    #[inline]\n    pub fn is_name(&self) -> bool {\n        self.as_name().is_some()\n    }\n\n    /// Returns `true` if the parameter is [`Filename`](DispositionParam::Filename).\n    #[inline]\n    pub fn is_filename(&self) -> bool {\n        self.as_filename().is_some()\n    }\n\n    /// Returns `true` if the parameter is [`FilenameExt`](DispositionParam::FilenameExt).\n    #[inline]\n    pub fn is_filename_ext(&self) -> bool {\n        self.as_filename_ext().is_some()\n    }\n\n    /// Returns `true` if the parameter is [`Unknown`](DispositionParam::Unknown) and the `name`\n    #[inline]\n    /// matches.\n    pub fn is_unknown<T: AsRef<str>>(&self, name: T) -> bool {\n        self.as_unknown(name).is_some()\n    }\n\n    /// Returns `true` if the parameter is [`UnknownExt`](DispositionParam::UnknownExt) and the\n    /// `name` matches.\n    #[inline]\n    pub fn is_unknown_ext<T: AsRef<str>>(&self, name: T) -> bool {\n        self.as_unknown_ext(name).is_some()\n    }\n\n    /// Returns the name if applicable.\n    #[inline]\n    pub fn as_name(&self) -> Option<&str> {\n        match self {\n            DispositionParam::Name(name) => Some(name.as_str()),\n            _ => None,\n        }\n    }\n\n    /// Returns the filename if applicable.\n    #[inline]\n    pub fn as_filename(&self) -> Option<&str> {\n        match self {\n            DispositionParam::Filename(filename) => Some(filename.as_str()),\n            _ => None,\n        }\n    }\n\n    /// Returns the filename* if applicable.\n    #[inline]\n    pub fn as_filename_ext(&self) -> Option<&ExtendedValue> {\n        match self {\n            DispositionParam::FilenameExt(value) => Some(value),\n            _ => None,\n        }\n    }\n\n    /// Returns the value of the unrecognized regular parameter if it is\n    /// [`Unknown`](DispositionParam::Unknown) and the `name` matches.\n    #[inline]\n    pub fn as_unknown<T: AsRef<str>>(&self, name: T) -> Option<&str> {\n        match self {\n            DispositionParam::Unknown(ref ext_name, ref value)\n                if ext_name.eq_ignore_ascii_case(name.as_ref()) =>\n            {\n                Some(value.as_str())\n            }\n            _ => None,\n        }\n    }\n\n    /// Returns the value of the unrecognized extended parameter if it is\n    /// [`Unknown`](DispositionParam::Unknown) and the `name` matches.\n    #[inline]\n    pub fn as_unknown_ext<T: AsRef<str>>(&self, name: T) -> Option<&ExtendedValue> {\n        match self {\n            DispositionParam::UnknownExt(ref ext_name, ref value)\n                if ext_name.eq_ignore_ascii_case(name.as_ref()) =>\n            {\n                Some(value)\n            }\n            _ => None,\n        }\n    }\n}\n\n/// `Content-Disposition` header.\n///\n/// It is compatible to be used either as [a response header for the main body][use_main_body]\n/// as (re)defined in [RFC 6266], or as [a header for a multipart body][use_multipart] as\n/// (re)defined in [RFC 7587].\n///\n/// In a regular HTTP response, the *Content-Disposition* response header is a header indicating if\n/// the content is expected to be displayed *inline* in the browser, that is, as a Web page or as\n/// part of a Web page, or as an attachment, that is downloaded and saved locally, and also can be\n/// used to attach additional metadata, such as the filename to use when saving the response payload\n/// locally.\n///\n/// In a *multipart/form-data* body, the HTTP *Content-Disposition* general header is a header that\n/// can be used on the subpart of a multipart body to give information about the field it applies to.\n/// The subpart is delimited by the boundary defined in the *Content-Type* header. Used on the body\n/// itself, *Content-Disposition* has no effect.\n///\n/// # ABNF\n/// ```plain\n/// content-disposition = \"Content-Disposition\" \":\"\n///                       disposition-type *( \";\" disposition-parm )\n///\n/// disposition-type    = \"inline\" | \"attachment\" | disp-ext-type\n///                       ; case-insensitive\n///\n/// disp-ext-type       = token\n///\n/// disposition-parm    = filename-parm | disp-ext-parm\n///\n/// filename-parm       = \"filename\" \"=\" value\n///                     | \"filename*\" \"=\" ext-value\n///\n/// disp-ext-parm       = token \"=\" value\n///                     | ext-token \"=\" ext-value\n///\n/// ext-token           = <the characters in token, followed by \"*\">\n/// ```\n///\n/// # Note\n/// *filename* is [not supposed](https://datatracker.ietf.org/doc/html/rfc6266#appendix-D) to\n/// contain any non-ASCII characters when used in a *Content-Disposition* HTTP response header,\n/// where filename* with charset UTF-8 may be used instead in case there are Unicode characters in\n/// file names. Filename is [acceptable](https://datatracker.ietf.org/doc/html/rfc7578#section-4.2)\n/// to be UTF-8 encoded directly in a *Content-Disposition* header for\n/// *multipart/form-data*, though.\n///\n/// *filename* [must not](https://datatracker.ietf.org/doc/html/rfc7578#section-4.2) be used within\n/// *multipart/form-data*.\n///\n/// # Examples\n/// ```\n/// use actix_web::http::header::{\n///     Charset, ContentDisposition, DispositionParam, DispositionType,\n///     ExtendedValue,\n/// };\n///\n/// let cd1 = ContentDisposition {\n///     disposition: DispositionType::Attachment,\n///     parameters: vec![DispositionParam::FilenameExt(ExtendedValue {\n///         charset: Charset::Iso_8859_1, // The character set for the bytes of the filename\n///         language_tag: None, // The optional language tag (see `language-tag` crate)\n///         value: b\"\\xA9 Ferris 2011.txt\".to_vec(), // the actual bytes of the filename\n///     })],\n/// };\n/// assert!(cd1.is_attachment());\n/// assert!(cd1.get_filename_ext().is_some());\n///\n/// let cd2 = ContentDisposition {\n///     disposition: DispositionType::FormData,\n///     parameters: vec![\n///         DispositionParam::Name(String::from(\"file\")),\n///         DispositionParam::Filename(String::from(\"bill.odt\")),\n///     ],\n/// };\n/// assert_eq!(cd2.get_name(), Some(\"file\")); // field name\n/// assert_eq!(cd2.get_filename(), Some(\"bill.odt\"));\n///\n/// // HTTP response header with Unicode characters in file names\n/// let cd3 = ContentDisposition {\n///     disposition: DispositionType::Attachment,\n///     parameters: vec![\n///         DispositionParam::FilenameExt(ExtendedValue {\n///             charset: Charset::Ext(String::from(\"UTF-8\")),\n///             language_tag: None,\n///             value: String::from(\"\\u{1f600}.svg\").into_bytes(),\n///         }),\n///         // fallback for better compatibility\n///         DispositionParam::Filename(String::from(\"Grinning-Face-Emoji.svg\"))\n///     ],\n/// };\n/// assert_eq!(cd3.get_filename_ext().map(|ev| ev.value.as_ref()),\n///            Some(\"\\u{1f600}.svg\".as_bytes()));\n/// ```\n///\n/// # Security Note\n/// If \"filename\" parameter is supplied, do not use the file name blindly, check and possibly\n/// change to match local file system conventions if applicable, and do not use directory path\n/// information that may be present.\n/// See [RFC 2183 §2.3](https://datatracker.ietf.org/doc/html/rfc2183#section-2.3).\n///\n/// [use_main_body]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#as_a_response_header_for_the_main_body\n/// [RFC 6266]: https://datatracker.ietf.org/doc/html/rfc6266\n/// [use_multipart]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#as_a_header_for_a_multipart_body\n/// [RFC 7587]: https://datatracker.ietf.org/doc/html/rfc7578\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ContentDisposition {\n    /// The disposition type\n    pub disposition: DispositionType,\n\n    /// Disposition parameters\n    pub parameters: Vec<DispositionParam>,\n}\n\nimpl ContentDisposition {\n    /// Constructs a Content-Disposition header suitable for downloads.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::http::header::{ContentDisposition, TryIntoHeaderValue as _};\n    ///\n    /// let cd = ContentDisposition::attachment(\"files.zip\");\n    ///\n    /// let cd_val = cd.try_into_value().unwrap();\n    /// assert_eq!(cd_val, \"attachment; filename=\\\"files.zip\\\"\");\n    /// ```\n    pub fn attachment(filename: impl Into<String>) -> Self {\n        Self {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::Filename(filename.into())],\n        }\n    }\n\n    /// Parse a raw Content-Disposition header value.\n    pub fn from_raw(hv: &header::HeaderValue) -> Result<Self, crate::error::ParseError> {\n        // `header::from_one_raw_str` invokes `hv.to_str` which assumes `hv` contains only visible\n        //  ASCII characters. So `hv.as_bytes` is necessary here.\n        let hv = String::from_utf8(hv.as_bytes().to_vec())\n            .map_err(|_| crate::error::ParseError::Header)?;\n\n        let (disp_type, mut left) = split_once_and_trim(hv.as_str().trim(), ';');\n        if disp_type.is_empty() {\n            return Err(crate::error::ParseError::Header);\n        }\n\n        let mut cd = ContentDisposition {\n            disposition: disp_type.into(),\n            parameters: Vec::new(),\n        };\n\n        while !left.is_empty() {\n            let (param_name, new_left) = split_once_and_trim(left, '=');\n            if param_name.is_empty() || param_name == \"*\" || new_left.is_empty() {\n                return Err(crate::error::ParseError::Header);\n            }\n            left = new_left;\n            if let Some(param_name) = param_name.strip_suffix('*') {\n                // extended parameters\n                let (ext_value, new_left) = split_once_and_trim(left, ';');\n                left = new_left;\n                let ext_value = header::parse_extended_value(ext_value)?;\n\n                let param = if param_name.eq_ignore_ascii_case(\"filename\") {\n                    DispositionParam::FilenameExt(ext_value)\n                } else {\n                    DispositionParam::UnknownExt(param_name.to_owned(), ext_value)\n                };\n                cd.parameters.push(param);\n            } else {\n                // regular parameters\n                let value = if left.starts_with('\\\"') {\n                    // quoted-string: defined in RFC 6266 -> RFC 2616 Section 3.6\n                    let mut escaping = false;\n                    let mut quoted_string = vec![];\n                    let mut end = None;\n                    // search for closing quote\n                    for (i, &c) in left.as_bytes().iter().skip(1).enumerate() {\n                        if escaping {\n                            escaping = false;\n                            quoted_string.push(c);\n                        } else if c == 0x5c {\n                            // backslash\n                            escaping = true;\n                        } else if c == 0x22 {\n                            // double quote\n                            end = Some(i + 1); // cuz skipped 1 for the leading quote\n                            break;\n                        } else {\n                            quoted_string.push(c);\n                        }\n                    }\n                    left = &left[end.ok_or(crate::error::ParseError::Header)? + 1..];\n                    left = split_once(left, ';').1.trim_start();\n                    // In fact, it should not be Err if the above code is correct.\n                    String::from_utf8(quoted_string)\n                        .map_err(|_| crate::error::ParseError::Header)?\n                } else {\n                    // token: won't contains semicolon according to RFC 2616 Section 2.2\n                    let (token, new_left) = split_once_and_trim(left, ';');\n                    left = new_left;\n                    if token.is_empty() {\n                        // quoted-string can be empty, but token cannot be empty\n                        return Err(crate::error::ParseError::Header);\n                    }\n                    token.to_owned()\n                };\n\n                let param = if param_name.eq_ignore_ascii_case(\"name\") {\n                    DispositionParam::Name(value)\n                } else if param_name.eq_ignore_ascii_case(\"filename\") {\n                    // See also comments in test_from_raw_unnecessary_percent_decode.\n                    DispositionParam::Filename(value)\n                } else {\n                    DispositionParam::Unknown(param_name.to_owned(), value)\n                };\n                cd.parameters.push(param);\n            }\n        }\n\n        Ok(cd)\n    }\n\n    /// Returns `true` if type is [`Inline`](DispositionType::Inline).\n    pub fn is_inline(&self) -> bool {\n        matches!(self.disposition, DispositionType::Inline)\n    }\n\n    /// Returns `true` if type is [`Attachment`](DispositionType::Attachment).\n    pub fn is_attachment(&self) -> bool {\n        matches!(self.disposition, DispositionType::Attachment)\n    }\n\n    /// Returns `true` if type is [`FormData`](DispositionType::FormData).\n    pub fn is_form_data(&self) -> bool {\n        matches!(self.disposition, DispositionType::FormData)\n    }\n\n    /// Returns `true` if type is [`Ext`](DispositionType::Ext) and the `disp_type` matches.\n    pub fn is_ext(&self, disp_type: impl AsRef<str>) -> bool {\n        matches!(\n            self.disposition,\n            DispositionType::Ext(ref t) if t.eq_ignore_ascii_case(disp_type.as_ref())\n        )\n    }\n\n    /// Return the value of *name* if exists.\n    pub fn get_name(&self) -> Option<&str> {\n        self.parameters.iter().find_map(DispositionParam::as_name)\n    }\n\n    /// Return the value of *filename* if exists.\n    pub fn get_filename(&self) -> Option<&str> {\n        self.parameters\n            .iter()\n            .find_map(DispositionParam::as_filename)\n    }\n\n    /// Return the value of *filename\\** if exists.\n    pub fn get_filename_ext(&self) -> Option<&ExtendedValue> {\n        self.parameters\n            .iter()\n            .find_map(DispositionParam::as_filename_ext)\n    }\n\n    /// Return the value of the parameter which the `name` matches.\n    pub fn get_unknown(&self, name: impl AsRef<str>) -> Option<&str> {\n        let name = name.as_ref();\n        self.parameters.iter().find_map(|p| p.as_unknown(name))\n    }\n\n    /// Return the value of the extended parameter which the `name` matches.\n    pub fn get_unknown_ext(&self, name: impl AsRef<str>) -> Option<&ExtendedValue> {\n        let name = name.as_ref();\n        self.parameters.iter().find_map(|p| p.as_unknown_ext(name))\n    }\n}\n\nimpl TryIntoHeaderValue for ContentDisposition {\n    type Error = header::InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<header::HeaderValue, Self::Error> {\n        let mut writer = Writer::new();\n        let _ = write!(&mut writer, \"{}\", self);\n        header::HeaderValue::from_maybe_shared(writer.take())\n    }\n}\n\nimpl Header for ContentDisposition {\n    fn name() -> header::HeaderName {\n        header::CONTENT_DISPOSITION\n    }\n\n    fn parse<T: crate::HttpMessage>(msg: &T) -> Result<Self, crate::error::ParseError> {\n        if let Some(h) = msg.headers().get(Self::name()) {\n            Self::from_raw(h)\n        } else {\n            Err(crate::error::ParseError::Header)\n        }\n    }\n}\n\nimpl fmt::Display for DispositionType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            DispositionType::Inline => write!(f, \"inline\"),\n            DispositionType::Attachment => write!(f, \"attachment\"),\n            DispositionType::FormData => write!(f, \"form-data\"),\n            DispositionType::Ext(ref s) => write!(f, \"{}\", s),\n        }\n    }\n}\n\nimpl fmt::Display for DispositionParam {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        // All ASCII control characters (0-30, 127) including horizontal tab, double quote, and\n        // backslash should be escaped in quoted-string (i.e. \"foobar\").\n        //\n        // Ref: RFC 6266 §4.1 -> RFC 2616 §3.6\n        //\n        // filename-parm  = \"filename\" \"=\" value\n        // value          = token | quoted-string\n        // quoted-string  = ( <\"> *(qdtext | quoted-pair ) <\"> )\n        // qdtext         = <any TEXT except <\">>\n        // quoted-pair    = \"\\\" CHAR\n        // TEXT           = <any OCTET except CTLs,\n        //                  but including LWS>\n        // LWS            = [CRLF] 1*( SP | HT )\n        // OCTET          = <any 8-bit sequence of data>\n        // CHAR           = <any US-ASCII character (octets 0 - 127)>\n        // CTL            = <any US-ASCII control character\n        //                  (octets 0 - 31) and DEL (127)>\n        //\n        // Ref: RFC 7578 S4.2 -> RFC 2183 S2 -> RFC 2045 S5.1\n        // parameter := attribute \"=\" value\n        // attribute := token\n        //              ; Matching of attributes\n        //              ; is ALWAYS case-insensitive.\n        // value := token / quoted-string\n        // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,\n        //             or tspecials>\n        // tspecials :=  \"(\" / \")\" / \"<\" / \">\" / \"@\" /\n        //               \",\" / \";\" / \":\" / \"\\\" / <\">\n        //               \"/\" / \"[\" / \"]\" / \"?\" / \"=\"\n        //               ; Must be in quoted-string,\n        //               ; to use within parameter values\n        //\n        //\n        // See also comments in test_from_raw_unnecessary_percent_decode.\n\n        static RE: Lazy<Regex> =\n            Lazy::new(|| Regex::new(\"[\\x00-\\x08\\x10-\\x1F\\x7F\\\"\\\\\\\\]\").unwrap());\n\n        match self {\n            DispositionParam::Name(ref value) => write!(f, \"name={}\", value),\n\n            DispositionParam::Filename(ref value) => {\n                write!(f, \"filename=\\\"{}\\\"\", RE.replace_all(value, \"\\\\$0\").as_ref())\n            }\n\n            DispositionParam::Unknown(ref name, ref value) => write!(\n                f,\n                \"{}=\\\"{}\\\"\",\n                name,\n                &RE.replace_all(value, \"\\\\$0\").as_ref()\n            ),\n\n            DispositionParam::FilenameExt(ref ext_value) => {\n                write!(f, \"filename*={}\", ext_value)\n            }\n\n            DispositionParam::UnknownExt(ref name, ref ext_value) => {\n                write!(f, \"{}*={}\", name, ext_value)\n            }\n        }\n    }\n}\n\nimpl fmt::Display for ContentDisposition {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.disposition)?;\n        self.parameters\n            .iter()\n            .try_for_each(|param| write!(f, \"; {}\", param))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{ContentDisposition, DispositionParam, DispositionType};\n    use crate::http::header::{Charset, ExtendedValue, HeaderValue};\n\n    #[test]\n    fn test_from_raw_basic() {\n        assert!(ContentDisposition::from_raw(&HeaderValue::from_static(\"\")).is_err());\n\n        let a =\n            HeaderValue::from_static(\"form-data; dummy=3; name=upload; filename=\\\"sample.png\\\"\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Unknown(\"dummy\".to_owned(), \"3\".to_owned()),\n                DispositionParam::Name(\"upload\".to_owned()),\n                DispositionParam::Filename(\"sample.png\".to_owned()),\n            ],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_static(\"attachment; filename=\\\"image.jpg\\\"\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::Filename(\"image.jpg\".to_owned())],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_static(\"inline; filename=image.jpg\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Inline,\n            parameters: vec![DispositionParam::Filename(\"image.jpg\".to_owned())],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_static(\n            \"attachment; creation-date=\\\"Wed, 12 Feb 1997 16:29:51 -0500\\\"\",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::Unknown(\n                String::from(\"creation-date\"),\n                \"Wed, 12 Feb 1997 16:29:51 -0500\".to_owned(),\n            )],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_extended() {\n        let a = HeaderValue::from_static(\n            \"attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates\",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::FilenameExt(ExtendedValue {\n                charset: Charset::Ext(String::from(\"UTF-8\")),\n                language_tag: None,\n                value: vec![\n                    0xc2, 0xa3, 0x20, b'a', b'n', b'd', 0x20, 0xe2, 0x82, 0xac, 0x20, b'r', b'a',\n                    b't', b'e', b's',\n                ],\n            })],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_static(\n            \"attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates\",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![DispositionParam::FilenameExt(ExtendedValue {\n                charset: Charset::Ext(String::from(\"UTF-8\")),\n                language_tag: None,\n                value: vec![\n                    0xc2, 0xa3, 0x20, b'a', b'n', b'd', 0x20, 0xe2, 0x82, 0xac, 0x20, b'r', b'a',\n                    b't', b'e', b's',\n                ],\n            })],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_extra_whitespace() {\n        let a = HeaderValue::from_static(\n            \"form-data  ; du-mmy= 3  ; name =upload ; filename =  \\\"sample.png\\\"  ; \",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Unknown(\"du-mmy\".to_owned(), \"3\".to_owned()),\n                DispositionParam::Name(\"upload\".to_owned()),\n                DispositionParam::Filename(\"sample.png\".to_owned()),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_unordered() {\n        let a = HeaderValue::from_static(\n            \"form-data; dummy=3; filename=\\\"sample.png\\\" ; name=upload;\",\n            // Actually, a trailing semicolon is not compliant. But it is fine to accept.\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Unknown(\"dummy\".to_owned(), \"3\".to_owned()),\n                DispositionParam::Filename(\"sample.png\".to_owned()),\n                DispositionParam::Name(\"upload\".to_owned()),\n            ],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_str(\n            \"attachment; filename*=iso-8859-1''foo-%E4.html; filename=\\\"foo-ä.html\\\"\",\n        )\n        .unwrap();\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![\n                DispositionParam::FilenameExt(ExtendedValue {\n                    charset: Charset::Iso_8859_1,\n                    language_tag: None,\n                    value: b\"foo-\\xe4.html\".to_vec(),\n                }),\n                DispositionParam::Filename(\"foo-ä.html\".to_owned()),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_only_disp() {\n        let a = ContentDisposition::from_raw(&HeaderValue::from_static(\"attachment\")).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Attachment,\n            parameters: vec![],\n        };\n        assert_eq!(a, b);\n\n        let a = ContentDisposition::from_raw(&HeaderValue::from_static(\"inline ;\")).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Inline,\n            parameters: vec![],\n        };\n        assert_eq!(a, b);\n\n        let a =\n            ContentDisposition::from_raw(&HeaderValue::from_static(\"unknown-disp-param\")).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Ext(String::from(\"unknown-disp-param\")),\n            parameters: vec![],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn from_raw_with_mixed_case() {\n        let a = HeaderValue::from_str(\n            \"InLInE; fIlenAME*=iso-8859-1''foo-%E4.html; filEName=\\\"foo-ä.html\\\"\",\n        )\n        .unwrap();\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::Inline,\n            parameters: vec![\n                DispositionParam::FilenameExt(ExtendedValue {\n                    charset: Charset::Iso_8859_1,\n                    language_tag: None,\n                    value: b\"foo-\\xe4.html\".to_vec(),\n                }),\n                DispositionParam::Filename(\"foo-ä.html\".to_owned()),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn from_raw_with_unicode() {\n        /* RFC 7578 Section 4.2:\n        Some commonly deployed systems use multipart/form-data with file names directly encoded\n        including octets outside the US-ASCII range. The encoding used for the file names is\n        typically UTF-8, although HTML forms will use the charset associated with the form.\n\n        Mainstream browsers like Firefox (gecko) and Chrome use UTF-8 directly as above.\n        (And now, only UTF-8 is handled by this implementation.)\n        */\n        let a = HeaderValue::from_str(\"form-data; name=upload; filename=\\\"文件.webp\\\"\").unwrap();\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Name(String::from(\"upload\")),\n                DispositionParam::Filename(String::from(\"文件.webp\")),\n            ],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_str(\n            \"form-data; name=upload; filename=\\\"余固知謇謇之為患兮，忍而不能舍也.pptx\\\"\",\n        )\n        .unwrap();\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Name(String::from(\"upload\")),\n                DispositionParam::Filename(String::from(\"余固知謇謇之為患兮，忍而不能舍也.pptx\")),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_escape() {\n        let a = HeaderValue::from_static(\n            \"form-data; dummy=3; name=upload; filename=\\\"s\\\\amp\\\\\\\"le.png\\\"\",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Unknown(\"dummy\".to_owned(), \"3\".to_owned()),\n                DispositionParam::Name(\"upload\".to_owned()),\n                DispositionParam::Filename(\n                    ['s', 'a', 'm', 'p', '\\\"', 'l', 'e', '.', 'p', 'n', 'g']\n                        .iter()\n                        .collect(),\n                ),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_semicolon() {\n        let a = HeaderValue::from_static(\"form-data; filename=\\\"A semicolon here;.pdf\\\"\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![DispositionParam::Filename(String::from(\n                \"A semicolon here;.pdf\",\n            ))],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_unnecessary_percent_decode() {\n        // In fact, RFC 7578 (multipart/form-data) Section 2 and 4.2 suggests that filename with\n        // non-ASCII characters MAY be percent-encoded.\n        // On the contrary, RFC 6266 or other RFCs related to Content-Disposition response header\n        // do not mention such percent-encoding.\n        // So, it appears to be undecidable whether to percent-decode or not without\n        // knowing the usage scenario (multipart/form-data v.s. HTTP response header) and\n        // inevitable to unnecessarily percent-decode filename with %XX in the former scenario.\n        // Fortunately, it seems that almost all mainstream browsers just send UTF-8 encoded file\n        // names in quoted-string format (tested on Edge, IE11, Chrome and Firefox) without\n        // percent-encoding. So we do not bother to attempt to percent-decode.\n        let a = HeaderValue::from_static(\n            \"form-data; name=photo; filename=\\\"%74%65%73%74%2e%70%6e%67\\\"\",\n        );\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Name(\"photo\".to_owned()),\n                DispositionParam::Filename(String::from(\"%74%65%73%74%2e%70%6e%67\")),\n            ],\n        };\n        assert_eq!(a, b);\n\n        let a = HeaderValue::from_static(\"form-data; name=photo; filename=\\\"%74%65%73%74.png\\\"\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let b = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Name(\"photo\".to_owned()),\n                DispositionParam::Filename(String::from(\"%74%65%73%74.png\")),\n            ],\n        };\n        assert_eq!(a, b);\n    }\n\n    #[test]\n    fn test_from_raw_param_value_missing() {\n        let a = HeaderValue::from_static(\"form-data; name=upload ; filename=\");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n\n        let a = HeaderValue::from_static(\"attachment; dummy=; filename=invoice.pdf\");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n\n        let a = HeaderValue::from_static(\"inline; filename=  \");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n\n        let a = HeaderValue::from_static(\"inline; filename=\\\"\\\"\");\n        assert!(ContentDisposition::from_raw(&a)\n            .expect(\"parse cd\")\n            .get_filename()\n            .expect(\"filename\")\n            .is_empty());\n    }\n\n    #[test]\n    fn test_from_raw_param_name_missing() {\n        let a = HeaderValue::from_static(\"inline; =\\\"test.txt\\\"\");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n\n        let a = HeaderValue::from_static(\"inline; =diary.odt\");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n\n        let a = HeaderValue::from_static(\"inline; =\");\n        assert!(ContentDisposition::from_raw(&a).is_err());\n    }\n\n    #[test]\n    fn test_display_extended() {\n        let as_string = \"attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates\";\n        let a = HeaderValue::from_static(as_string);\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(as_string, display_rendered);\n\n        let a = HeaderValue::from_static(\"attachment; filename=colourful.csv\");\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(\n            \"attachment; filename=\\\"colourful.csv\\\"\".to_owned(),\n            display_rendered\n        );\n    }\n\n    #[test]\n    fn test_display_quote() {\n        let as_string = \"form-data; name=upload; filename=\\\"Quote\\\\\\\"here.png\\\"\";\n        as_string\n            .find(['\\\\', '\\\"'].iter().collect::<String>().as_str())\n            .unwrap(); // ensure `\\\"` is there\n        let a = HeaderValue::from_static(as_string);\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(as_string, display_rendered);\n    }\n\n    #[test]\n    fn test_display_space_tab() {\n        let as_string = \"form-data; name=upload; filename=\\\"Space here.png\\\"\";\n        let a = HeaderValue::from_static(as_string);\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(as_string, display_rendered);\n\n        let a: ContentDisposition = ContentDisposition {\n            disposition: DispositionType::Inline,\n            parameters: vec![DispositionParam::Filename(String::from(\"Tab\\there.png\"))],\n        };\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(\"inline; filename=\\\"Tab\\x09here.png\\\"\", display_rendered);\n    }\n\n    #[test]\n    fn test_display_control_characters() {\n        /* let a = \"attachment; filename=\\\"carriage\\rreturn.png\\\"\";\n        let a = HeaderValue::from_static(a);\n        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(\n            \"attachment; filename=\\\"carriage\\\\\\rreturn.png\\\"\",\n            display_rendered\n        );*/\n        // No way to create a HeaderValue containing a carriage return.\n\n        let a: ContentDisposition = ContentDisposition {\n            disposition: DispositionType::Inline,\n            parameters: vec![DispositionParam::Filename(String::from(\"bell\\x07.png\"))],\n        };\n        let display_rendered = format!(\"{}\", a);\n        assert_eq!(\"inline; filename=\\\"bell\\\\\\x07.png\\\"\", display_rendered);\n    }\n\n    #[test]\n    fn test_param_methods() {\n        let param = DispositionParam::Filename(String::from(\"sample.txt\"));\n        assert!(param.is_filename());\n        assert_eq!(param.as_filename().unwrap(), \"sample.txt\");\n\n        let param = DispositionParam::Unknown(String::from(\"foo\"), String::from(\"bar\"));\n        assert!(param.is_unknown(\"foo\"));\n        assert_eq!(param.as_unknown(\"fOo\"), Some(\"bar\"));\n    }\n\n    #[test]\n    fn test_disposition_methods() {\n        let cd = ContentDisposition {\n            disposition: DispositionType::FormData,\n            parameters: vec![\n                DispositionParam::Unknown(\"dummy\".to_owned(), \"3\".to_owned()),\n                DispositionParam::Name(\"upload\".to_owned()),\n                DispositionParam::Filename(\"sample.png\".to_owned()),\n            ],\n        };\n        assert_eq!(cd.get_name(), Some(\"upload\"));\n        assert_eq!(cd.get_unknown(\"dummy\"), Some(\"3\"));\n        assert_eq!(cd.get_filename(), Some(\"sample.png\"));\n        assert_eq!(cd.get_unknown_ext(\"dummy\"), None);\n        assert_eq!(cd.get_unknown(\"duMMy\"), Some(\"3\"));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/content_language.rs",
    "content": "use language_tags::LanguageTag;\n\nuse super::{common_header, QualityItem, CONTENT_LANGUAGE};\n\ncommon_header! {\n    /// `Content-Language` header, defined\n    /// in [RFC 7231 §3.1.3.2](https://datatracker.ietf.org/doc/html/rfc7231#section-3.1.3.2)\n    ///\n    /// The `Content-Language` header field describes the natural language(s)\n    /// of the intended audience for the representation.  Note that this\n    /// might not be equivalent to all the languages used within the\n    /// representation.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Content-Language = 1#language-tag\n    /// ```\n    ///\n    /// # Example Values\n    /// * `da`\n    /// * `mi, en`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{ContentLanguage, LanguageTag, QualityItem};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     ContentLanguage(vec![\n    ///         QualityItem::max(LanguageTag::parse(\"en\").unwrap()),\n    ///     ])\n    /// );\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{ContentLanguage, LanguageTag, QualityItem};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     ContentLanguage(vec![\n    ///         QualityItem::max(LanguageTag::parse(\"da\").unwrap()),\n    ///         QualityItem::max(LanguageTag::parse(\"en-GB\").unwrap()),\n    ///     ])\n    /// );\n    /// ```\n    (ContentLanguage, CONTENT_LANGUAGE) => (QualityItem<LanguageTag>)+\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(test1, [b\"da\"]);\n        crate::http::header::common_header_test!(test2, [b\"mi, en\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/content_length.rs",
    "content": "use std::{convert::Infallible, str};\n\nuse derive_more::{Deref, DerefMut};\n\nuse crate::{\n    error::ParseError,\n    http::header::{\n        from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue, CONTENT_LENGTH,\n    },\n    HttpMessage,\n};\n\n/// `Content-Length` header, defined in [RFC 9110 §8.6].\n///\n/// The Content-Length\n///\n/// # ABNF\n///\n/// ```plain\n/// Content-Length = 1*DIGIT\n/// ```\n///\n/// # Example Values\n///\n/// - `0`\n/// - `3495`\n///\n/// # Examples\n///\n/// ```\n/// use actix_web::{http::header::ContentLength, HttpResponse};\n///\n/// let res_empty = HttpResponse::Ok()\n///     .insert_header(ContentLength(0));\n///\n/// let res_fake_cl = HttpResponse::Ok()\n///     .insert_header(ContentLength(3_495));\n/// ```\n///\n/// [RFC 9110 §8.6]: https://www.rfc-editor.org/rfc/rfc9110#name-content-length\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut)]\npub struct ContentLength(pub usize);\n\nimpl ContentLength {\n    /// Returns Content-Length value.\n    pub fn into_inner(&self) -> usize {\n        self.0\n    }\n}\n\nimpl str::FromStr for ContentLength {\n    type Err = <usize as str::FromStr>::Err;\n\n    #[inline]\n    fn from_str(val: &str) -> Result<Self, Self::Err> {\n        let val = val.trim();\n\n        // decoder prevents this case\n        debug_assert!(!val.starts_with('+'));\n\n        val.parse().map(Self)\n    }\n}\n\nimpl TryIntoHeaderValue for ContentLength {\n    type Error = Infallible;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        Ok(HeaderValue::from(self.0))\n    }\n}\n\nimpl Header for ContentLength {\n    fn name() -> HeaderName {\n        CONTENT_LENGTH\n    }\n\n    fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {\n        let val = from_one_raw_str(msg.headers().get(Self::name()))?;\n\n        // decoder prevents multiple CL headers\n        debug_assert_eq!(msg.headers().get_all(Self::name()).count(), 1);\n\n        Ok(val)\n    }\n}\n\nimpl From<ContentLength> for usize {\n    fn from(ContentLength(len): ContentLength) -> Self {\n        len\n    }\n}\n\nimpl From<usize> for ContentLength {\n    fn from(len: usize) -> Self {\n        ContentLength(len)\n    }\n}\n\nimpl PartialEq<usize> for ContentLength {\n    fn eq(&self, other: &usize) -> bool {\n        self.0 == *other\n    }\n}\n\nimpl PartialEq<ContentLength> for usize {\n    fn eq(&self, other: &ContentLength) -> bool {\n        *self == other.0\n    }\n}\n\nimpl PartialOrd<usize> for ContentLength {\n    fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {\n        self.0.partial_cmp(other)\n    }\n}\n\nimpl PartialOrd<ContentLength> for usize {\n    fn partial_cmp(&self, other: &ContentLength) -> Option<std::cmp::Ordering> {\n        self.partial_cmp(&other.0)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::fmt;\n\n    use super::*;\n    use crate::{test::TestRequest, HttpRequest};\n\n    fn req_from_raw_headers<H: Header, I: IntoIterator<Item = V>, V: AsRef<[u8]>>(\n        header_lines: I,\n    ) -> HttpRequest {\n        header_lines\n            .into_iter()\n            .fold(TestRequest::default(), |req, item| {\n                req.append_header((H::name(), item.as_ref().to_vec()))\n            })\n            .to_http_request()\n    }\n\n    #[track_caller]\n    pub(crate) fn assert_parse_fail<\n        H: Header + fmt::Debug,\n        I: IntoIterator<Item = V>,\n        V: AsRef<[u8]>,\n    >(\n        headers: I,\n    ) {\n        let req = req_from_raw_headers::<H, _, _>(headers);\n        H::parse(&req).unwrap_err();\n    }\n\n    #[track_caller]\n    pub(crate) fn assert_parse_eq<\n        H: Header + fmt::Debug + PartialEq,\n        I: IntoIterator<Item = V>,\n        V: AsRef<[u8]>,\n    >(\n        headers: I,\n        expect: H,\n    ) {\n        let req = req_from_raw_headers::<H, _, _>(headers);\n        assert_eq!(H::parse(&req).unwrap(), expect);\n    }\n\n    #[test]\n    fn missing_header() {\n        assert_parse_fail::<ContentLength, _, _>([\"\"; 0]);\n        assert_parse_fail::<ContentLength, _, _>([\"\"]);\n    }\n\n    #[test]\n    fn bad_header() {\n        assert_parse_fail::<ContentLength, _, _>([\"-123\"]);\n        assert_parse_fail::<ContentLength, _, _>([\"123_456\"]);\n        assert_parse_fail::<ContentLength, _, _>([\"123.456\"]);\n\n        // too large for u64 (2^64, 2^64 + 1)\n        assert_parse_fail::<ContentLength, _, _>([\"18446744073709551616\"]);\n        assert_parse_fail::<ContentLength, _, _>([\"18446744073709551617\"]);\n\n        // hex notation\n        assert_parse_fail::<ContentLength, _, _>([\"0x123\"]);\n\n        // multi-value\n        assert_parse_fail::<ContentLength, _, _>([\"0, 123\"]);\n    }\n\n    #[test]\n    #[should_panic]\n    fn bad_header_plus() {\n        // prevented by HTTP decoder anyway\n        assert_parse_fail::<ContentLength, _, _>([\"+123\"]);\n    }\n\n    #[test]\n    #[should_panic]\n    fn bad_multiple_value() {\n        // prevented by HTTP decoder anyway\n        assert_parse_fail::<ContentLength, _, _>([\"0\", \"123\"]);\n    }\n\n    #[test]\n    fn good_header() {\n        assert_parse_eq::<ContentLength, _, _>([\"0\"], ContentLength(0));\n        assert_parse_eq::<ContentLength, _, _>([\"1\"], ContentLength(1));\n        assert_parse_eq::<ContentLength, _, _>([\"123\"], ContentLength(123));\n\n        // value that looks like octal notation is not interpreted as such\n        assert_parse_eq::<ContentLength, _, _>([\"0123\"], ContentLength(123));\n\n        // whitespace variations\n        assert_parse_eq::<ContentLength, _, _>([\" 0\"], ContentLength(0));\n        assert_parse_eq::<ContentLength, _, _>([\"0 \"], ContentLength(0));\n        assert_parse_eq::<ContentLength, _, _>([\" 0 \"], ContentLength(0));\n\n        // large value (2^64 - 1)\n        assert_parse_eq::<ContentLength, _, _>(\n            [\"18446744073709551615\"],\n            ContentLength(18_446_744_073_709_551_615),\n        );\n    }\n\n    #[test]\n    fn equality() {\n        assert!(ContentLength(0) == ContentLength(0));\n        assert!(ContentLength(0) == 0);\n        assert!(0 != ContentLength(123));\n    }\n\n    #[test]\n    fn ordering() {\n        assert!(ContentLength(0) < ContentLength(123));\n        assert!(ContentLength(0) < 123);\n        assert!(0 < ContentLength(123));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/content_range.rs",
    "content": "use std::{\n    fmt::{self, Display, Write},\n    str::FromStr,\n};\n\nuse super::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer, CONTENT_RANGE};\nuse crate::error::ParseError;\n\ncrate::http::header::common_header! {\n    /// `Content-Range` header, defined\n    /// in [RFC 7233 §4.2](https://datatracker.ietf.org/doc/html/rfc7233#section-4.2)\n    (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(test_bytes,\n            [b\"bytes 0-499/500\"],\n            Some(ContentRange(ContentRangeSpec::Bytes {\n                range: Some((0, 499)),\n                instance_length: Some(500)\n            })));\n\n        crate::http::header::common_header_test!(test_bytes_unknown_len,\n            [b\"bytes 0-499/*\"],\n            Some(ContentRange(ContentRangeSpec::Bytes {\n                range: Some((0, 499)),\n                instance_length: None\n            })));\n\n        crate::http::header::common_header_test!(test_bytes_unknown_range,\n            [b\"bytes */500\"],\n            Some(ContentRange(ContentRangeSpec::Bytes {\n                range: None,\n                instance_length: Some(500)\n            })));\n\n        crate::http::header::common_header_test!(test_unregistered,\n            [b\"seconds 1-2\"],\n            Some(ContentRange(ContentRangeSpec::Unregistered {\n                unit: \"seconds\".to_owned(),\n                resp: \"1-2\".to_owned()\n            })));\n\n        crate::http::header::common_header_test!(test_no_len,\n            [b\"bytes 0-499\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_only_unit,\n            [b\"bytes\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_end_less_than_start,\n            [b\"bytes 499-0/500\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_blank,\n            [b\"\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_bytes_many_spaces,\n            [b\"bytes 1-2/500 3\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_bytes_many_slashes,\n            [b\"bytes 1-2/500/600\"],\n            None::<ContentRange>);\n\n        crate::http::header::common_header_test!(test_bytes_many_dashes,\n            [b\"bytes 1-2-3/500\"],\n            None::<ContentRange>);\n    }\n}\n\n/// Content-Range header, defined\n/// in [RFC 7233 §4.2](https://datatracker.ietf.org/doc/html/rfc7233#section-4.2)\n///\n/// # ABNF\n/// ```plain\n/// Content-Range       = byte-content-range\n///                     / other-content-range\n///\n/// byte-content-range  = bytes-unit SP\n///                       ( byte-range-resp / unsatisfied-range )\n///\n/// byte-range-resp     = byte-range \"/\" ( complete-length / \"*\" )\n/// byte-range          = first-byte-pos \"-\" last-byte-pos\n/// unsatisfied-range   = \"*/\" complete-length\n///\n/// complete-length     = 1*DIGIT\n///\n/// other-content-range = other-range-unit SP other-range-resp\n/// other-range-resp    = *CHAR\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ContentRangeSpec {\n    /// Byte range\n    Bytes {\n        /// First and last bytes of the range, omitted if request could not be\n        /// satisfied\n        range: Option<(u64, u64)>,\n\n        /// Total length of the instance, can be omitted if unknown\n        instance_length: Option<u64>,\n    },\n\n    /// Custom range, with unit not registered at IANA\n    Unregistered {\n        /// other-range-unit\n        unit: String,\n\n        /// other-range-resp\n        resp: String,\n    },\n}\n\nimpl FromStr for ContentRangeSpec {\n    type Err = ParseError;\n\n    fn from_str(s: &str) -> Result<Self, ParseError> {\n        let res = match s.split_once(' ') {\n            Some((\"bytes\", resp)) => {\n                let (range, instance_length) = resp.split_once('/').ok_or(ParseError::Header)?;\n\n                let instance_length = if instance_length == \"*\" {\n                    None\n                } else {\n                    Some(instance_length.parse().map_err(|_| ParseError::Header)?)\n                };\n\n                let range = if range == \"*\" {\n                    None\n                } else {\n                    let (first_byte, last_byte) =\n                        range.split_once('-').ok_or(ParseError::Header)?;\n                    let first_byte = first_byte.parse().map_err(|_| ParseError::Header)?;\n                    let last_byte = last_byte.parse().map_err(|_| ParseError::Header)?;\n                    if last_byte < first_byte {\n                        return Err(ParseError::Header);\n                    }\n                    Some((first_byte, last_byte))\n                };\n\n                ContentRangeSpec::Bytes {\n                    range,\n                    instance_length,\n                }\n            }\n            Some((unit, resp)) => ContentRangeSpec::Unregistered {\n                unit: unit.to_owned(),\n                resp: resp.to_owned(),\n            },\n            _ => return Err(ParseError::Header),\n        };\n        Ok(res)\n    }\n}\n\nimpl Display for ContentRangeSpec {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            ContentRangeSpec::Bytes {\n                range,\n                instance_length,\n            } => {\n                f.write_str(\"bytes \")?;\n                match range {\n                    Some((first_byte, last_byte)) => {\n                        write!(f, \"{}-{}\", first_byte, last_byte)?;\n                    }\n                    None => {\n                        f.write_str(\"*\")?;\n                    }\n                };\n                f.write_str(\"/\")?;\n                if let Some(v) = instance_length {\n                    write!(f, \"{}\", v)\n                } else {\n                    f.write_str(\"*\")\n                }\n            }\n            ContentRangeSpec::Unregistered { ref unit, ref resp } => {\n                f.write_str(unit)?;\n                f.write_str(\" \")?;\n                f.write_str(resp)\n            }\n        }\n    }\n}\n\nimpl TryIntoHeaderValue for ContentRangeSpec {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        let mut writer = Writer::new();\n        let _ = write!(&mut writer, \"{}\", self);\n        HeaderValue::from_maybe_shared(writer.take())\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/content_type.rs",
    "content": "use mime::Mime;\n\nuse super::CONTENT_TYPE;\n\ncrate::http::header::common_header! {\n    /// `Content-Type` header, defined in [RFC 9110 §8.3].\n    ///\n    /// The `Content-Type` header field indicates the media type of the associated representation:\n    /// either the representation enclosed in the message payload or the selected representation,\n    /// as determined by the message semantics. The indicated media type defines both the data\n    /// format and how that data is intended to be processed by a recipient, within the scope of the\n    /// received message semantics, after any content codings indicated by Content-Encoding are\n    /// decoded.\n    ///\n    /// Although the `mime` crate allows the mime options to be any slice, this crate forces the use\n    /// of Vec. This is to make sure the same header can't have more than 1 type. If this is an\n    /// issue, it's possible to implement `Header` on a custom struct.\n    ///\n    /// # ABNF\n    ///\n    /// ```plain\n    /// Content-Type = media-type\n    /// ```\n    ///\n    /// # Example Values\n    ///\n    /// - `text/html; charset=utf-8`\n    /// - `application/json`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use actix_web::{http::header::ContentType, HttpResponse};\n    ///\n    /// let res_json = HttpResponse::Ok()\n    ///     .insert_header(ContentType::json());\n    ///\n    /// let res_html = HttpResponse::Ok()\n    ///     .insert_header(ContentType(mime::TEXT_HTML));\n    /// ```\n    ///\n    /// [RFC 9110 §8.3]: https://datatracker.ietf.org/doc/html/rfc9110#section-8.3\n    (ContentType, CONTENT_TYPE) => [Mime]\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(\n            test_text_html,\n            [b\"text/html\"],\n            Some(HeaderField(mime::TEXT_HTML)));\n        crate::http::header::common_header_test!(\n            test_image_star,\n            [b\"image/*\"],\n            Some(HeaderField(mime::IMAGE_STAR)));\n\n    }\n}\n\nimpl ContentType {\n    /// Constructs a `Content-Type: application/json` header.\n    #[inline]\n    pub fn json() -> ContentType {\n        ContentType(mime::APPLICATION_JSON)\n    }\n\n    /// Constructs a `Content-Type: text/plain; charset=utf-8` header.\n    #[inline]\n    pub fn plaintext() -> ContentType {\n        ContentType(mime::TEXT_PLAIN_UTF_8)\n    }\n\n    /// Constructs a `Content-Type: text/html; charset=utf-8` header.\n    #[inline]\n    pub fn html() -> ContentType {\n        ContentType(mime::TEXT_HTML_UTF_8)\n    }\n\n    /// Constructs a `Content-Type: text/xml` header.\n    #[inline]\n    pub fn xml() -> ContentType {\n        ContentType(mime::TEXT_XML)\n    }\n\n    /// Constructs a `Content-Type: application/www-form-url-encoded` header.\n    #[inline]\n    pub fn form_url_encoded() -> ContentType {\n        ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)\n    }\n\n    /// Constructs a `Content-Type: image/jpeg` header.\n    #[inline]\n    pub fn jpeg() -> ContentType {\n        ContentType(mime::IMAGE_JPEG)\n    }\n\n    /// Constructs a `Content-Type: image/png` header.\n    #[inline]\n    pub fn png() -> ContentType {\n        ContentType(mime::IMAGE_PNG)\n    }\n\n    /// Constructs a `Content-Type: application/octet-stream` header.\n    #[inline]\n    pub fn octet_stream() -> ContentType {\n        ContentType(mime::APPLICATION_OCTET_STREAM)\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/date.rs",
    "content": "use std::time::SystemTime;\n\nuse super::{HttpDate, DATE};\n\ncrate::http::header::common_header! {\n    /// `Date` header, defined\n    /// in [RFC 7231 §7.1.1.2](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.1.2)\n    ///\n    /// The `Date` header field represents the date and time at which the\n    /// message was originated.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Date = HTTP-date\n    /// ```\n    ///\n    /// # Example Values\n    /// * `Tue, 15 Nov 1994 08:12:31 GMT`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::time::SystemTime;\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::Date;\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     Date(SystemTime::now().into())\n    /// );\n    /// ```\n    (Date, DATE) => [HttpDate]\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(test1, [b\"Tue, 15 Nov 1994 08:12:31 GMT\"]);\n    }\n}\n\nimpl Date {\n    /// Create a date instance set to the current system time\n    pub fn now() -> Date {\n        Date(SystemTime::now().into())\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/encoding.rs",
    "content": "use std::{fmt, str};\n\nuse actix_http::ContentEncoding;\n\n/// A value to represent an encoding used in the `Accept-Encoding` and `Content-Encoding` header.\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub enum Encoding {\n    /// A supported content encoding. See [`ContentEncoding`] for variants.\n    Known(ContentEncoding),\n\n    /// Some other encoding that is less common, can be any string.\n    Unknown(String),\n}\n\nimpl Encoding {\n    pub const fn identity() -> Self {\n        Self::Known(ContentEncoding::Identity)\n    }\n\n    pub const fn brotli() -> Self {\n        Self::Known(ContentEncoding::Brotli)\n    }\n\n    pub const fn deflate() -> Self {\n        Self::Known(ContentEncoding::Deflate)\n    }\n\n    pub const fn gzip() -> Self {\n        Self::Known(ContentEncoding::Gzip)\n    }\n\n    pub const fn zstd() -> Self {\n        Self::Known(ContentEncoding::Zstd)\n    }\n}\n\nimpl fmt::Display for Encoding {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(match self {\n            Encoding::Known(enc) => enc.as_str(),\n            Encoding::Unknown(enc) => enc.as_str(),\n        })\n    }\n}\n\nimpl str::FromStr for Encoding {\n    type Err = crate::error::ParseError;\n\n    fn from_str(enc: &str) -> Result<Self, crate::error::ParseError> {\n        match enc.parse::<ContentEncoding>() {\n            Ok(enc) => Ok(Self::Known(enc)),\n            Err(_) => Ok(Self::Unknown(enc.to_owned())),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/entity.rs",
    "content": "use std::{\n    fmt::{self, Display, Write},\n    str::FromStr,\n};\n\nuse super::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer};\n\n/// check that each char in the slice is either:\n/// 1. `%x21`, or\n/// 2. in the range `%x23` to `%x7E`, or\n/// 3. above `%x80`\nfn entity_validate_char(c: u8) -> bool {\n    c == 0x21 || (0x23..=0x7e).contains(&c) || (c >= 0x80)\n}\n\nfn check_slice_validity(slice: &str) -> bool {\n    slice.bytes().all(entity_validate_char)\n}\n\n/// An entity tag, defined in [RFC 7232 §2.3].\n///\n/// An entity tag consists of a string enclosed by two literal double quotes.\n/// Preceding the first double quote is an optional weakness indicator,\n/// which always looks like `W/`. Examples for valid tags are `\"xyzzy\"` and\n/// `W/\"xyzzy\"`.\n///\n/// # ABNF\n/// ```plain\n/// entity-tag = [ weak ] opaque-tag\n/// weak       = %x57.2F ; \"W/\", case-sensitive\n/// opaque-tag = DQUOTE *etagc DQUOTE\n/// etagc      = %x21 / %x23-7E / obs-text\n///            ; VCHAR except double quotes, plus obs-text\n/// ```\n///\n/// # Comparison\n/// To check if two entity tags are equivalent in an application always use the\n/// `strong_eq` or `weak_eq` methods based on the context of the Tag. Only use\n/// `==` to check if two tags are identical.\n///\n/// The example below shows the results for a set of entity-tag pairs and\n/// both the weak and strong comparison function results:\n///\n/// | `ETag 1`| `ETag 2`| Strong Comparison | Weak Comparison |\n/// |---------|---------|-------------------|-----------------|\n/// | `W/\"1\"` | `W/\"1\"` | no match          | match           |\n/// | `W/\"1\"` | `W/\"2\"` | no match          | no match        |\n/// | `W/\"1\"` | `\"1\"`   | no match          | match           |\n/// | `\"1\"`   | `\"1\"`   | match             | match           |\n///\n/// [RFC 7232 §2.3](https://datatracker.ietf.org/doc/html/rfc7232#section-2.3)\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct EntityTag {\n    /// Weakness indicator for the tag\n    pub weak: bool,\n\n    /// The opaque string in between the DQUOTEs\n    tag: String,\n}\n\nimpl EntityTag {\n    /// Constructs a new `EntityTag`.\n    ///\n    /// # Panics\n    /// If the tag contains invalid characters.\n    pub fn new(weak: bool, tag: String) -> EntityTag {\n        assert!(check_slice_validity(&tag), \"Invalid tag: {:?}\", tag);\n        EntityTag { weak, tag }\n    }\n\n    /// Constructs a new weak EntityTag.\n    ///\n    /// # Panics\n    /// If the tag contains invalid characters.\n    pub fn new_weak(tag: String) -> EntityTag {\n        EntityTag::new(true, tag)\n    }\n\n    #[deprecated(since = \"3.0.0\", note = \"Renamed to `new_weak`.\")]\n    pub fn weak(tag: String) -> EntityTag {\n        Self::new_weak(tag)\n    }\n\n    /// Constructs a new strong EntityTag.\n    ///\n    /// # Panics\n    /// If the tag contains invalid characters.\n    pub fn new_strong(tag: String) -> EntityTag {\n        EntityTag::new(false, tag)\n    }\n\n    #[deprecated(since = \"3.0.0\", note = \"Renamed to `new_strong`.\")]\n    pub fn strong(tag: String) -> EntityTag {\n        Self::new_strong(tag)\n    }\n\n    /// Returns tag.\n    pub fn tag(&self) -> &str {\n        self.tag.as_ref()\n    }\n\n    /// Sets tag.\n    ///\n    /// # Panics\n    /// If the tag contains invalid characters.\n    pub fn set_tag(&mut self, tag: impl Into<String>) {\n        let tag = tag.into();\n        assert!(check_slice_validity(&tag), \"Invalid tag: {:?}\", tag);\n        self.tag = tag\n    }\n\n    /// For strong comparison two entity-tags are equivalent if both are not weak and their\n    /// opaque-tags match character-by-character.\n    pub fn strong_eq(&self, other: &EntityTag) -> bool {\n        !self.weak && !other.weak && self.tag == other.tag\n    }\n\n    /// For weak comparison two entity-tags are equivalent if their opaque-tags match\n    /// character-by-character, regardless of either or both being tagged as \"weak\".\n    pub fn weak_eq(&self, other: &EntityTag) -> bool {\n        self.tag == other.tag\n    }\n\n    /// Returns the inverse of `strong_eq()`.\n    pub fn strong_ne(&self, other: &EntityTag) -> bool {\n        !self.strong_eq(other)\n    }\n\n    /// Returns inverse of `weak_eq()`.\n    pub fn weak_ne(&self, other: &EntityTag) -> bool {\n        !self.weak_eq(other)\n    }\n}\n\nimpl Display for EntityTag {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if self.weak {\n            write!(f, \"W/\\\"{}\\\"\", self.tag)\n        } else {\n            write!(f, \"\\\"{}\\\"\", self.tag)\n        }\n    }\n}\n\nimpl FromStr for EntityTag {\n    type Err = crate::error::ParseError;\n\n    fn from_str(slice: &str) -> Result<EntityTag, crate::error::ParseError> {\n        let length = slice.len();\n        // Early exits if it doesn't terminate in a DQUOTE.\n        if !slice.ends_with('\"') || slice.len() < 2 {\n            return Err(crate::error::ParseError::Header);\n        }\n        // The etag is weak if its first char is not a DQUOTE.\n        if slice.len() >= 2 && slice.starts_with('\"') && check_slice_validity(&slice[1..length - 1])\n        {\n            // No need to check if the last char is a DQUOTE,\n            // we already did that above.\n            return Ok(EntityTag {\n                weak: false,\n                tag: slice[1..length - 1].to_owned(),\n            });\n        } else if slice.len() >= 4\n            && slice.starts_with(\"W/\\\"\")\n            && check_slice_validity(&slice[3..length - 1])\n        {\n            return Ok(EntityTag {\n                weak: true,\n                tag: slice[3..length - 1].to_owned(),\n            });\n        }\n        Err(crate::error::ParseError::Header)\n    }\n}\n\nimpl TryIntoHeaderValue for EntityTag {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        let mut wrt = Writer::new();\n        write!(wrt, \"{}\", self).unwrap();\n        HeaderValue::from_maybe_shared(wrt.take())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::EntityTag;\n\n    #[test]\n    fn test_etag_parse_success() {\n        // Expected success\n        assert_eq!(\n            \"\\\"foobar\\\"\".parse::<EntityTag>().unwrap(),\n            EntityTag::new_strong(\"foobar\".to_owned())\n        );\n        assert_eq!(\n            \"\\\"\\\"\".parse::<EntityTag>().unwrap(),\n            EntityTag::new_strong(\"\".to_owned())\n        );\n        assert_eq!(\n            \"W/\\\"weaktag\\\"\".parse::<EntityTag>().unwrap(),\n            EntityTag::new_weak(\"weaktag\".to_owned())\n        );\n        assert_eq!(\n            \"W/\\\"\\x65\\x62\\\"\".parse::<EntityTag>().unwrap(),\n            EntityTag::new_weak(\"\\x65\\x62\".to_owned())\n        );\n        assert_eq!(\n            \"W/\\\"\\\"\".parse::<EntityTag>().unwrap(),\n            EntityTag::new_weak(\"\".to_owned())\n        );\n    }\n\n    #[test]\n    fn test_etag_parse_failures() {\n        // Expected failures\n        assert!(\"no-dquotes\".parse::<EntityTag>().is_err());\n        assert!(\"w/\\\"the-first-w-is-case-sensitive\\\"\"\n            .parse::<EntityTag>()\n            .is_err());\n        assert!(\"\".parse::<EntityTag>().is_err());\n        assert!(\"\\\"unmatched-dquotes1\".parse::<EntityTag>().is_err());\n        assert!(\"unmatched-dquotes2\\\"\".parse::<EntityTag>().is_err());\n        assert!(\"matched-\\\"dquotes\\\"\".parse::<EntityTag>().is_err());\n    }\n\n    #[test]\n    fn test_etag_fmt() {\n        assert_eq!(\n            format!(\"{}\", EntityTag::new_strong(\"foobar\".to_owned())),\n            \"\\\"foobar\\\"\"\n        );\n        assert_eq!(format!(\"{}\", EntityTag::new_strong(\"\".to_owned())), \"\\\"\\\"\");\n        assert_eq!(\n            format!(\"{}\", EntityTag::new_weak(\"weak-etag\".to_owned())),\n            \"W/\\\"weak-etag\\\"\"\n        );\n        assert_eq!(\n            format!(\"{}\", EntityTag::new_weak(\"\\u{0065}\".to_owned())),\n            \"W/\\\"\\x65\\\"\"\n        );\n        assert_eq!(format!(\"{}\", EntityTag::new_weak(\"\".to_owned())), \"W/\\\"\\\"\");\n    }\n\n    #[test]\n    fn test_cmp() {\n        // | ETag 1  | ETag 2  | Strong Comparison | Weak Comparison |\n        // |---------|---------|-------------------|-----------------|\n        // | `W/\"1\"` | `W/\"1\"` | no match          | match           |\n        // | `W/\"1\"` | `W/\"2\"` | no match          | no match        |\n        // | `W/\"1\"` | `\"1\"`   | no match          | match           |\n        // | `\"1\"`   | `\"1\"`   | match             | match           |\n        let mut etag1 = EntityTag::new_weak(\"1\".to_owned());\n        let mut etag2 = EntityTag::new_weak(\"1\".to_owned());\n        assert!(!etag1.strong_eq(&etag2));\n        assert!(etag1.weak_eq(&etag2));\n        assert!(etag1.strong_ne(&etag2));\n        assert!(!etag1.weak_ne(&etag2));\n\n        etag1 = EntityTag::new_weak(\"1\".to_owned());\n        etag2 = EntityTag::new_weak(\"2\".to_owned());\n        assert!(!etag1.strong_eq(&etag2));\n        assert!(!etag1.weak_eq(&etag2));\n        assert!(etag1.strong_ne(&etag2));\n        assert!(etag1.weak_ne(&etag2));\n\n        etag1 = EntityTag::new_weak(\"1\".to_owned());\n        etag2 = EntityTag::new_strong(\"1\".to_owned());\n        assert!(!etag1.strong_eq(&etag2));\n        assert!(etag1.weak_eq(&etag2));\n        assert!(etag1.strong_ne(&etag2));\n        assert!(!etag1.weak_ne(&etag2));\n\n        etag1 = EntityTag::new_strong(\"1\".to_owned());\n        etag2 = EntityTag::new_strong(\"1\".to_owned());\n        assert!(etag1.strong_eq(&etag2));\n        assert!(etag1.weak_eq(&etag2));\n        assert!(!etag1.strong_ne(&etag2));\n        assert!(!etag1.weak_ne(&etag2));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/etag.rs",
    "content": "use super::{EntityTag, ETAG};\n\ncrate::http::header::common_header! {\n    /// `ETag` header, defined in\n    /// [RFC 7232 §2.3](https://datatracker.ietf.org/doc/html/rfc7232#section-2.3)\n    ///\n    /// The `ETag` header field in a response provides the current entity-tag\n    /// for the selected representation, as determined at the conclusion of\n    /// handling the request.  An entity-tag is an opaque validator for\n    /// differentiating between multiple representations of the same\n    /// resource, regardless of whether those multiple representations are\n    /// due to resource state changes over time, content negotiation\n    /// resulting in multiple representations being valid at the same time,\n    /// or both.  An entity-tag consists of an opaque quoted string, possibly\n    /// prefixed by a weakness indicator.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// ETag       = entity-tag\n    /// ```\n    ///\n    /// # Example Values\n    /// * `\"xyzzy\"`\n    /// * `W/\"xyzzy\"`\n    /// * `\"\"`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{ETag, EntityTag};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     ETag(EntityTag::new_strong(\"xyzzy\".to_owned()))\n    /// );\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::{ETag, EntityTag};\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// builder.insert_header(\n    ///     ETag(EntityTag::new_weak(\"xyzzy\".to_owned()))\n    /// );\n    /// ```\n    (ETag, ETAG) => [EntityTag]\n\n    test_parse_and_format {\n        // From the RFC\n        crate::http::header::common_header_test!(test1,\n            [b\"\\\"xyzzy\\\"\"],\n            Some(ETag(EntityTag::new_strong(\"xyzzy\".to_owned()))));\n        crate::http::header::common_header_test!(test2,\n            [b\"W/\\\"xyzzy\\\"\"],\n            Some(ETag(EntityTag::new_weak(\"xyzzy\".to_owned()))));\n        crate::http::header::common_header_test!(test3,\n            [b\"\\\"\\\"\"],\n            Some(ETag(EntityTag::new_strong(\"\".to_owned()))));\n        // Own tests\n        crate::http::header::common_header_test!(test4,\n            [b\"\\\"foobar\\\"\"],\n            Some(ETag(EntityTag::new_strong(\"foobar\".to_owned()))));\n        crate::http::header::common_header_test!(test5,\n            [b\"\\\"\\\"\"],\n            Some(ETag(EntityTag::new_strong(\"\".to_owned()))));\n        crate::http::header::common_header_test!(test6,\n            [b\"W/\\\"weak-etag\\\"\"],\n            Some(ETag(EntityTag::new_weak(\"weak-etag\".to_owned()))));\n        crate::http::header::common_header_test!(test7,\n            [b\"W/\\\"\\x65\\x62\\\"\"],\n            Some(ETag(EntityTag::new_weak(\"\\u{0065}\\u{0062}\".to_owned()))));\n        crate::http::header::common_header_test!(test8,\n            [b\"W/\\\"\\\"\"],\n            Some(ETag(EntityTag::new_weak(\"\".to_owned()))));\n        crate::http::header::common_header_test!(test9,\n            [b\"no-dquotes\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test10,\n            [b\"w/\\\"the-first-w-is-case-sensitive\\\"\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test11,\n            [b\"\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test12,\n            [b\"\\\"unmatched-dquotes1\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test13,\n            [b\"unmatched-dquotes2\\\"\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test14,\n            [b\"matched-\\\"dquotes\\\"\"],\n            None::<ETag>);\n        crate::http::header::common_header_test!(test15,\n            [b\"\\\"\"],\n            None::<ETag>);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/expires.rs",
    "content": "use super::{HttpDate, EXPIRES};\n\ncrate::http::header::common_header! {\n    /// `Expires` header, defined\n    /// in [RFC 7234 §5.3](https://datatracker.ietf.org/doc/html/rfc7234#section-5.3)\n    ///\n    /// The `Expires` header field gives the date/time after which the\n    /// response is considered stale.\n    ///\n    /// The presence of an Expires field does not imply that the original\n    /// resource will change or cease to exist at, before, or after that\n    /// time.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Expires = HTTP-date\n    /// ```\n    ///\n    /// # Example Values\n    /// * `Thu, 01 Dec 1994 16:00:00 GMT`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::time::{SystemTime, Duration};\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::Expires;\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// let expiration = SystemTime::now() + Duration::from_secs(60 * 60 * 24);\n    /// builder.insert_header(\n    ///     Expires(expiration.into())\n    /// );\n    /// ```\n    (Expires, EXPIRES) => [HttpDate]\n\n    test_parse_and_format {\n        // Test case from RFC\n        crate::http::header::common_header_test!(test1, [b\"Thu, 01 Dec 1994 16:00:00 GMT\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/if_match.rs",
    "content": "use super::{common_header, EntityTag, IF_MATCH};\n\ncommon_header! {\n    /// `If-Match` header, defined\n    /// in [RFC 7232 §3.1](https://datatracker.ietf.org/doc/html/rfc7232#section-3.1)\n    ///\n    /// The `If-Match` header field makes the request method conditional on\n    /// the recipient origin server either having at least one current\n    /// representation of the target resource, when the field-value is \"*\",\n    /// or having a current representation of the target resource that has an\n    /// entity-tag matching a member of the list of entity-tags provided in\n    /// the field-value.\n    ///\n    /// An origin server MUST use the strong comparison function when\n    /// comparing entity-tags for `If-Match`, since the client\n    /// intends this precondition to prevent the method from being applied if\n    /// there have been any changes to the representation data.\n    ///\n    /// # Note\n    /// This is a request header used for conditional requests (typically to avoid lost updates).\n    /// Servers should not send `If-Match` in responses; use [`ETag`](super::ETag) to describe the\n    /// current representation instead.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// If-Match = \"*\" / 1#entity-tag\n    /// ```\n    ///\n    /// # Example Values\n    /// * `\"xyzzy\"`\n    /// * \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::IfMatch, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfMatch::Any)\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{EntityTag, IfMatch}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfMatch::Items(vec![\n    ///         EntityTag::new(false, \"xyzzy\".to_owned()),\n    ///         EntityTag::new(false, \"foobar\".to_owned()),\n    ///         EntityTag::new(false, \"bazquux\".to_owned()),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (IfMatch, IF_MATCH) => {Any / (EntityTag)+}\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(\n            test1,\n            [b\"\\\"xyzzy\\\"\"],\n            Some(HeaderField::Items(\n                vec![EntityTag::new_strong(\"xyzzy\".to_owned())])));\n\n        crate::http::header::common_header_test!(\n            test2,\n            [b\"\\\"xyzzy\\\", \\\"r2d2xxxx\\\", \\\"c3piozzzz\\\"\"],\n            Some(HeaderField::Items(\n                vec![EntityTag::new_strong(\"xyzzy\".to_owned()),\n                     EntityTag::new_strong(\"r2d2xxxx\".to_owned()),\n                     EntityTag::new_strong(\"c3piozzzz\".to_owned())])));\n        crate::http::header::common_header_test!(test3, [b\"*\"], Some(IfMatch::Any));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/if_modified_since.rs",
    "content": "use super::{HttpDate, IF_MODIFIED_SINCE};\n\ncrate::http::header::common_header! {\n    /// `If-Modified-Since` header, defined\n    /// in [RFC 7232 §3.3](https://datatracker.ietf.org/doc/html/rfc7232#section-3.3)\n    ///\n    /// The `If-Modified-Since` header field makes a GET or HEAD request\n    /// method conditional on the selected representation's modification date\n    /// being more recent than the date provided in the field-value.\n    /// Transfer of the selected representation's data is avoided if that\n    /// data has not changed.\n    ///\n    /// # Note\n    /// This is a request header used for cache validation. Servers should not send\n    /// `If-Modified-Since` in responses; use [`LastModified`](super::LastModified) / the\n    /// `Last-Modified` header instead.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// If-Modified-Since = HTTP-date\n    /// ```\n    ///\n    /// # Example Values\n    /// * `Sat, 29 Oct 1994 19:43:31 GMT`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::time::{SystemTime, Duration};\n    /// use actix_web::{http::header::IfModifiedSince, test};\n    ///\n    /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfModifiedSince(modified.into()))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (IfModifiedSince, IF_MODIFIED_SINCE) => [HttpDate]\n\n    test_parse_and_format {\n        // Test case from RFC\n        crate::http::header::common_header_test!(test1, [b\"Sat, 29 Oct 1994 19:43:31 GMT\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/if_none_match.rs",
    "content": "use super::{EntityTag, IF_NONE_MATCH};\n\ncrate::http::header::common_header! {\n    /// `If-None-Match` header, defined\n    /// in [RFC 7232 §3.2](https://datatracker.ietf.org/doc/html/rfc7232#section-3.2)\n    ///\n    /// The `If-None-Match` header field makes the request method conditional\n    /// on a recipient cache or origin server either not having any current\n    /// representation of the target resource, when the field-value is \"*\",\n    /// or having a selected representation with an entity-tag that does not\n    /// match any of those listed in the field-value.\n    ///\n    /// A recipient MUST use the weak comparison function when comparing\n    /// entity-tags for If-None-Match (Section 2.3.2), since weak entity-tags\n    /// can be used for cache validation even if there have been changes to\n    /// the representation data.\n    ///\n    /// # Note\n    /// This is a request header used for cache validation (and conditional requests). Servers\n    /// should not send `If-None-Match` in responses; use [`ETag`](super::ETag) to describe the\n    /// current representation instead.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// If-None-Match = \"*\" / 1#entity-tag\n    /// ```\n    ///\n    /// # Example Values\n    /// * `\"xyzzy\"`\n    /// * `W/\"xyzzy\"`\n    /// * `\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"`\n    /// * `W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"`\n    /// * `*`\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{http::header::IfNoneMatch, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfNoneMatch::Any)\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    ///\n    /// ```\n    /// use actix_web::{http::header::{EntityTag, IfNoneMatch}, test};\n    ///\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfNoneMatch::Items(vec![\n    ///         EntityTag::new(false, \"xyzzy\".to_owned()),\n    ///         EntityTag::new(false, \"foobar\".to_owned()),\n    ///         EntityTag::new(false, \"bazquux\".to_owned()),\n    ///     ]))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (IfNoneMatch, IF_NONE_MATCH) => {Any / (EntityTag)+}\n\n    test_parse_and_format {\n        crate::http::header::common_header_test!(test1, [b\"\\\"xyzzy\\\"\"]);\n        crate::http::header::common_header_test!(test2, [b\"W/\\\"xyzzy\\\"\"]);\n        crate::http::header::common_header_test!(test3, [b\"\\\"xyzzy\\\", \\\"r2d2xxxx\\\", \\\"c3piozzzz\\\"\"]);\n        crate::http::header::common_header_test!(test4, [b\"W/\\\"xyzzy\\\", W/\\\"r2d2xxxx\\\", W/\\\"c3piozzzz\\\"\"]);\n        crate::http::header::common_header_test!(test5, [b\"*\"]);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::test::TestRequest;\n\n    use super::IfNoneMatch;\n    use crate::http::header::{EntityTag, Header, IF_NONE_MATCH};\n\n    #[test]\n    fn test_if_none_match() {\n        let req = TestRequest::default()\n            .insert_header((IF_NONE_MATCH, \"*\"))\n            .finish();\n\n        let mut if_none_match = IfNoneMatch::parse(&req);\n        assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Any));\n\n        let req = TestRequest::default()\n            .insert_header((IF_NONE_MATCH, &b\"\\\"foobar\\\", W/\\\"weak-etag\\\"\"[..]))\n            .finish();\n\n        if_none_match = Header::parse(&req);\n        let mut entities: Vec<EntityTag> = Vec::new();\n        let foobar_etag = EntityTag::new_strong(\"foobar\".to_owned());\n        let weak_etag = EntityTag::new_weak(\"weak-etag\".to_owned());\n        entities.push(foobar_etag);\n        entities.push(weak_etag);\n        assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Items(entities)));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/if_range.rs",
    "content": "use std::fmt::{self, Display, Write};\n\nuse super::{\n    from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, InvalidHeaderValue,\n    TryIntoHeaderValue, Writer,\n};\nuse crate::{error::ParseError, http::header, HttpMessage};\n\n/// `If-Range` header, defined\n/// in [RFC 7233 §3.2](https://datatracker.ietf.org/doc/html/rfc7233#section-3.2)\n///\n/// If a client has a partial copy of a representation and wishes to have\n/// an up-to-date copy of the entire representation, it could use the\n/// Range header field with a conditional GET (using either or both of\n/// If-Unmodified-Since and If-Match.)  However, if the precondition\n/// fails because the representation has been modified, the client would\n/// then have to make a second request to obtain the entire current\n/// representation.\n///\n/// The `If-Range` header field allows a client to \\\"short-circuit\\\" the\n/// second request.  Informally, its meaning is as follows: if the\n/// representation is unchanged, send me the part(s) that I am requesting\n/// in Range; otherwise, send me the entire representation.\n///\n/// # Note\n/// This is a request header. Servers should not send `If-Range` in responses.\n///\n/// # ABNF\n/// ```plain\n/// If-Range = entity-tag / HTTP-date\n/// ```\n///\n/// # Example Values\n///\n/// * `Sat, 29 Oct 1994 19:43:31 GMT`\n/// * `\\\"xyzzy\\\"`\n///\n/// # Examples\n/// ```\n/// use actix_web::{http::header::{EntityTag, IfRange}, test};\n///\n/// let req = test::TestRequest::default()\n///     .insert_header(IfRange::EntityTag(EntityTag::new(false, \"abc\".to_owned())))\n///     .to_http_request();\n/// # let _ = req;\n/// ```\n///\n/// ```\n/// use std::time::{Duration, SystemTime};\n/// use actix_web::{http::header::IfRange, test};\n///\n/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);\n/// let req = test::TestRequest::default()\n///     .insert_header(IfRange::Date(fetched.into()))\n///     .to_http_request();\n/// # let _ = req;\n/// ```\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum IfRange {\n    /// The entity-tag the client has of the resource.\n    EntityTag(EntityTag),\n\n    /// The date when the client retrieved the resource.\n    Date(HttpDate),\n}\n\nimpl Header for IfRange {\n    fn name() -> HeaderName {\n        header::IF_RANGE\n    }\n    #[inline]\n    fn parse<T>(msg: &T) -> Result<Self, ParseError>\n    where\n        T: HttpMessage,\n    {\n        let etag: Result<EntityTag, _> = from_one_raw_str(msg.headers().get(&header::IF_RANGE));\n        if let Ok(etag) = etag {\n            return Ok(IfRange::EntityTag(etag));\n        }\n        let date: Result<HttpDate, _> = from_one_raw_str(msg.headers().get(&header::IF_RANGE));\n        if let Ok(date) = date {\n            return Ok(IfRange::Date(date));\n        }\n        Err(ParseError::Header)\n    }\n}\n\nimpl Display for IfRange {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            IfRange::EntityTag(ref x) => Display::fmt(x, f),\n            IfRange::Date(ref x) => Display::fmt(x, f),\n        }\n    }\n}\n\nimpl TryIntoHeaderValue for IfRange {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        let mut writer = Writer::new();\n        let _ = write!(&mut writer, \"{}\", self);\n        HeaderValue::from_maybe_shared(writer.take())\n    }\n}\n\n#[cfg(test)]\nmod test_parse_and_format {\n    use std::str;\n\n    use super::IfRange as HeaderField;\n    use crate::http::header::*;\n\n    crate::http::header::common_header_test!(test1, [b\"Sat, 29 Oct 1994 19:43:31 GMT\"]);\n    crate::http::header::common_header_test!(test2, [b\"\\\"abc\\\"\"]);\n    crate::http::header::common_header_test!(test3, [b\"this-is-invalid\"], None::<IfRange>);\n}\n"
  },
  {
    "path": "actix-web/src/http/header/if_unmodified_since.rs",
    "content": "use super::{HttpDate, IF_UNMODIFIED_SINCE};\n\ncrate::http::header::common_header! {\n    /// `If-Unmodified-Since` header, defined\n    /// in [RFC 7232 §3.4](https://datatracker.ietf.org/doc/html/rfc7232#section-3.4)\n    ///\n    /// The `If-Unmodified-Since` header field makes the request method\n    /// conditional on the selected representation's last modification date\n    /// being earlier than or equal to the date provided in the field-value.\n    /// This field accomplishes the same purpose as If-Match for cases where\n    /// the user agent does not have an entity-tag for the representation.\n    ///\n    /// # Note\n    /// This is a request header used for conditional requests. Servers should not send\n    /// `If-Unmodified-Since` in responses; use [`LastModified`](super::LastModified) / the\n    /// `Last-Modified` header instead.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// If-Unmodified-Since = HTTP-date\n    /// ```\n    ///\n    /// # Example Values\n    /// * `Sat, 29 Oct 1994 19:43:31 GMT`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::time::{SystemTime, Duration};\n    /// use actix_web::{http::header::IfUnmodifiedSince, test};\n    ///\n    /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);\n    /// let req = test::TestRequest::default()\n    ///     .insert_header(IfUnmodifiedSince(modified.into()))\n    ///     .to_http_request();\n    /// # let _ = req;\n    /// ```\n    (IfUnmodifiedSince, IF_UNMODIFIED_SINCE) => [HttpDate]\n\n    test_parse_and_format {\n        // Test case from RFC\n        crate::http::header::common_header_test!(test1, [b\"Sat, 29 Oct 1994 19:43:31 GMT\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/last_modified.rs",
    "content": "use super::{HttpDate, LAST_MODIFIED};\n\ncrate::http::header::common_header! {\n    /// `Last-Modified` header, defined\n    /// in [RFC 7232 §2.2](https://datatracker.ietf.org/doc/html/rfc7232#section-2.2)\n    ///\n    /// The `Last-Modified` header field in a response provides a timestamp\n    /// indicating the date and time at which the origin server believes the\n    /// selected representation was last modified, as determined at the\n    /// conclusion of handling the request.\n    ///\n    /// # ABNF\n    /// ```plain\n    /// Expires = HTTP-date\n    /// ```\n    ///\n    /// # Example Values\n    /// * `Sat, 29 Oct 1994 19:43:31 GMT`\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::time::{SystemTime, Duration};\n    /// use actix_web::HttpResponse;\n    /// use actix_web::http::header::LastModified;\n    ///\n    /// let mut builder = HttpResponse::Ok();\n    /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);\n    /// builder.insert_header(\n    ///     LastModified(modified.into())\n    /// );\n    /// ```\n    (LastModified, LAST_MODIFIED) => [HttpDate]\n\n    test_parse_and_format {\n        // Test case from RFC\n        crate::http::header::common_header_test!(test1, [b\"Sat, 29 Oct 1994 19:43:31 GMT\"]);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/macros.rs",
    "content": "macro_rules! common_header_test_module {\n    ($id:ident, $tm:ident{$($tf:item)*}) => {\n        #[cfg(test)]\n        mod $tm {\n            #![allow(unused_imports)]\n\n            use ::core::str;\n\n            use ::actix_http::{Method, test};\n            use ::mime::*;\n\n            use $crate::http::header::{self, *};\n            use super::{$id as HeaderField, *};\n\n            $($tf)*\n        }\n    }\n}\n\n#[cfg(test)]\nmacro_rules! common_header_test {\n    ($id:ident, $raw:expr) => {\n        #[test]\n        fn $id() {\n            use ::actix_http::test;\n\n            let raw = $raw;\n            let headers = raw.iter().map(|x| x.to_vec()).collect::<Vec<_>>();\n\n            let mut req = test::TestRequest::default();\n\n            for item in headers {\n                req = req.append_header((HeaderField::name(), item)).take();\n            }\n\n            let req = req.finish();\n            let value = HeaderField::parse(&req);\n\n            let result = format!(\"{}\", value.unwrap());\n            let expected = ::std::string::String::from_utf8(raw[0].to_vec()).unwrap();\n\n            let result_cmp: Vec<String> = result\n                .to_ascii_lowercase()\n                .split(' ')\n                .map(|x| x.to_owned())\n                .collect();\n            let expected_cmp: Vec<String> = expected\n                .to_ascii_lowercase()\n                .split(' ')\n                .map(|x| x.to_owned())\n                .collect();\n\n            assert_eq!(result_cmp.concat(), expected_cmp.concat());\n        }\n    };\n\n    ($id:ident, $raw:expr, $exp:expr) => {\n        #[test]\n        fn $id() {\n            use actix_http::test;\n\n            let headers = $raw.iter().map(|x| x.to_vec()).collect::<Vec<_>>();\n            let mut req = test::TestRequest::default();\n\n            for item in headers {\n                req.append_header((HeaderField::name(), item));\n            }\n\n            let req = req.finish();\n            let val = HeaderField::parse(&req);\n\n            let exp: ::core::option::Option<HeaderField> = $exp;\n\n            // test parsing\n            assert_eq!(val.ok(), exp);\n\n            // test formatting\n            if let Some(exp) = exp {\n                let raw = &($raw)[..];\n                let mut iter = raw.iter().map(|b| str::from_utf8(&b[..]).unwrap());\n                let mut joined = String::new();\n                if let Some(s) = iter.next() {\n                    joined.push_str(s);\n                    for s in iter {\n                        joined.push_str(\", \");\n                        joined.push_str(s);\n                    }\n                }\n                assert_eq!(format!(\"{}\", exp), joined);\n            }\n        }\n    };\n}\n\nmacro_rules! common_header {\n    // TODO: these docs are wrong, there's no $n or $nn\n    // $attrs:meta: Attributes associated with the header item (usually docs)\n    // $id:ident: Identifier of the header\n    // $n:expr: Lowercase name of the header\n    // $nn:expr: Nice name of the header\n\n    // List header, zero or more items\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)*) => {\n        $(#[$attrs])*\n        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]\n        pub struct $id(pub Vec<$item>);\n\n        impl $crate::http::header::Header for $id {\n            #[inline]\n            fn name() -> $crate::http::header::HeaderName {\n                $name\n            }\n\n            #[inline]\n            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {\n                let headers = msg.headers().get_all(Self::name());\n                $crate::http::header::from_comma_delimited(headers).map($id)\n            }\n        }\n\n        impl ::core::fmt::Display for $id {\n            #[inline]\n            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {\n                $crate::http::header::fmt_comma_delimited(f, &self.0[..])\n            }\n        }\n\n        impl $crate::http::header::TryIntoHeaderValue for $id {\n            type Error = $crate::http::header::InvalidHeaderValue;\n\n            #[inline]\n            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {\n                use ::core::fmt::Write;\n                let mut writer = $crate::http::header::Writer::new();\n                let _ = write!(&mut writer, \"{}\", self);\n                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())\n            }\n        }\n    };\n\n    // List header, one or more items\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)+) => {\n        $(#[$attrs])*\n        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]\n        pub struct $id(pub Vec<$item>);\n\n        impl $crate::http::header::Header for $id {\n            #[inline]\n            fn name() -> $crate::http::header::HeaderName {\n                $name\n            }\n\n            #[inline]\n            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError>{\n                let headers = msg.headers().get_all(Self::name());\n\n                $crate::http::header::from_comma_delimited(headers)\n                    .and_then(|items| {\n                        if items.is_empty() {\n                            Err($crate::error::ParseError::Header)\n                        } else {\n                            Ok($id(items))\n                        }\n                    })\n            }\n        }\n\n        impl ::core::fmt::Display for $id {\n            #[inline]\n            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {\n                $crate::http::header::fmt_comma_delimited(f, &self.0[..])\n            }\n        }\n\n        impl $crate::http::header::TryIntoHeaderValue for $id {\n            type Error = $crate::http::header::InvalidHeaderValue;\n\n            #[inline]\n            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {\n                use ::core::fmt::Write;\n                let mut writer = $crate::http::header::Writer::new();\n                let _ = write!(&mut writer, \"{}\", self);\n                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())\n            }\n        }\n    };\n\n    // Single value header\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => [$value:ty]) => {\n        $(#[$attrs])*\n        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]\n        pub struct $id(pub $value);\n\n        impl $crate::http::header::Header for $id {\n            #[inline]\n            fn name() -> $crate::http::header::HeaderName {\n                $name\n            }\n\n            #[inline]\n            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {\n                let header = msg.headers().get(Self::name());\n                $crate::http::header::from_one_raw_str(header).map($id)\n            }\n        }\n\n        impl ::core::fmt::Display for $id {\n            #[inline]\n            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {\n                ::core::fmt::Display::fmt(&self.0, f)\n            }\n        }\n\n        impl $crate::http::header::TryIntoHeaderValue for $id {\n            type Error = $crate::http::header::InvalidHeaderValue;\n\n            #[inline]\n            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {\n                self.0.try_into_value()\n            }\n        }\n    };\n\n    // List header, one or more items with \"*\" option\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+}) => {\n        $(#[$attrs])*\n        #[derive(Clone, Debug, PartialEq, Eq)]\n        pub enum $id {\n            /// Any value is a match\n            Any,\n\n            /// Only the listed items are a match\n            Items(Vec<$item>),\n        }\n\n        impl $crate::http::header::Header for $id {\n            #[inline]\n            fn name() -> $crate::http::header::HeaderName {\n                $name\n            }\n\n            #[inline]\n            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {\n                let is_any = msg\n                    .headers()\n                    .get(Self::name())\n                    .and_then(|hdr| hdr.to_str().ok())\n                    .map(|hdr| hdr.trim() == \"*\");\n\n                if let Some(true) = is_any {\n                    Ok($id::Any)\n                } else {\n                    let headers = msg.headers().get_all(Self::name());\n                    Ok($id::Items($crate::http::header::from_comma_delimited(headers)?))\n                }\n            }\n        }\n\n        impl ::core::fmt::Display for $id {\n            #[inline]\n            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {\n                match *self {\n                    $id::Any => f.write_str(\"*\"),\n                    $id::Items(ref fields) =>\n                        $crate::http::header::fmt_comma_delimited(f, &fields[..])\n                }\n            }\n        }\n\n        impl $crate::http::header::TryIntoHeaderValue for $id {\n            type Error = $crate::http::header::InvalidHeaderValue;\n\n            #[inline]\n            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {\n                use ::core::fmt::Write;\n                let mut writer = $crate::http::header::Writer::new();\n                let _ = write!(&mut writer, \"{}\", self);\n                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())\n            }\n        }\n    };\n\n    // optional test module\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)* $tm:ident{$($tf:item)*}) => {\n        crate::http::header::common_header! {\n            $(#[$attrs])*\n            ($id, $name) => ($item)*\n        }\n\n        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}\n    };\n    ($(#[$attrs:meta])*($id:ident, $n:expr) => ($item:ty)+ $tm:ident{$($tf:item)*}) => {\n        crate::http::header::common_header! {\n            $(#[$attrs])*\n            ($id, $n) => ($item)+\n        }\n\n        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}\n    };\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => [$item:ty] $tm:ident{$($tf:item)*}) => {\n        crate::http::header::common_header! {\n            $(#[$attrs])* ($id, $name) => [$item]\n        }\n\n        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}\n    };\n    ($(#[$attrs:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+} $tm:ident{$($tf:item)*}) => {\n        crate::http::header::common_header! {\n            $(#[$attrs])*\n            ($id, $name) => {Any / ($item)+}\n        }\n\n        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}\n    };\n}\n\npub(crate) use common_header;\n#[cfg(test)]\npub(crate) use common_header_test;\npub(crate) use common_header_test_module;\n"
  },
  {
    "path": "actix-web/src/http/header/mod.rs",
    "content": "//! A Collection of Header implementations for common HTTP Headers.\n//!\n//! ## Mime Types\n//! Several header fields use MIME values for their contents. Keeping with the strongly-typed theme,\n//! the [mime] crate is used in such headers as [`ContentType`] and [`Accept`].\n\nuse std::fmt;\n\n// re-export from actix-http\n// - header name / value types\n// - relevant traits for converting to header name / value\n// - all const header names\n// - header map\n// - the few typed headers from actix-http\n// - header parsing utils\npub use actix_http::header::*;\nuse bytes::{Bytes, BytesMut};\n\nmod accept;\nmod accept_charset;\nmod accept_encoding;\nmod accept_language;\nmod allow;\nmod cache_control;\nmod content_disposition;\nmod content_language;\nmod content_length;\nmod content_range;\nmod content_type;\nmod date;\nmod encoding;\nmod entity;\nmod etag;\nmod expires;\nmod if_match;\nmod if_modified_since;\nmod if_none_match;\nmod if_range;\nmod if_unmodified_since;\nmod last_modified;\nmod macros;\nmod preference;\nmod range;\n\n#[cfg(test)]\npub(crate) use self::macros::common_header_test;\npub(crate) use self::macros::{common_header, common_header_test_module};\npub use self::{\n    accept::Accept,\n    accept_charset::AcceptCharset,\n    accept_encoding::AcceptEncoding,\n    accept_language::AcceptLanguage,\n    allow::Allow,\n    cache_control::{CacheControl, CacheDirective},\n    content_disposition::{ContentDisposition, DispositionParam, DispositionType},\n    content_language::ContentLanguage,\n    content_length::ContentLength,\n    content_range::{ContentRange, ContentRangeSpec},\n    content_type::ContentType,\n    date::Date,\n    encoding::Encoding,\n    entity::EntityTag,\n    etag::ETag,\n    expires::Expires,\n    if_match::IfMatch,\n    if_modified_since::IfModifiedSince,\n    if_none_match::IfNoneMatch,\n    if_range::IfRange,\n    if_unmodified_since::IfUnmodifiedSince,\n    last_modified::LastModified,\n    preference::Preference,\n    range::{ByteRangeSpec, Range},\n};\n\n/// Format writer ([`fmt::Write`]) for a [`BytesMut`].\n#[derive(Debug, Default)]\nstruct Writer {\n    buf: BytesMut,\n}\n\nimpl Writer {\n    /// Constructs new bytes writer.\n    pub fn new() -> Writer {\n        Writer::default()\n    }\n\n    /// Splits bytes out of writer, leaving writer buffer empty.\n    pub fn take(&mut self) -> Bytes {\n        self.buf.split().freeze()\n    }\n}\n\nimpl fmt::Write for Writer {\n    #[inline]\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        self.buf.extend_from_slice(s.as_bytes());\n        Ok(())\n    }\n\n    #[inline]\n    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {\n        fmt::write(self, args)\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/preference.rs",
    "content": "use std::{\n    fmt::{self, Write as _},\n    str,\n};\n\n/// A wrapper for types used in header values where wildcard (`*`) items are allowed but the\n/// underlying type does not support them.\n///\n/// For example, we use the `language-tags` crate for the [`AcceptLanguage`](super::AcceptLanguage)\n/// typed header but it does not parse `*` successfully. On the other hand, the `mime` crate, used\n/// for [`Accept`](super::Accept), has first-party support for wildcard items so this wrapper is not\n/// used in those header types.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)]\npub enum Preference<T> {\n    /// A wildcard value.\n    Any,\n\n    /// A valid `T`.\n    Specific(T),\n}\n\nimpl<T> Preference<T> {\n    /// Returns true if preference is the any/wildcard (`*`) value.\n    pub fn is_any(&self) -> bool {\n        matches!(self, Self::Any)\n    }\n\n    /// Returns true if preference is the specific item (`T`) variant.\n    pub fn is_specific(&self) -> bool {\n        matches!(self, Self::Specific(_))\n    }\n\n    /// Returns reference to value in `Specific` variant, if it is set.\n    pub fn item(&self) -> Option<&T> {\n        match self {\n            Preference::Specific(ref item) => Some(item),\n            Preference::Any => None,\n        }\n    }\n\n    /// Consumes the container, returning the value in the `Specific` variant, if it is set.\n    pub fn into_item(self) -> Option<T> {\n        match self {\n            Preference::Specific(item) => Some(item),\n            Preference::Any => None,\n        }\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for Preference<T> {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Preference::Any => f.write_char('*'),\n            Preference::Specific(item) => fmt::Display::fmt(item, f),\n        }\n    }\n}\n\nimpl<T: str::FromStr> str::FromStr for Preference<T> {\n    type Err = T::Err;\n\n    #[inline]\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s.trim() {\n            \"*\" => Ok(Self::Any),\n            other => other.parse().map(Preference::Specific),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/header/range.rs",
    "content": "use std::{\n    cmp,\n    fmt::{self, Display, Write},\n    str::FromStr,\n};\n\nuse actix_http::{error::ParseError, header, HttpMessage};\n\nuse super::{Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer};\n\n/// `Range` header, defined\n/// in [RFC 7233 §3.1](https://datatracker.ietf.org/doc/html/rfc7233#section-3.1)\n///\n/// The \"Range\" header field on a GET request modifies the method semantics to request transfer of\n/// only one or more sub-ranges of the selected representation data, rather than the entire selected\n/// representation data.\n///\n/// # Note\n/// This is a request header. Servers should not send `Range` in responses; use\n/// [`ContentRange`](super::ContentRange) / the `Content-Range` header for partial responses.\n///\n/// # ABNF\n/// ```plain\n/// Range = byte-ranges-specifier / other-ranges-specifier\n/// other-ranges-specifier = other-range-unit \"=\" other-range-set\n/// other-range-set = 1*VCHAR\n///\n/// bytes-unit = \"bytes\"\n///\n/// byte-ranges-specifier = bytes-unit \"=\" byte-range-set\n/// byte-range-set = 1#(byte-range-spec / suffix-byte-range-spec)\n/// byte-range-spec = first-byte-pos \"-\" [last-byte-pos]\n/// suffix-byte-range-spec = \"-\" suffix-length\n/// suffix-length = 1*DIGIT\n/// first-byte-pos = 1*DIGIT\n/// last-byte-pos = 1*DIGIT\n/// ```\n///\n/// # Example Values\n/// * `bytes=1000-`\n/// * `bytes=-50`\n/// * `bytes=0-1,30-40`\n/// * `bytes=0-10,20-90,-100`\n/// * `custom_unit=0-123`\n/// * `custom_unit=xxx-yyy`\n///\n/// # Examples\n/// ```\n/// use actix_web::{http::header::{ByteRangeSpec, Range}, test};\n///\n/// let req = test::TestRequest::default()\n///     .insert_header(Range::Bytes(vec![\n///         ByteRangeSpec::FromTo(1, 100),\n///         ByteRangeSpec::From(200),\n///     ]))\n///     .insert_header(Range::Unregistered(\"letters\".to_owned(), \"a-f\".to_owned()))\n///     .insert_header(Range::bytes(1, 100))\n///     .insert_header(Range::bytes_multi(vec![(1, 100), (200, 300)]))\n///     .to_http_request();\n/// # let _ = req;\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Range {\n    /// Byte range.\n    Bytes(Vec<ByteRangeSpec>),\n\n    /// Custom range, with unit not registered at IANA.\n    ///\n    /// (`other-range-unit`: String , `other-range-set`: String)\n    Unregistered(String, String),\n}\n\n/// A range of bytes to fetch.\n///\n/// Each [`Range::Bytes`] header can contain one or more `ByteRangeSpec`s.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ByteRangeSpec {\n    /// All bytes from `x` to `y`, inclusive.\n    ///\n    /// Serialized as `x-y`.\n    ///\n    /// Example: `bytes=500-999` would represent the second 500 bytes.\n    FromTo(u64, u64),\n\n    /// All bytes starting from `x`, inclusive.\n    ///\n    /// Serialized as `x-`.\n    ///\n    /// Example: For a file of 1000 bytes, `bytes=950-` would represent bytes 950-999, inclusive.\n    From(u64),\n\n    /// The last `y` bytes, inclusive.\n    ///\n    /// Using the spec terminology, this is `suffix-byte-range-spec`. Serialized as `-y`.\n    ///\n    /// Example: For a file of 1000 bytes, `bytes=-50` is equivalent to `bytes=950-`.\n    Last(u64),\n}\n\nimpl ByteRangeSpec {\n    /// Given the full length of the entity, attempt to normalize the byte range into an satisfiable\n    /// end-inclusive `(from, to)` range.\n    ///\n    /// The resulting range is guaranteed to be a satisfiable range within the bounds\n    /// of `0 <= from <= to < full_length`.\n    ///\n    /// If the byte range is deemed unsatisfiable, `None` is returned. An unsatisfiable range is\n    /// generally cause for a server to either reject the client request with a\n    /// `416 Range Not Satisfiable` status code, or to simply ignore the range header and serve the\n    /// full entity using a `200 OK` status code.\n    ///\n    /// This function closely follows [RFC 7233 §2.1]. As such, it considers ranges to be\n    /// satisfiable if they meet the following conditions:\n    ///\n    /// > If a valid byte-range-set includes at least one byte-range-spec with a first-byte-pos that\n    /// > is less than the current length of the representation, or at least one\n    /// > suffix-byte-range-spec with a non-zero suffix-length, then the byte-range-set is\n    /// > satisfiable. Otherwise, the byte-range-set is unsatisfiable.\n    ///\n    /// The function also computes remainder ranges based on the RFC:\n    ///\n    /// > If the last-byte-pos value is absent, or if the value is greater than or equal to the\n    /// > current length of the representation data, the byte range is interpreted as the remainder\n    /// > of the representation (i.e., the server replaces the value of last-byte-pos with a value\n    /// > that is one less than the current length of the selected representation).\n    ///\n    /// [RFC 7233 §2.1]: https://datatracker.ietf.org/doc/html/rfc7233\n    pub fn to_satisfiable_range(&self, full_length: u64) -> Option<(u64, u64)> {\n        // If the full length is zero, there is no satisfiable end-inclusive range.\n        if full_length == 0 {\n            return None;\n        }\n\n        match *self {\n            ByteRangeSpec::FromTo(from, to) => {\n                if from < full_length && from <= to {\n                    Some((from, cmp::min(to, full_length - 1)))\n                } else {\n                    None\n                }\n            }\n\n            ByteRangeSpec::From(from) => {\n                if from < full_length {\n                    Some((from, full_length - 1))\n                } else {\n                    None\n                }\n            }\n\n            ByteRangeSpec::Last(last) => {\n                if last > 0 {\n                    // From the RFC: If the selected representation is shorter than the specified\n                    // suffix-length, the entire representation is used.\n                    if last > full_length {\n                        Some((0, full_length - 1))\n                    } else {\n                        Some((full_length - last, full_length - 1))\n                    }\n                } else {\n                    None\n                }\n            }\n        }\n    }\n}\n\nimpl Range {\n    /// Constructs a common byte range header.\n    ///\n    /// Eg: `bytes=from-to`\n    pub fn bytes(from: u64, to: u64) -> Range {\n        Range::Bytes(vec![ByteRangeSpec::FromTo(from, to)])\n    }\n\n    /// Constructs a byte range header with multiple subranges.\n    ///\n    /// Eg: `bytes=from1-to1,from2-to2,fromX-toX`\n    pub fn bytes_multi(ranges: Vec<(u64, u64)>) -> Range {\n        Range::Bytes(\n            ranges\n                .into_iter()\n                .map(|(from, to)| ByteRangeSpec::FromTo(from, to))\n                .collect(),\n        )\n    }\n}\n\nimpl fmt::Display for ByteRangeSpec {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            ByteRangeSpec::FromTo(from, to) => write!(f, \"{}-{}\", from, to),\n            ByteRangeSpec::Last(pos) => write!(f, \"-{}\", pos),\n            ByteRangeSpec::From(pos) => write!(f, \"{}-\", pos),\n        }\n    }\n}\n\nimpl fmt::Display for Range {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Range::Bytes(ranges) => {\n                write!(f, \"bytes=\")?;\n\n                for (i, range) in ranges.iter().enumerate() {\n                    if i != 0 {\n                        f.write_str(\",\")?;\n                    }\n\n                    Display::fmt(range, f)?;\n                }\n                Ok(())\n            }\n\n            Range::Unregistered(unit, range_str) => {\n                write!(f, \"{}={}\", unit, range_str)\n            }\n        }\n    }\n}\n\nimpl FromStr for Range {\n    type Err = ParseError;\n\n    fn from_str(s: &str) -> Result<Range, ParseError> {\n        let (unit, val) = s.split_once('=').ok_or(ParseError::Header)?;\n\n        match (unit, val) {\n            (\"bytes\", ranges) => {\n                let ranges = from_comma_delimited(ranges);\n\n                if ranges.is_empty() {\n                    return Err(ParseError::Header);\n                }\n\n                Ok(Range::Bytes(ranges))\n            }\n\n            (_, \"\") => Err(ParseError::Header),\n            (\"\", _) => Err(ParseError::Header),\n\n            (unit, range_str) => Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())),\n        }\n    }\n}\n\nimpl FromStr for ByteRangeSpec {\n    type Err = ParseError;\n\n    fn from_str(s: &str) -> Result<ByteRangeSpec, ParseError> {\n        let (start, end) = s.split_once('-').ok_or(ParseError::Header)?;\n\n        match (start, end) {\n            (\"\", end) => end\n                .parse()\n                .or(Err(ParseError::Header))\n                .map(ByteRangeSpec::Last),\n\n            (start, \"\") => start\n                .parse()\n                .or(Err(ParseError::Header))\n                .map(ByteRangeSpec::From),\n\n            (start, end) => match (start.parse(), end.parse()) {\n                (Ok(start), Ok(end)) if start <= end => Ok(ByteRangeSpec::FromTo(start, end)),\n                _ => Err(ParseError::Header),\n            },\n        }\n    }\n}\n\nimpl Header for Range {\n    fn name() -> HeaderName {\n        header::RANGE\n    }\n\n    #[inline]\n    fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {\n        header::from_one_raw_str(msg.headers().get(Self::name()))\n    }\n}\n\nimpl TryIntoHeaderValue for Range {\n    type Error = InvalidHeaderValue;\n\n    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {\n        let mut wrt = Writer::new();\n        let _ = write!(wrt, \"{}\", self);\n        HeaderValue::from_maybe_shared(wrt.take())\n    }\n}\n\n/// Parses 0 or more items out of a comma delimited string, ignoring invalid items.\nfn from_comma_delimited<T: FromStr>(s: &str) -> Vec<T> {\n    s.split(',')\n        .filter_map(|x| match x.trim() {\n            \"\" => None,\n            y => Some(y),\n        })\n        .filter_map(|x| x.parse().ok())\n        .collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::{test::TestRequest, Request};\n\n    use super::*;\n\n    fn req(s: &str) -> Request {\n        TestRequest::default()\n            .insert_header((header::RANGE, s))\n            .finish()\n    }\n\n    #[test]\n    fn test_parse_bytes_range_valid() {\n        let r: Range = Header::parse(&req(\"bytes=1-100\")).unwrap();\n        let r2: Range = Header::parse(&req(\"bytes=1-100,-\")).unwrap();\n        let r3 = Range::bytes(1, 100);\n        assert_eq!(r, r2);\n        assert_eq!(r2, r3);\n\n        let r: Range = Header::parse(&req(\"bytes=1-100,200-\")).unwrap();\n        let r2: Range = Header::parse(&req(\"bytes= 1-100 , 101-xxx,  200- \")).unwrap();\n        let r3 = Range::Bytes(vec![\n            ByteRangeSpec::FromTo(1, 100),\n            ByteRangeSpec::From(200),\n        ]);\n        assert_eq!(r, r2);\n        assert_eq!(r2, r3);\n\n        let r: Range = Header::parse(&req(\"bytes=1-100,-100\")).unwrap();\n        let r2: Range = Header::parse(&req(\"bytes=1-100, ,,-100\")).unwrap();\n        let r3 = Range::Bytes(vec![\n            ByteRangeSpec::FromTo(1, 100),\n            ByteRangeSpec::Last(100),\n        ]);\n        assert_eq!(r, r2);\n        assert_eq!(r2, r3);\n\n        let r: Range = Header::parse(&req(\"custom=1-100,-100\")).unwrap();\n        let r2 = Range::Unregistered(\"custom\".to_owned(), \"1-100,-100\".to_owned());\n        assert_eq!(r, r2);\n    }\n\n    #[test]\n    fn test_parse_unregistered_range_valid() {\n        let r: Range = Header::parse(&req(\"custom=1-100,-100\")).unwrap();\n        let r2 = Range::Unregistered(\"custom\".to_owned(), \"1-100,-100\".to_owned());\n        assert_eq!(r, r2);\n\n        let r: Range = Header::parse(&req(\"custom=abcd\")).unwrap();\n        let r2 = Range::Unregistered(\"custom\".to_owned(), \"abcd\".to_owned());\n        assert_eq!(r, r2);\n\n        let r: Range = Header::parse(&req(\"custom=xxx-yyy\")).unwrap();\n        let r2 = Range::Unregistered(\"custom\".to_owned(), \"xxx-yyy\".to_owned());\n        assert_eq!(r, r2);\n    }\n\n    #[test]\n    fn test_parse_invalid() {\n        let r: Result<Range, ParseError> = Header::parse(&req(\"bytes=1-a,-\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"bytes=1-2-3\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"abc\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"bytes=1-100=\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"bytes=\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"custom=\"));\n        assert_eq!(r.ok(), None);\n\n        let r: Result<Range, ParseError> = Header::parse(&req(\"=1-100\"));\n        assert_eq!(r.ok(), None);\n    }\n\n    #[test]\n    fn test_fmt() {\n        let range = Range::Bytes(vec![\n            ByteRangeSpec::FromTo(0, 1000),\n            ByteRangeSpec::From(2000),\n        ]);\n        assert_eq!(&range.to_string(), \"bytes=0-1000,2000-\");\n\n        let range = Range::Bytes(vec![]);\n\n        assert_eq!(&range.to_string(), \"bytes=\");\n\n        let range = Range::Unregistered(\"custom\".to_owned(), \"1-xxx\".to_owned());\n\n        assert_eq!(&range.to_string(), \"custom=1-xxx\");\n    }\n\n    #[test]\n    fn test_byte_range_spec_to_satisfiable_range() {\n        assert_eq!(\n            Some((0, 0)),\n            ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3)\n        );\n        assert_eq!(\n            Some((1, 2)),\n            ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3)\n        );\n        assert_eq!(\n            Some((1, 2)),\n            ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3)\n        );\n        assert_eq!(None, ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0));\n\n        assert_eq!(Some((0, 2)), ByteRangeSpec::From(0).to_satisfiable_range(3));\n        assert_eq!(Some((2, 2)), ByteRangeSpec::From(2).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::From(3).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::From(5).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::From(0).to_satisfiable_range(0));\n\n        assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3));\n        assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3));\n        assert_eq!(Some((0, 2)), ByteRangeSpec::Last(5).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3));\n        assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/http/mod.rs",
    "content": "//! Various HTTP related types.\n\npub mod header;\n\npub use actix_http::{uri, ConnectionType, Error, KeepAlive, Method, StatusCode, Uri, Version};\n"
  },
  {
    "path": "actix-web/src/info.rs",
    "content": "use std::{convert::Infallible, net::SocketAddr};\n\nuse actix_utils::future::{err, ok, Ready};\nuse derive_more::{Display, Error};\n\nuse crate::{\n    dev::{AppConfig, Payload, RequestHead},\n    http::{\n        header::{self, HeaderName},\n        uri::{Authority, Scheme},\n    },\n    FromRequest, HttpRequest, ResponseError,\n};\n\nstatic X_FORWARDED_FOR: HeaderName = HeaderName::from_static(\"x-forwarded-for\");\nstatic X_FORWARDED_HOST: HeaderName = HeaderName::from_static(\"x-forwarded-host\");\nstatic X_FORWARDED_PROTO: HeaderName = HeaderName::from_static(\"x-forwarded-proto\");\n\n/// Trim whitespace then any quote marks.\nfn unquote(val: &str) -> &str {\n    val.trim().trim_start_matches('\"').trim_end_matches('\"')\n}\n\n/// Remove port and IPv6 square brackets from a peer specification.\nfn bare_address(val: &str) -> &str {\n    if val.starts_with('[') {\n        val.split(\"]:\")\n            .next()\n            .map(|s| s.trim_start_matches('[').trim_end_matches(']'))\n            // this indicates that the IPv6 address is malformed so shouldn't\n            // usually happen, but if it does, just return the original input\n            .unwrap_or(val)\n    } else {\n        val.split(':').next().unwrap_or(val)\n    }\n}\n\n/// Extracts and trims first value for given header name.\nfn first_header_value<'a>(req: &'a RequestHead, name: &'_ HeaderName) -> Option<&'a str> {\n    let hdr = req.headers.get(name)?.to_str().ok()?;\n    let val = hdr.split(',').next()?.trim();\n    Some(val)\n}\n\n/// HTTP connection information.\n///\n/// `ConnectionInfo` implements `FromRequest` and can be extracted in handlers.\n///\n/// # Examples\n/// ```\n/// # use actix_web::{HttpResponse, Responder};\n/// use actix_web::dev::ConnectionInfo;\n///\n/// async fn handler(conn: ConnectionInfo) -> impl Responder {\n///     match conn.host() {\n///         \"actix.rs\" => HttpResponse::Ok().body(\"Welcome!\"),\n///         \"admin.actix.rs\" => HttpResponse::Ok().body(\"Admin portal.\"),\n///         _ => HttpResponse::NotFound().finish()\n///     }\n/// }\n/// # let _svc = actix_web::web::to(handler);\n/// ```\n///\n/// # Implementation Notes\n/// Parses `Forwarded` header information according to [RFC 7239][rfc7239] but does not try to\n/// interpret the values for each property. As such, the getter methods on `ConnectionInfo` return\n/// strings instead of IP addresses or other types to acknowledge that they may be\n/// [obfuscated][rfc7239-63] or [unknown][rfc7239-62].\n///\n/// If the older, related headers are also present (eg. `X-Forwarded-For`), then `Forwarded`\n/// is preferred.\n///\n/// [rfc7239]: https://datatracker.ietf.org/doc/html/rfc7239\n/// [rfc7239-62]: https://datatracker.ietf.org/doc/html/rfc7239#section-6.2\n/// [rfc7239-63]: https://datatracker.ietf.org/doc/html/rfc7239#section-6.3\n#[derive(Debug, Clone, Default)]\npub struct ConnectionInfo {\n    host: String,\n    scheme: String,\n    peer_addr: Option<String>,\n    realip_remote_addr: Option<String>,\n}\n\nimpl ConnectionInfo {\n    pub(crate) fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {\n        let mut host = None;\n        let mut scheme = None;\n        let mut realip_remote_addr = None;\n\n        for (name, val) in req\n            .headers\n            .get_all(&header::FORWARDED)\n            .filter_map(|hdr| hdr.to_str().ok())\n            // \"for=1.2.3.4, for=5.6.7.8; scheme=https\"\n            .flat_map(|val| val.split(';'))\n            // [\"for=1.2.3.4, for=5.6.7.8\", \" scheme=https\"]\n            .flat_map(|vals| vals.split(','))\n            // [\"for=1.2.3.4\", \" for=5.6.7.8\", \" scheme=https\"]\n            .flat_map(|pair| {\n                let mut items = pair.trim().splitn(2, '=');\n                Some((items.next()?, items.next()?))\n            })\n        {\n            // [(name , val      ), ...                                    ]\n            // [(\"for\", \"1.2.3.4\"), (\"for\", \"5.6.7.8\"), (\"scheme\", \"https\")]\n\n            // taking the first value for each property is correct because spec states that first\n            // \"for\" value is client and rest are proxies; multiple values other properties have\n            // no defined semantics\n            //\n            // > In a chain of proxy servers where this is fully utilized, the first\n            // > \"for\" parameter will disclose the client where the request was first\n            // > made, followed by any subsequent proxy identifiers.\n            // --- https://datatracker.ietf.org/doc/html/rfc7239#section-5.2\n\n            match name.trim().to_lowercase().as_str() {\n                \"for\" => realip_remote_addr.get_or_insert_with(|| bare_address(unquote(val))),\n                \"proto\" => scheme.get_or_insert_with(|| unquote(val)),\n                \"host\" => host.get_or_insert_with(|| unquote(val)),\n                \"by\" => {\n                    // TODO: implement https://datatracker.ietf.org/doc/html/rfc7239#section-5.1\n                    continue;\n                }\n                _ => continue,\n            };\n        }\n\n        let scheme = scheme\n            .or_else(|| first_header_value(req, &X_FORWARDED_PROTO))\n            .or_else(|| req.uri.scheme().map(Scheme::as_str))\n            .or_else(|| Some(\"https\").filter(|_| cfg.secure()))\n            .unwrap_or(\"http\")\n            .to_owned();\n\n        let host = host\n            .or_else(|| first_header_value(req, &X_FORWARDED_HOST))\n            .or_else(|| req.headers.get(&header::HOST)?.to_str().ok())\n            .or_else(|| req.uri.authority().map(Authority::as_str))\n            .unwrap_or_else(|| cfg.host())\n            .to_owned();\n\n        let realip_remote_addr = realip_remote_addr\n            .or_else(|| first_header_value(req, &X_FORWARDED_FOR))\n            .map(str::to_owned);\n\n        let peer_addr = req.peer_addr.map(|addr| addr.ip().to_string());\n\n        ConnectionInfo {\n            host,\n            scheme,\n            peer_addr,\n            realip_remote_addr,\n        }\n    }\n\n    /// Real IP (remote address) of client that initiated request.\n    ///\n    /// The address is resolved through the following, in order:\n    /// - `Forwarded` header\n    /// - `X-Forwarded-For` header\n    /// - peer address of opened socket (same as [`peer_addr`](Self::peer_addr))\n    ///\n    /// # Security\n    /// Do not use this function for security purposes unless you can be sure that the `Forwarded`\n    /// and `X-Forwarded-For` headers cannot be spoofed by the client. If you are running without a\n    /// proxy then [obtaining the peer address](Self::peer_addr) would be more appropriate.\n    #[inline]\n    pub fn realip_remote_addr(&self) -> Option<&str> {\n        self.realip_remote_addr\n            .as_deref()\n            .or(self.peer_addr.as_deref())\n    }\n\n    /// Returns serialized IP address of the peer connection.\n    ///\n    /// See [`HttpRequest::peer_addr`] for more details.\n    #[inline]\n    pub fn peer_addr(&self) -> Option<&str> {\n        self.peer_addr.as_deref()\n    }\n\n    /// Hostname of the request.\n    ///\n    /// Hostname is resolved through the following, in order:\n    /// - `Forwarded` header\n    /// - `X-Forwarded-Host` header\n    /// - `Host` header\n    /// - request target / URI\n    /// - configured server hostname\n    #[inline]\n    pub fn host(&self) -> &str {\n        &self.host\n    }\n\n    /// Scheme of the request.\n    ///\n    /// Scheme is resolved through the following, in order:\n    /// - `Forwarded` header\n    /// - `X-Forwarded-Proto` header\n    /// - request target / URI\n    #[inline]\n    pub fn scheme(&self) -> &str {\n        &self.scheme\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Renamed to `peer_addr`.\")]\n    pub fn remote_addr(&self) -> Option<&str> {\n        self.peer_addr()\n    }\n}\n\nimpl FromRequest for ConnectionInfo {\n    type Error = Infallible;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        ok(req.connection_info().clone())\n    }\n}\n\n/// Extractor for peer's socket address.\n///\n/// Also see [`HttpRequest::peer_addr`] and [`ConnectionInfo::peer_addr`].\n///\n/// # Examples\n/// ```\n/// # use actix_web::Responder;\n/// use actix_web::dev::PeerAddr;\n///\n/// async fn handler(peer_addr: PeerAddr) -> impl Responder {\n///     let socket_addr = peer_addr.0;\n///     socket_addr.to_string()\n/// }\n/// # let _svc = actix_web::web::to(handler);\n/// ```\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display)]\n#[display(\"{}\", _0)]\npub struct PeerAddr(pub SocketAddr);\n\nimpl PeerAddr {\n    /// Unwrap into inner `SocketAddr` value.\n    pub fn into_inner(self) -> SocketAddr {\n        self.0\n    }\n}\n\n#[derive(Debug, Display, Error)]\n#[non_exhaustive]\n#[display(\"Missing peer address\")]\npub struct MissingPeerAddr;\n\nimpl ResponseError for MissingPeerAddr {}\n\nimpl FromRequest for PeerAddr {\n    type Error = MissingPeerAddr;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        match req.peer_addr() {\n            Some(addr) => ok(PeerAddr(addr)),\n            None => {\n                log::error!(\"Missing peer address.\");\n                err(MissingPeerAddr)\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test::TestRequest;\n\n    const X_FORWARDED_FOR: &str = \"x-forwarded-for\";\n    const X_FORWARDED_HOST: &str = \"x-forwarded-host\";\n    const X_FORWARDED_PROTO: &str = \"x-forwarded-proto\";\n\n    #[test]\n    fn info_default() {\n        let req = TestRequest::default().to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"http\");\n        assert_eq!(info.host(), \"localhost:8080\");\n    }\n\n    #[test]\n    fn host_header() {\n        let req = TestRequest::default()\n            .insert_header((header::HOST, \"rust-lang.org\"))\n            .to_http_request();\n\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"http\");\n        assert_eq!(info.host(), \"rust-lang.org\");\n        assert_eq!(info.realip_remote_addr(), None);\n    }\n\n    #[test]\n    fn x_forwarded_for_header() {\n        let req = TestRequest::default()\n            .insert_header((X_FORWARDED_FOR, \"192.0.2.60\"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n    }\n\n    #[test]\n    fn x_forwarded_host_header() {\n        let req = TestRequest::default()\n            .insert_header((X_FORWARDED_HOST, \"192.0.2.60\"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.host(), \"192.0.2.60\");\n        assert_eq!(info.realip_remote_addr(), None);\n    }\n\n    #[test]\n    fn x_forwarded_proto_header() {\n        let req = TestRequest::default()\n            .insert_header((X_FORWARDED_PROTO, \"https\"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"https\");\n    }\n\n    #[test]\n    fn forwarded_header() {\n        let req = TestRequest::default()\n            .insert_header((\n                header::FORWARDED,\n                \"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org\",\n            ))\n            .to_http_request();\n\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"https\");\n        assert_eq!(info.host(), \"rust-lang.org\");\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::FORWARDED,\n                \"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org\",\n            ))\n            .to_http_request();\n\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"https\");\n        assert_eq!(info.host(), \"rust-lang.org\");\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n    }\n\n    #[test]\n    fn forwarded_case_sensitivity() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, \"For=192.0.2.60\"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n    }\n\n    #[test]\n    fn forwarded_weird_whitespace() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, \"for= 1.2.3.4; proto= https\"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"1.2.3.4\"));\n        assert_eq!(info.scheme(), \"https\");\n\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, \"  for = 1.2.3.4  \"))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"1.2.3.4\"));\n    }\n\n    #[test]\n    fn forwarded_for_quoted() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, r#\"for=\"192.0.2.60:8080\"\"#))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n    }\n\n    #[test]\n    fn forwarded_for_ipv6() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, r#\"for=\"[2001:db8:cafe::17]\"\"#))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"2001:db8:cafe::17\"));\n    }\n\n    #[test]\n    fn forwarded_for_ipv6_with_port() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, r#\"for=\"[2001:db8:cafe::17]:4711\"\"#))\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.realip_remote_addr(), Some(\"2001:db8:cafe::17\"));\n    }\n\n    #[test]\n    fn forwarded_for_multiple() {\n        let req = TestRequest::default()\n            .insert_header((header::FORWARDED, \"for=192.0.2.60, for=198.51.100.17\"))\n            .to_http_request();\n        let info = req.connection_info();\n        // takes the first value\n        assert_eq!(info.realip_remote_addr(), Some(\"192.0.2.60\"));\n    }\n\n    #[test]\n    fn scheme_from_uri() {\n        let req = TestRequest::get()\n            .uri(\"https://actix.rs/test\")\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.scheme(), \"https\");\n    }\n\n    #[test]\n    fn host_from_uri() {\n        let req = TestRequest::get()\n            .uri(\"https://actix.rs/test\")\n            .to_http_request();\n        let info = req.connection_info();\n        assert_eq!(info.host(), \"actix.rs\");\n    }\n\n    #[test]\n    fn host_from_server_hostname() {\n        let mut req = TestRequest::get();\n        req.set_server_hostname(\"actix.rs\");\n        let req = req.to_http_request();\n\n        let info = req.connection_info();\n        assert_eq!(info.host(), \"actix.rs\");\n    }\n\n    #[actix_rt::test]\n    async fn conn_info_extract() {\n        let req = TestRequest::default()\n            .uri(\"https://actix.rs/test\")\n            .to_http_request();\n        let conn_info = ConnectionInfo::extract(&req).await.unwrap();\n        assert_eq!(conn_info.scheme(), \"https\");\n        assert_eq!(conn_info.host(), \"actix.rs\");\n    }\n\n    #[actix_rt::test]\n    async fn peer_addr_extract() {\n        let req = TestRequest::default().to_http_request();\n        let res = PeerAddr::extract(&req).await;\n        assert!(res.is_err());\n\n        let addr = \"127.0.0.1:8080\".parse().unwrap();\n        let req = TestRequest::default().peer_addr(addr).to_http_request();\n        let peer_addr = PeerAddr::extract(&req).await.unwrap();\n        assert_eq!(peer_addr, PeerAddr(addr));\n    }\n\n    #[actix_rt::test]\n    async fn remote_address() {\n        let req = TestRequest::default().to_http_request();\n        let res = ConnectionInfo::extract(&req).await.unwrap();\n        assert!(res.peer_addr().is_none());\n\n        let addr = \"127.0.0.1:8080\".parse().unwrap();\n        let req = TestRequest::default().peer_addr(addr).to_http_request();\n        let conn_info = ConnectionInfo::extract(&req).await.unwrap();\n        assert_eq!(conn_info.peer_addr().unwrap(), \"127.0.0.1\");\n    }\n\n    #[actix_rt::test]\n    async fn real_ip_from_socket_addr() {\n        let req = TestRequest::default().to_http_request();\n        let res = ConnectionInfo::extract(&req).await.unwrap();\n        assert!(res.realip_remote_addr().is_none());\n\n        let addr = \"127.0.0.1:8080\".parse().unwrap();\n        let req = TestRequest::default().peer_addr(addr).to_http_request();\n        let conn_info = ConnectionInfo::extract(&req).await.unwrap();\n        assert_eq!(conn_info.realip_remote_addr().unwrap(), \"127.0.0.1\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/introspection.rs",
    "content": "//! Experimental route introspection helpers.\n//!\n//! Enabled with the `experimental-introspection` feature.\n//!\n//! What it reports:\n//! - Configured routes with their patterns, method guards, guard details, and resource metadata\n//!   (`resource_name`, `resource_type`, `scope_depth`).\n//! - Reachability hints for routes that may be shadowed by registration order or conflicting\n//!   method guards.\n//! - External resources (used only for URL generation) in a separate report, including the scope\n//!   path where they were registered. External resources never participate in request routing.\n//!\n//! Notes:\n//! - Method lists are best-effort and derived only from explicit method guards; an empty list means\n//!   no explicit method guards were observed for the node.\n//! - Guard and method lists are aggregated per `full_path` and do not preserve per-route\n//!   correlations when multiple routes/services share the same path.\n//! - Reachability hints are best-effort and should be treated as diagnostics, not a hard guarantee.\n//!\n//! This feature is intended for local/non-production use. Avoid exposing introspection endpoints\n//! in production, since reports can include sensitive configuration details.\n\nuse std::{\n    collections::{BTreeMap, BTreeSet},\n    fmt::Write as FmtWrite,\n};\n\nuse serde::Serialize;\n\nuse crate::{\n    dev::ResourceDef,\n    guard::{Guard, GuardDetail},\n    http::Method,\n};\n\n#[derive(Clone)]\nstruct RouteDetail {\n    methods: Vec<Method>,\n    guards: Vec<String>,\n    guard_details: Vec<GuardReport>,\n    patterns: Vec<String>,\n    resource_name: Option<String>,\n    is_resource: bool,\n}\n\n/// Input data for registering routes with the introspector.\n#[derive(Clone)]\npub(crate) struct RouteInfo {\n    full_path: String,\n    methods: Vec<Method>,\n    guards: Vec<String>,\n    guard_details: Vec<GuardReport>,\n    patterns: Vec<String>,\n    resource_name: Option<String>,\n}\n\nimpl RouteInfo {\n    pub(crate) fn new(\n        full_path: String,\n        methods: Vec<Method>,\n        guards: Vec<String>,\n        guard_details: Vec<GuardReport>,\n        patterns: Vec<String>,\n        resource_name: Option<String>,\n    ) -> Self {\n        Self {\n            full_path,\n            methods,\n            guards,\n            guard_details,\n            patterns,\n            resource_name,\n        }\n    }\n}\n\n#[non_exhaustive]\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct GuardReport {\n    pub name: String,\n    #[serde(skip_serializing_if = \"Vec::is_empty\")]\n    pub details: Vec<GuardDetailReport>,\n}\n\n#[non_exhaustive]\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\n#[serde(tag = \"kind\", rename_all = \"snake_case\")]\npub enum GuardDetailReport {\n    HttpMethods { methods: Vec<String> },\n    Headers { headers: Vec<HeaderReport> },\n    Generic { value: String },\n}\n\n#[non_exhaustive]\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct HeaderReport {\n    pub name: String,\n    pub value: String,\n}\n\n/// A report item for an external resource configured for URL generation.\n///\n/// `origin_scope` is the scope path where the external resource was registered. It is informational\n/// only and does not affect URL generation or routing; external resources are always global.\n#[non_exhaustive]\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct ExternalResourceReportItem {\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub name: Option<String>,\n    pub patterns: Vec<String>,\n    pub origin_scope: String,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum RegistrationKind {\n    Service,\n    Route,\n}\n\n#[derive(Clone)]\nstruct Registration {\n    order: usize,\n    kind: RegistrationKind,\n    scope_id: Option<usize>,\n    parent_scope_id: Option<usize>,\n    full_path: String,\n    is_prefix: bool,\n    methods: Vec<Method>,\n    guards: Vec<String>,\n}\n\n#[derive(Clone)]\nstruct ShadowingContext {\n    path: String,\n    order: usize,\n}\n\n/// Node type within an introspection tree.\n#[non_exhaustive]\n#[derive(Debug, Clone, Copy)]\npub enum ResourceType {\n    /// The application root.\n    App,\n    /// A scope/prefix path.\n    Scope,\n    /// A resource (route) path.\n    Resource,\n}\n\nfn resource_type_label(kind: ResourceType) -> &'static str {\n    match kind {\n        ResourceType::App => \"app\",\n        ResourceType::Scope => \"scope\",\n        ResourceType::Resource => \"resource\",\n    }\n}\n\n/// A node in the introspection tree.\n#[non_exhaustive]\n#[derive(Debug, Clone)]\npub struct IntrospectionNode {\n    /// The node's classification.\n    pub kind: ResourceType,\n    /// The path segment used for this node.\n    pub pattern: String,\n    /// The full path for this node.\n    pub full_path: String,\n    /// HTTP methods derived from explicit method guards.\n    pub methods: Vec<Method>,\n    /// Guard names attached to this node.\n    pub guards: Vec<String>,\n    /// Structured guard details, when available.\n    pub guard_details: Vec<GuardReport>,\n    /// Resource name, when configured.\n    pub resource_name: Option<String>,\n    /// Original patterns used for this resource.\n    pub patterns: Vec<String>,\n    /// Child nodes under this prefix.\n    pub children: Vec<IntrospectionNode>,\n    /// True if the node might be unreachable at runtime.\n    pub potentially_unreachable: bool,\n    /// Reasons for potential unreachability.\n    pub reachability_notes: Vec<String>,\n}\n\n/// A flattened report item for a route.\n#[non_exhaustive]\n#[derive(Debug, Clone, Serialize)]\npub struct IntrospectionReportItem {\n    /// Full path for the route.\n    pub full_path: String,\n    /// Methods derived from explicit method guards.\n    ///\n    /// An empty list indicates no explicit method guards were observed for the node.\n    pub methods: Vec<String>,\n    /// Guard names attached to the route.\n    ///\n    /// This is aggregated per `full_path` and does not necessarily represent a single matching\n    /// condition when multiple routes/services share the same path.\n    pub guards: Vec<String>,\n    /// Structured guard details, when available.\n    ///\n    /// Includes method guards even if `guards` filters them out for readability.\n    #[serde(skip_serializing_if = \"Vec::is_empty\")]\n    pub guards_detail: Vec<GuardReport>,\n    /// Resource name, when configured.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub resource_name: Option<String>,\n    /// Original patterns used for this resource.\n    ///\n    /// These are raw ResourceDef patterns (may be relative to a scope), not expanded full paths.\n    #[serde(skip_serializing_if = \"Vec::is_empty\")]\n    pub patterns: Vec<String>,\n    /// The type of node represented by the report item.\n    pub resource_type: String,\n    /// Depth within this report tree (root = 0).\n    ///\n    /// This currently corresponds to the number of path segments (for example, `/foo` has depth 1\n    /// and `/foo/bar` has depth 2).\n    pub scope_depth: usize,\n    /// True if the route might be unreachable at runtime.\n    #[serde(skip_serializing_if = \"is_false\")]\n    pub potentially_unreachable: bool,\n    /// Reasons for potential unreachability.\n    #[serde(skip_serializing_if = \"Vec::is_empty\")]\n    pub reachability_notes: Vec<String>,\n}\n\nimpl IntrospectionNode {\n    pub fn new(kind: ResourceType, pattern: String, full_path: String) -> Self {\n        IntrospectionNode {\n            kind,\n            pattern,\n            full_path,\n            methods: Vec::new(),\n            guards: Vec::new(),\n            guard_details: Vec::new(),\n            resource_name: None,\n            patterns: Vec::new(),\n            children: Vec::new(),\n            potentially_unreachable: false,\n            reachability_notes: Vec::new(),\n        }\n    }\n}\n\nimpl From<&IntrospectionNode> for Vec<IntrospectionReportItem> {\n    fn from(node: &IntrospectionNode) -> Self {\n        fn collect_report_items(\n            node: &IntrospectionNode,\n            report_items: &mut Vec<IntrospectionReportItem>,\n            depth: usize,\n        ) {\n            let include_node = matches!(node.kind, ResourceType::Resource)\n                || !node.methods.is_empty()\n                || !node.guards.is_empty()\n                || node.potentially_unreachable;\n\n            if include_node {\n                let method_names = node\n                    .methods\n                    .iter()\n                    .map(|m| m.to_string())\n                    .collect::<Vec<_>>();\n                let filtered_guards = filter_guard_names(&node.guards, &node.methods);\n\n                report_items.push(IntrospectionReportItem {\n                    full_path: node.full_path.clone(),\n                    methods: method_names,\n                    guards: filtered_guards,\n                    guards_detail: node.guard_details.clone(),\n                    resource_name: node.resource_name.clone(),\n                    patterns: node.patterns.clone(),\n                    resource_type: resource_type_label(node.kind).to_string(),\n                    scope_depth: depth,\n                    potentially_unreachable: node.potentially_unreachable,\n                    reachability_notes: node.reachability_notes.clone(),\n                });\n            }\n\n            for child in &node.children {\n                collect_report_items(child, report_items, depth + 1);\n            }\n        }\n\n        let mut report_items = Vec::new();\n        collect_report_items(node, &mut report_items, 0);\n        report_items\n    }\n}\n\n/// Collects route details during app configuration.\n#[derive(Clone, Default)]\npub(crate) struct IntrospectionCollector {\n    details: BTreeMap<String, RouteDetail>,\n    registrations: Vec<Registration>,\n    externals: Vec<ExternalResourceReportItem>,\n    next_registration_order: usize,\n    next_scope_id: usize,\n}\n\nimpl IntrospectionCollector {\n    /// Creates a new, empty collector.\n    pub(crate) fn new() -> Self {\n        Self {\n            details: BTreeMap::new(),\n            registrations: Vec::new(),\n            externals: Vec::new(),\n            next_registration_order: 0,\n            next_scope_id: 0,\n        }\n    }\n\n    pub(crate) fn next_scope_id(&mut self) -> usize {\n        let scope_id = self.next_scope_id;\n        self.next_scope_id += 1;\n        scope_id\n    }\n\n    pub(crate) fn register_service(\n        &mut self,\n        info: RouteInfo,\n        is_resource: bool,\n        is_prefix: bool,\n        scope_id: Option<usize>,\n        parent_scope_id: Option<usize>,\n    ) {\n        let full_path = normalize_path(&info.full_path);\n\n        self.register_pattern_detail(&full_path, &info, is_resource);\n\n        self.registrations.push(Registration {\n            order: self.next_registration_order,\n            kind: RegistrationKind::Service,\n            scope_id,\n            parent_scope_id,\n            full_path,\n            is_prefix,\n            methods: info.methods,\n            guards: info.guards,\n        });\n        self.next_registration_order += 1;\n    }\n\n    pub(crate) fn register_route(&mut self, info: RouteInfo, scope_id: Option<usize>) {\n        let full_path = normalize_path(&info.full_path);\n\n        self.register_pattern_detail(&full_path, &info, true);\n\n        self.registrations.push(Registration {\n            order: self.next_registration_order,\n            kind: RegistrationKind::Route,\n            scope_id,\n            parent_scope_id: None,\n            full_path,\n            is_prefix: false,\n            methods: info.methods,\n            guards: info.guards,\n        });\n        self.next_registration_order += 1;\n    }\n\n    pub(crate) fn register_external(&mut self, rdef: &ResourceDef, origin_scope: &str) {\n        let report = external_report_from_rdef(rdef, origin_scope);\n\n        if let Some(name) = report.name.as_deref() {\n            if let Some(existing) = self\n                .externals\n                .iter_mut()\n                .find(|item| item.name.as_deref() == Some(name))\n            {\n                *existing = report;\n                return;\n            }\n        }\n\n        if !self.externals.contains(&report) {\n            self.externals.push(report);\n        }\n    }\n\n    /// Registers details for a route pattern.\n    fn register_pattern_detail(&mut self, full_path: &str, info: &RouteInfo, is_resource: bool) {\n        let full_path = normalize_path(full_path);\n\n        self.details\n            .entry(full_path)\n            .and_modify(|d| {\n                update_unique(&mut d.methods, &info.methods);\n                update_unique(&mut d.guards, &info.guards);\n                merge_guard_reports(&mut d.guard_details, &info.guard_details);\n                update_unique(&mut d.patterns, &info.patterns);\n                if d.resource_name.is_none() {\n                    d.resource_name = info.resource_name.clone();\n                }\n                if !d.is_resource && is_resource {\n                    d.is_resource = true;\n                }\n            })\n            .or_insert(RouteDetail {\n                methods: info.methods.clone(),\n                guards: info.guards.clone(),\n                guard_details: info.guard_details.clone(),\n                patterns: info.patterns.clone(),\n                resource_name: info.resource_name.clone(),\n                is_resource,\n            });\n    }\n\n    /// Produces the finalized introspection tree.\n    pub(crate) fn finalize(&mut self) -> IntrospectionTree {\n        let detail_registry = std::mem::take(&mut self.details);\n        let registrations = std::mem::take(&mut self.registrations);\n        let externals = std::mem::take(&mut self.externals);\n        let mut root = IntrospectionNode::new(ResourceType::App, \"\".into(), \"\".into());\n\n        for (full_path, _) in detail_registry.iter() {\n            let parts = split_path_segments(full_path);\n            let mut current_node = &mut root;\n            let mut assembled = String::new();\n\n            for part in parts.iter() {\n                assembled.push('/');\n                assembled.push_str(part);\n\n                let child_full_path = assembled.clone();\n                let existing_child_index = current_node\n                    .children\n                    .iter()\n                    .position(|n| n.pattern == *part);\n\n                let child_index = if let Some(idx) = existing_child_index {\n                    idx\n                } else {\n                    let kind = if detail_registry\n                        .get(&child_full_path)\n                        .is_some_and(|d| d.is_resource)\n                    {\n                        ResourceType::Resource\n                    } else {\n                        ResourceType::Scope\n                    };\n                    let new_node = IntrospectionNode::new(kind, part.to_string(), child_full_path);\n                    current_node.children.push(new_node);\n                    current_node.children.len() - 1\n                };\n\n                current_node = &mut current_node.children[child_index];\n\n                if let Some(detail) = detail_registry.get(&current_node.full_path) {\n                    update_unique(&mut current_node.methods, &detail.methods);\n                    update_unique(&mut current_node.guards, &detail.guards);\n                    merge_guard_reports(&mut current_node.guard_details, &detail.guard_details);\n                    update_unique(&mut current_node.patterns, &detail.patterns);\n                    if current_node.resource_name.is_none() {\n                        current_node.resource_name = detail.resource_name.clone();\n                    }\n                }\n            }\n        }\n\n        let reachability = analyze_reachability(&registrations);\n        apply_reachability(&mut root, &reachability);\n\n        IntrospectionTree { root, externals }\n    }\n}\n\n/// The finalized introspection tree.\n#[non_exhaustive]\n#[derive(Clone)]\npub struct IntrospectionTree {\n    /// Root node of the tree.\n    pub root: IntrospectionNode,\n    /// External resources configured for URL generation.\n    pub externals: Vec<ExternalResourceReportItem>,\n}\n\nimpl IntrospectionTree {\n    /// Returns a formatted, human-readable report.\n    pub fn report_as_text(&self) -> String {\n        warn_release_mode_once();\n        let report_items: Vec<IntrospectionReportItem> = (&self.root).into();\n\n        let mut buf = String::new();\n        for item in report_items {\n            let full_path = sanitize_text(&item.full_path);\n            let methods = item\n                .methods\n                .iter()\n                .map(|method| sanitize_text(method))\n                .collect::<Vec<_>>();\n            let guards = item\n                .guards\n                .iter()\n                .map(|guard| sanitize_text(guard))\n                .collect::<Vec<_>>();\n            writeln!(\n                buf,\n                \"{} => Methods: {:?} | Guards: {:?}{}\",\n                full_path,\n                methods,\n                guards,\n                format_reachability(&item)\n            )\n            .unwrap();\n        }\n\n        buf\n    }\n\n    /// Returns a JSON report of configured routes.\n    pub fn report_as_json(&self) -> String {\n        warn_release_mode_once();\n        let report_items: Vec<IntrospectionReportItem> = (&self.root).into();\n        serde_json::to_string_pretty(&report_items).unwrap()\n    }\n\n    /// Returns a JSON report of external resources.\n    pub fn report_externals_as_json(&self) -> String {\n        warn_release_mode_once();\n        serde_json::to_string_pretty(&self.externals).unwrap()\n    }\n}\n\npub(crate) fn guard_reports_from_iter<'a, I>(guards: I) -> Vec<GuardReport>\nwhere\n    I: IntoIterator<Item = &'a Box<dyn Guard>>,\n{\n    guards\n        .into_iter()\n        .map(|guard| {\n            let mut details = Vec::new();\n            if let Some(guard_details) = guard.details() {\n                for detail in guard_details {\n                    merge_guard_detail_reports(&mut details, detail.into());\n                }\n            }\n            GuardReport {\n                name: guard.name(),\n                details,\n            }\n        })\n        .collect()\n}\n\nimpl From<GuardDetail> for GuardDetailReport {\n    fn from(detail: GuardDetail) -> Self {\n        match detail {\n            GuardDetail::HttpMethods(methods) => GuardDetailReport::HttpMethods { methods },\n            GuardDetail::Headers(headers) => GuardDetailReport::Headers {\n                headers: headers\n                    .into_iter()\n                    .map(|(name, value)| HeaderReport { name, value })\n                    .collect(),\n            },\n            GuardDetail::Generic(value) => GuardDetailReport::Generic { value },\n        }\n    }\n}\n\npub(crate) fn external_report_from_rdef(\n    rdef: &ResourceDef,\n    origin_scope: &str,\n) -> ExternalResourceReportItem {\n    ExternalResourceReportItem {\n        name: rdef.name().map(|name| name.to_string()),\n        patterns: rdef\n            .pattern_iter()\n            .map(|pattern| pattern.to_string())\n            .collect(),\n        origin_scope: normalize_path(origin_scope),\n    }\n}\n\npub(crate) fn expand_patterns(prefix: &str, rdef: &ResourceDef) -> Vec<String> {\n    let mut full_paths = Vec::new();\n\n    if prefix.is_empty() {\n        for pat in rdef.pattern_iter() {\n            full_paths.push(normalize_path(pat));\n        }\n\n        return full_paths;\n    }\n\n    let joined = ResourceDef::root_prefix(prefix).join(rdef);\n\n    for pat in joined.pattern_iter() {\n        full_paths.push(normalize_path(pat));\n    }\n\n    full_paths\n}\n\nfn analyze_reachability(registrations: &[Registration]) -> BTreeMap<String, Vec<String>> {\n    let shadowed_scopes = shadowed_scope_context(registrations);\n    let shadowed_routes = shadowed_route_context(registrations);\n\n    let mut notes_by_path: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();\n\n    for reg in registrations {\n        let mut notes = Vec::new();\n\n        if let Some(scope_id) = reg.scope_id {\n            if let Some(context) = shadowed_scopes.get(&scope_id) {\n                notes.push(\"shadowed_by_scope\".to_string());\n                notes.push(format!(\"shadowed_by_path:{}\", context.path));\n                notes.push(format!(\"shadowed_by_order:{}\", context.order));\n            }\n        }\n\n        if reg.kind == RegistrationKind::Route {\n            if let Some(context) = shadowed_routes.get(&(reg.scope_id, reg.full_path.clone())) {\n                notes.push(\"shadowed_by_route\".to_string());\n                notes.push(format!(\"shadowed_by_path:{}\", context.path));\n                notes.push(format!(\"shadowed_by_order:{}\", context.order));\n            }\n\n            if has_conflicting_methods(&reg.methods, &reg.guards) {\n                notes.push(\"conflicting_method_guards\".to_string());\n            }\n        }\n\n        if !notes.is_empty() {\n            let entry = notes_by_path.entry(reg.full_path.clone()).or_default();\n            for note in notes {\n                entry.insert(note);\n            }\n        }\n    }\n\n    notes_by_path\n        .into_iter()\n        .map(|(path, notes)| (path, notes.into_iter().collect()))\n        .collect()\n}\n\nfn shadowed_scope_context(registrations: &[Registration]) -> BTreeMap<usize, ShadowingContext> {\n    let mut groups: BTreeMap<(Option<usize>, String), Vec<&Registration>> = BTreeMap::new();\n\n    for reg in registrations {\n        if reg.kind != RegistrationKind::Service || !reg.is_prefix {\n            continue;\n        }\n\n        if reg.scope_id.is_none() {\n            continue;\n        }\n\n        groups\n            .entry((reg.parent_scope_id, reg.full_path.clone()))\n            .or_default()\n            .push(reg);\n    }\n\n    let mut shadowed = BTreeMap::new();\n\n    for regs in groups.values_mut() {\n        regs.sort_by_key(|reg| reg.order);\n\n        let mut shadowing_reg = None;\n\n        for reg in regs.iter() {\n            if matches_all(&reg.methods, &reg.guards) {\n                shadowing_reg = Some(*reg);\n                break;\n            }\n        }\n\n        if let Some(shadowing) = shadowing_reg {\n            for reg in regs.iter() {\n                if reg.order > shadowing.order {\n                    let scope_id = reg.scope_id.expect(\"scope_id must exist\");\n                    shadowed.insert(\n                        scope_id,\n                        ShadowingContext {\n                            path: shadowing.full_path.clone(),\n                            order: shadowing.order,\n                        },\n                    );\n                }\n            }\n        }\n    }\n\n    shadowed\n}\n\nfn shadowed_route_context(\n    registrations: &[Registration],\n) -> BTreeMap<(Option<usize>, String), ShadowingContext> {\n    let mut groups: BTreeMap<(Option<usize>, String), Vec<&Registration>> = BTreeMap::new();\n\n    for reg in registrations {\n        if reg.kind != RegistrationKind::Route {\n            continue;\n        }\n\n        groups\n            .entry((reg.scope_id, reg.full_path.clone()))\n            .or_default()\n            .push(reg);\n    }\n\n    let mut shadowed = BTreeMap::new();\n\n    for (key, regs) in groups {\n        let mut regs = regs;\n        regs.sort_by_key(|reg| reg.order);\n\n        for idx in 1..regs.len() {\n            let current = regs[idx];\n            let current_methods = method_set(&current.methods);\n\n            if !guards_only_methods(&current.guards, &current.methods) {\n                continue;\n            }\n\n            let mut shadowing_reg = None;\n\n            for earlier in &regs[..idx] {\n                if !guards_only_methods(&earlier.guards, &earlier.methods) {\n                    continue;\n                }\n\n                if earlier.methods.is_empty() {\n                    shadowing_reg = Some(*earlier);\n                    break;\n                }\n\n                let earlier_methods = method_set(&earlier.methods);\n                if !current_methods.is_empty() && current_methods.is_subset(&earlier_methods) {\n                    shadowing_reg = Some(*earlier);\n                    break;\n                }\n            }\n\n            if let Some(reg) = shadowing_reg {\n                shadowed.insert(\n                    key.clone(),\n                    ShadowingContext {\n                        path: reg.full_path.clone(),\n                        order: reg.order,\n                    },\n                );\n                break;\n            }\n        }\n    }\n\n    shadowed\n}\n\nfn apply_reachability(root: &mut IntrospectionNode, notes: &BTreeMap<String, Vec<String>>) {\n    fn apply(node: &mut IntrospectionNode, notes: &BTreeMap<String, Vec<String>>) {\n        if let Some(node_notes) = notes.get(&node.full_path) {\n            node.potentially_unreachable = true;\n            node.reachability_notes = node_notes.clone();\n        }\n\n        for child in &mut node.children {\n            apply(child, notes);\n        }\n    }\n\n    apply(root, notes);\n}\n\nfn normalize_path(path: &str) -> String {\n    if path.is_empty() {\n        return \"/\".to_string();\n    }\n\n    if path.starts_with('/') {\n        path.to_string()\n    } else {\n        let mut buf = String::with_capacity(path.len() + 1);\n        buf.push('/');\n        buf.push_str(path);\n        buf\n    }\n}\n\nfn split_path_segments(path: &str) -> Vec<&str> {\n    let trimmed = path.strip_prefix('/').unwrap_or(path);\n\n    if trimmed.is_empty() {\n        return vec![\"\"];\n    }\n\n    trimmed.split('/').collect()\n}\n\nfn matches_all(methods: &[Method], guards: &[String]) -> bool {\n    methods.is_empty() && filter_guard_names(guards, methods).is_empty()\n}\n\nfn guards_only_methods(guards: &[String], methods: &[Method]) -> bool {\n    filter_guard_names(guards, methods).is_empty()\n}\n\nfn has_conflicting_methods(methods: &[Method], guards: &[String]) -> bool {\n    // This check is best-effort: it tries to determine if the conjunction of method guards can\n    // match any single HTTP method. It relies on guard names since introspection details flatten\n    // guard structure.\n    if method_set(methods).len() <= 1 {\n        return false;\n    }\n\n    fn split_top_level_args(mut args: &str) -> Vec<&str> {\n        args = args.trim();\n        if args.is_empty() {\n            return Vec::new();\n        }\n\n        let mut parts = Vec::new();\n        let mut depth = 0usize;\n        let mut start = 0usize;\n\n        for (idx, ch) in args.char_indices() {\n            match ch {\n                '(' => depth += 1,\n                ')' => depth = depth.saturating_sub(1),\n                ',' if depth == 0 => {\n                    parts.push(args[start..idx].trim());\n                    start = idx + 1;\n                }\n                _ => {}\n            }\n        }\n\n        parts.push(args[start..].trim());\n        parts.into_iter().filter(|s| !s.is_empty()).collect()\n    }\n\n    fn parse_method(name: &str) -> Option<BTreeSet<String>> {\n        name.trim().parse::<Method>().ok().map(|method| {\n            let mut set = BTreeSet::new();\n            set.insert(method.to_string());\n            set\n        })\n    }\n\n    fn union_methods(\n        left: Option<BTreeSet<String>>,\n        right: Option<BTreeSet<String>>,\n    ) -> Option<BTreeSet<String>> {\n        match (left, right) {\n            // If any branch doesn't constrain methods, the disjunction doesn't either.\n            (None, _) | (_, None) => None,\n            (Some(mut a), Some(b)) => {\n                a.extend(b);\n                Some(a)\n            }\n        }\n    }\n\n    fn intersect_methods(\n        left: Option<BTreeSet<String>>,\n        right: Option<BTreeSet<String>>,\n    ) -> Option<BTreeSet<String>> {\n        match (left, right) {\n            (None, x) | (x, None) => x,\n            (Some(a), Some(b)) => Some(a.intersection(&b).cloned().collect()),\n        }\n    }\n\n    fn guard_possible_methods(name: &str) -> Option<BTreeSet<String>> {\n        let name = name.trim();\n        if name.is_empty() {\n            return None;\n        }\n\n        if let Some(set) = parse_method(name) {\n            return Some(set);\n        }\n\n        if let Some(inner) = name\n            .strip_prefix(\"AnyGuard(\")\n            .and_then(|s| s.strip_suffix(')'))\n        {\n            let mut acc = Some(BTreeSet::new());\n            for arg in split_top_level_args(inner) {\n                acc = union_methods(acc, guard_possible_methods(arg));\n                if acc.is_none() {\n                    break;\n                }\n            }\n            return acc;\n        }\n\n        if let Some(inner) = name\n            .strip_prefix(\"AllGuard(\")\n            .and_then(|s| s.strip_suffix(')'))\n        {\n            let mut acc = None;\n            for arg in split_top_level_args(inner) {\n                acc = intersect_methods(acc, guard_possible_methods(arg));\n                if matches!(acc, Some(ref set) if set.is_empty()) {\n                    break;\n                }\n            }\n            return acc;\n        }\n\n        // `Not(...)` (and unknown/custom guard names) are treated as not restricting methods.\n        None\n    }\n\n    let mut possible = None;\n    for guard in guards {\n        possible = intersect_methods(possible, guard_possible_methods(guard));\n        if matches!(possible, Some(ref set) if set.is_empty()) {\n            return true;\n        }\n    }\n\n    false\n}\n\nfn method_set(methods: &[Method]) -> BTreeSet<String> {\n    methods.iter().map(|m| m.to_string()).collect()\n}\n\nfn filter_guard_names(guards: &[String], methods: &[Method]) -> Vec<String> {\n    let method_names = method_set(methods);\n    guards\n        .iter()\n        .filter(|guard| !method_names.iter().any(|method| method == *guard))\n        .cloned()\n        .collect()\n}\n\nfn merge_guard_reports(existing: &mut Vec<GuardReport>, incoming: &[GuardReport]) {\n    for report in incoming {\n        if let Some(existing_report) = existing.iter_mut().find(|r| r.name == report.name) {\n            for detail in &report.details {\n                merge_guard_detail_reports(&mut existing_report.details, detail.clone());\n            }\n        } else {\n            existing.push(report.clone());\n        }\n    }\n}\n\nfn merge_guard_detail_reports(existing: &mut Vec<GuardDetailReport>, incoming: GuardDetailReport) {\n    match incoming {\n        GuardDetailReport::HttpMethods { methods } => {\n            if let Some(existing_methods) = existing.iter_mut().find_map(|detail| {\n                if let GuardDetailReport::HttpMethods { methods } = detail {\n                    Some(methods)\n                } else {\n                    None\n                }\n            }) {\n                update_unique(existing_methods, &methods);\n            } else {\n                existing.push(GuardDetailReport::HttpMethods { methods });\n            }\n        }\n        GuardDetailReport::Headers { headers } => {\n            if let Some(existing_headers) = existing.iter_mut().find_map(|detail| {\n                if let GuardDetailReport::Headers { headers } = detail {\n                    Some(headers)\n                } else {\n                    None\n                }\n            }) {\n                update_unique(existing_headers, &headers);\n            } else {\n                existing.push(GuardDetailReport::Headers { headers });\n            }\n        }\n        GuardDetailReport::Generic { value } => {\n            let detail = GuardDetailReport::Generic { value };\n            if !existing.contains(&detail) {\n                existing.push(detail);\n            }\n        }\n    }\n}\n\nfn update_unique<T: Clone + PartialEq>(existing: &mut Vec<T>, new_items: &[T]) {\n    for item in new_items {\n        if !existing.contains(item) {\n            existing.push(item.clone());\n        }\n    }\n}\n\nfn is_false(value: &bool) -> bool {\n    !*value\n}\n\nfn format_reachability(item: &IntrospectionReportItem) -> String {\n    if !item.potentially_unreachable {\n        return String::new();\n    }\n\n    if item.reachability_notes.is_empty() {\n        \" | PotentiallyUnreachable\".to_string()\n    } else {\n        let notes = item\n            .reachability_notes\n            .iter()\n            .map(|note| sanitize_text(note))\n            .collect::<Vec<_>>();\n        format!(\" | PotentiallyUnreachable | Notes: {:?}\", notes)\n    }\n}\n\nfn sanitize_text(value: &str) -> String {\n    // Escape control characters to keep the text report format stable in logs/terminals.\n    let mut buf = String::with_capacity(value.len());\n    for ch in value.chars() {\n        if ch.is_control() {\n            let code = ch as u32;\n            if code <= 0xFF {\n                write!(buf, \"\\\\x{:02x}\", code).unwrap();\n            } else {\n                write!(buf, \"\\\\u{{{:x}}}\", code).unwrap();\n            }\n        } else {\n            buf.push(ch);\n        }\n    }\n    buf\n}\n\nfn warn_release_mode_once() {\n    #[cfg(not(debug_assertions))]\n    {\n        use std::sync::Once;\n\n        static WARN_ONCE: Once = Once::new();\n        WARN_ONCE.call_once(|| {\n            log::warn!(\n                \"experimental-introspection is intended for local/non-production use; \\\navoid exposing introspection endpoints in production\"\n            );\n        });\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn route_info(\n        full_path: &str,\n        methods: Vec<Method>,\n        guards: Vec<String>,\n        guard_details: Vec<GuardReport>,\n        patterns: Vec<String>,\n        resource_name: Option<String>,\n    ) -> RouteInfo {\n        RouteInfo::new(\n            full_path.to_string(),\n            methods,\n            guards,\n            guard_details,\n            patterns,\n            resource_name,\n        )\n    }\n\n    #[test]\n    fn report_includes_resources_without_methods() {\n        let mut collector = IntrospectionCollector::new();\n        let info = route_info(\n            \"/no-guards\",\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/no-guards\")\n            .expect(\"missing resource without guards\");\n\n        assert!(item.methods.is_empty());\n        assert!(item.guards.is_empty());\n        assert_eq!(item.resource_type, \"resource\");\n        assert!(!item.potentially_unreachable);\n        assert!(item.reachability_notes.is_empty());\n    }\n\n    #[test]\n    fn report_includes_guard_details_and_metadata() {\n        let mut collector = IntrospectionCollector::new();\n        let guard_details = vec![GuardReport {\n            name: \"Header(accept, text/plain)\".to_string(),\n            details: vec![GuardDetailReport::Headers {\n                headers: vec![HeaderReport {\n                    name: \"accept\".to_string(),\n                    value: \"text/plain\".to_string(),\n                }],\n            }],\n        }];\n\n        let info = route_info(\n            \"/meta\",\n            vec![Method::GET],\n            vec![\"Header(accept, text/plain)\".to_string()],\n            guard_details,\n            vec![\"/meta\".to_string()],\n            Some(\"meta-resource\".to_string()),\n        );\n        collector.register_route(info, None);\n\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/meta\")\n            .expect(\"missing metadata route\");\n\n        assert_eq!(item.resource_name.as_deref(), Some(\"meta-resource\"));\n        assert!(item.patterns.contains(&\"/meta\".to_string()));\n        assert_eq!(item.resource_type, \"resource\");\n        assert_eq!(item.scope_depth, 1);\n        assert_eq!(item.guards_detail.len(), 1);\n    }\n\n    #[test]\n    fn expand_patterns_handles_scope_paths() {\n        let empty = ResourceDef::new(\"\");\n        let slash = ResourceDef::new(\"/\");\n\n        assert_eq!(expand_patterns(\"/app\", &empty), vec![\"/app\"]);\n        assert_eq!(expand_patterns(\"/app\", &slash), vec![\"/app/\"]);\n        assert_eq!(expand_patterns(\"/app/\", &empty), vec![\"/app/\"]);\n        assert_eq!(expand_patterns(\"/app/\", &slash), vec![\"/app//\"]);\n    }\n\n    #[test]\n    fn expand_patterns_handles_multi_patterns() {\n        let rdef = ResourceDef::new([\"/a\", \"/b\"]);\n        assert_eq!(expand_patterns(\"/api\", &rdef), vec![\"/api/a\", \"/api/b\"]);\n    }\n\n    #[test]\n    fn conflicting_method_guards_mark_unreachable() {\n        let mut collector = IntrospectionCollector::new();\n        let info = route_info(\n            \"/all-guard\",\n            vec![Method::GET, Method::POST],\n            vec![\"AllGuard(GET, POST)\".to_string()],\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/all-guard\")\n            .expect(\"missing route\");\n\n        assert!(item.potentially_unreachable);\n        assert!(item\n            .reachability_notes\n            .contains(&\"conflicting_method_guards\".to_string()));\n    }\n\n    #[test]\n    fn allguard_anyguard_does_not_mark_conflict_when_methods_are_feasible() {\n        let mut collector = IntrospectionCollector::new();\n        let info = route_info(\n            \"/feasible\",\n            vec![Method::GET, Method::POST],\n            vec![\n                \"AllGuard(AnyGuard(GET, POST), Header(x, y))\".to_string(),\n                \"Header(x, y)\".to_string(),\n            ],\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/feasible\")\n            .expect(\"missing route\");\n\n        assert!(!item.potentially_unreachable);\n        assert!(!item\n            .reachability_notes\n            .contains(&\"conflicting_method_guards\".to_string()));\n    }\n\n    #[test]\n    fn allguard_anyguard_marks_conflict_when_methods_are_impossible() {\n        let mut collector = IntrospectionCollector::new();\n        let info = route_info(\n            \"/impossible\",\n            vec![Method::GET, Method::POST],\n            vec![\"AllGuard(GET, AnyGuard(POST))\".to_string()],\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/impossible\")\n            .expect(\"missing route\");\n\n        assert!(item.potentially_unreachable);\n        assert!(item\n            .reachability_notes\n            .contains(&\"conflicting_method_guards\".to_string()));\n    }\n\n    #[test]\n    fn shadowed_scopes_mark_routes() {\n        let mut collector = IntrospectionCollector::new();\n\n        let scope_a = collector.next_scope_id();\n        let info = route_info(\n            \"/extra\",\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_service(info, true, true, Some(scope_a), None);\n        let info = route_info(\n            \"/extra/ping\",\n            vec![Method::GET],\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, Some(scope_a));\n\n        let scope_b = collector.next_scope_id();\n        let info = route_info(\n            \"/extra\",\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_service(info, true, true, Some(scope_b), None);\n        let info = route_info(\n            \"/extra/ping\",\n            vec![Method::POST],\n            Vec::new(),\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, Some(scope_b));\n\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/extra/ping\")\n            .expect(\"missing route\");\n\n        assert!(item.potentially_unreachable);\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_scope\".to_string()));\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_path:/extra\".to_string()));\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_order:0\".to_string()));\n    }\n\n    #[test]\n    fn shadowed_routes_include_context() {\n        let mut collector = IntrospectionCollector::new();\n\n        let info = route_info(\n            \"/shadow\",\n            vec![Method::GET],\n            vec![\"GET\".to_string()],\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n        let info = route_info(\n            \"/shadow\",\n            vec![Method::GET],\n            vec![\"GET\".to_string()],\n            Vec::new(),\n            Vec::new(),\n            None,\n        );\n        collector.register_route(info, None);\n\n        let tree = collector.finalize();\n        let items: Vec<IntrospectionReportItem> = (&tree.root).into();\n\n        let item = items\n            .iter()\n            .find(|item| item.full_path == \"/shadow\")\n            .expect(\"missing route\");\n\n        assert!(item.potentially_unreachable);\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_route\".to_string()));\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_path:/shadow\".to_string()));\n        assert!(item\n            .reachability_notes\n            .contains(&\"shadowed_by_order:0\".to_string()));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/lib.rs",
    "content": "//! Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.\n//!\n//! # Examples\n//! ```no_run\n//! use actix_web::{get, web, App, HttpServer, Responder};\n//!\n//! #[get(\"/hello/{name}\")]\n//! async fn greet(name: web::Path<String>) -> impl Responder {\n//!     format!(\"Hello {}!\", name)\n//! }\n//!\n//! #[actix_web::main] // or #[tokio::main]\n//! async fn main() -> std::io::Result<()> {\n//!     HttpServer::new(|| {\n//!         App::new().service(greet)\n//!     })\n//!     .bind((\"127.0.0.1\", 8080))?\n//!     .run()\n//!     .await\n//! }\n//! ```\n//!\n//! # Documentation & Community Resources\n//! In addition to this API documentation, several other resources are available:\n//!\n//! * [Website & User Guide](https://actix.rs/)\n//! * [Examples Repository](https://github.com/actix/examples)\n//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x)\n//!\n//! To get started navigating the API docs, you may consider looking at the following pages first:\n//!\n//! * [`App`]: This struct represents an Actix Web application and is used to\n//!   configure routes and other common application settings.\n//!\n//! * [`HttpServer`]: This struct represents an HTTP server instance and is\n//!   used to instantiate and configure servers.\n//!\n//! * [`web`]: This module provides essential types for route registration as well as\n//!   common utilities for request handlers.\n//!\n//! * [`HttpRequest`] and [`HttpResponse`]: These\n//!   structs represent HTTP requests and responses and expose methods for creating, inspecting,\n//!   and otherwise utilizing them.\n//!\n//! # Features\n//! - Supports HTTP/1.x and HTTP/2\n//! - Streaming and pipelining\n//! - Powerful [request routing](https://actix.rs/docs/url-dispatch/) with optional macros\n//! - Full [Tokio](https://tokio.rs) compatibility\n//! - Keep-alive and slow requests handling\n//! - Client/server [WebSockets](https://actix.rs/docs/websockets/) support\n//! - Transparent content compression/decompression (br, gzip, deflate, zstd)\n//! - Multipart streams\n//! - Static assets\n//! - SSL support using OpenSSL or Rustls\n//! - Middlewares ([Logger, Session, CORS, etc](middleware))\n//! - Integrates with the [`awc` HTTP client](https://docs.rs/awc/)\n//! - Runs on stable Rust 1.88+\n//!\n//! # Crate Features\n//! - `cookies` - cookies support (enabled by default)\n//! - `macros` - routing and runtime macros (enabled by default)\n//! - `compress-brotli` - brotli content encoding compression support (enabled by default)\n//! - `compress-gzip` - gzip and deflate content encoding compression support (enabled by default)\n//! - `compress-zstd` - zstd content encoding compression support (enabled by default)\n//! - `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`\n//! - `rustls` - HTTPS support via `rustls` 0.20 crate, supports `HTTP/2`\n//! - `rustls-0_21` - HTTPS support via `rustls` 0.21 crate, supports `HTTP/2`\n//! - `rustls-0_22` - HTTPS support via `rustls` 0.22 crate, supports `HTTP/2`\n//! - `rustls-0_23` - HTTPS support via `rustls` 0.23 crate, supports `HTTP/2`\n//! - `secure-cookies` - secure cookies support\n//!\n//! ## Experimental features\n//! To enable faster release iterations, we mark some features as experimental.\n//! These features are prefixed with `experimental` and a breaking change may happen at any release.\n//! Please use them in a production environment at your own risk.\n//!\n//! - `experimental-introspection` - route and method reporting utilities for local diagnostics\n//!   and tooling. See `examples/introspection.rs` and `examples/introspection_multi_servers.rs`.\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\npub use actix_http::{body, HttpMessage};\n#[cfg(feature = \"cookies\")]\n#[doc(inline)]\npub use cookie;\npub use mime;\nmod app;\nmod app_service;\nmod config;\nmod data;\npub mod dev;\npub mod error;\nmod extract;\npub mod guard;\nmod handler;\nmod helpers;\npub mod http;\nmod info;\npub mod middleware;\nmod redirect;\nmod request;\nmod request_data;\nmod resource;\nmod response;\nmod rmap;\nmod route;\npub mod rt;\nmod scope;\nmod server;\nmod service;\npub mod test;\nmod thin_data;\npub(crate) mod types;\npub mod web;\n\n#[cfg(feature = \"experimental-introspection\")]\npub mod introspection;\n\n#[doc(inline)]\npub use crate::error::Result;\npub use crate::{\n    app::App,\n    error::{Error, ResponseError},\n    extract::FromRequest,\n    handler::Handler,\n    request::HttpRequest,\n    resource::Resource,\n    response::{CustomizeResponder, HttpResponse, HttpResponseBuilder, Responder},\n    route::Route,\n    scope::Scope,\n    server::HttpServer,\n    types::Either,\n};\n\nmacro_rules! codegen_reexport {\n    ($name:ident) => {\n        #[cfg(feature = \"macros\")]\n        pub use actix_web_codegen::$name;\n    };\n}\n\ncodegen_reexport!(main);\ncodegen_reexport!(test);\ncodegen_reexport!(route);\ncodegen_reexport!(routes);\ncodegen_reexport!(head);\ncodegen_reexport!(get);\ncodegen_reexport!(post);\ncodegen_reexport!(patch);\ncodegen_reexport!(put);\ncodegen_reexport!(delete);\ncodegen_reexport!(trace);\ncodegen_reexport!(connect);\ncodegen_reexport!(options);\ncodegen_reexport!(scope);\n\npub(crate) type BoxError = Box<dyn std::error::Error>;\n"
  },
  {
    "path": "actix-web/src/middleware/authors-guide.md",
    "content": "# Middleware Author's Guide\n\n## What Is A Middleware?\n\nMiddleware in Actix Web is a powerful mechanism that allows you to add additional behavior to request/response processing. It enables you to:\n\n- Pre-process incoming requests (e.g., path normalization, authentication)\n- Post-process outgoing responses (e.g., logging, compression)\n- Modify application state through ServiceRequest\n- Access external services (e.g., sessions, caching)\n\nMiddleware is registered for each App, Scope, or Resource and executed in the reverse order of registration. This means the last registered middleware is the first to process the request.\n\n## Middleware Traits\n\nActix Web's middleware system is built on two main traits:\n\n1. `Transform<S, Req>`: The builder trait that creates the actual Service. It's responsible for:\n   - Creating new middleware instances\n   - Assembling the middleware chain\n   - Handling initialization errors\n\n2. `Service<Req>`: The trait that represents the actual middleware functionality. It:\n   - Processes requests and responses\n   - Can modify both request and response\n   - Can short-circuit request processing\n   - Must be implemented for the middleware to work\n\n## Understanding Body Types\n\nWhen working with middleware, it's important to understand body types:\n\n- Middleware can work with different body types for requests and responses\n- The `MessageBody` trait is used to handle different body types\n- You can use `EitherBody` when you need to handle multiple body types\n- Be careful with body consumption - once a body is consumed, it cannot be read again\n\n## Best Practices\n\n1. Keep middleware focused and single-purpose\n2. Handle errors appropriately and propagate them correctly\n3. Be mindful of performance impact\n4. Use appropriate body types and handle them correctly\n5. Consider middleware ordering carefully\n6. Document your middleware's behavior and requirements\n7. Test your middleware thoroughly\n\n## Error Propagation\n\nProper error handling is crucial in middleware:\n\n1. Always propagate errors from the inner service\n2. Use appropriate error types\n3. Handle initialization errors\n4. Consider using custom error types for specific middleware errors\n5. Document error conditions and handling\n\n## When To (Not) Use Middleware\n\nUse middleware when you need to:\n\n- Add cross-cutting concerns\n- Modify requests/responses globally\n- Add authentication/authorization\n- Add logging or monitoring\n- Handle compression or caching\n\nAvoid middleware when:\n\n- The functionality is specific to a single route\n- The operation is better handled by a service\n- The overhead would be too high\n- The functionality can be implemented more simply\n\n## Author's References\n\n- `EitherBody` + when is middleware appropriate: https://discord.com/channels/771444961383153695/952016890723729428\n- Actix Web Documentation: https://docs.rs/actix-web\n- Service Trait Documentation: https://docs.rs/actix-service\n- MessageBody Trait Documentation: https://docs.rs/actix-web/latest/actix_web/body/trait.MessageBody.html\n"
  },
  {
    "path": "actix-web/src/middleware/compat.rs",
    "content": "//! For middleware documentation, see [`Compat`].\n\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse futures_core::{future::LocalBoxFuture, ready};\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::{BoxBody, MessageBody},\n    dev::{Service, Transform},\n    error::Error,\n    service::ServiceResponse,\n};\n\n/// Middleware for enabling any middleware to be used in [`Resource::wrap`](crate::Resource::wrap),\n/// and [`Condition`](super::Condition).\n///\n/// # Examples\n/// ```\n/// use actix_web::middleware::{Logger, Compat};\n/// use actix_web::{App, web};\n///\n/// let logger = Logger::default();\n///\n/// // this would not compile because of incompatible body types\n/// // let app = App::new()\n/// //     .service(web::scope(\"scoped\").wrap(logger));\n///\n/// // by using this middleware we can use the logger on a scope\n/// let app = App::new()\n///     .service(web::scope(\"scoped\").wrap(Compat::new(logger)));\n/// ```\npub struct Compat<T> {\n    transform: T,\n}\n\nimpl<T> Compat<T> {\n    /// Wrap a middleware to give it broader compatibility.\n    pub fn new(middleware: T) -> Self {\n        Self {\n            transform: middleware,\n        }\n    }\n}\n\nimpl<S, T, Req> Transform<S, Req> for Compat<T>\nwhere\n    S: Service<Req>,\n    T: Transform<S, Req>,\n    T::Future: 'static,\n    T::Response: MapServiceResponseBody,\n    T::Error: Into<Error>,\n{\n    type Response = ServiceResponse<BoxBody>;\n    type Error = Error;\n    type Transform = CompatMiddleware<T::Transform>;\n    type InitError = T::InitError;\n    type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        let fut = self.transform.new_transform(service);\n        Box::pin(async move {\n            let service = fut.await?;\n            Ok(CompatMiddleware { service })\n        })\n    }\n}\n\npub struct CompatMiddleware<S> {\n    service: S,\n}\n\nimpl<S, Req> Service<Req> for CompatMiddleware<S>\nwhere\n    S: Service<Req>,\n    S::Response: MapServiceResponseBody,\n    S::Error: Into<Error>,\n{\n    type Response = ServiceResponse<BoxBody>;\n    type Error = Error;\n    type Future = CompatMiddlewareFuture<S::Future>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: Req) -> Self::Future {\n        let fut = self.service.call(req);\n        CompatMiddlewareFuture { fut }\n    }\n}\n\npin_project! {\n    pub struct CompatMiddlewareFuture<Fut> {\n        #[pin]\n        fut: Fut,\n    }\n}\n\nimpl<Fut, T, E> Future for CompatMiddlewareFuture<Fut>\nwhere\n    Fut: Future<Output = Result<T, E>>,\n    T: MapServiceResponseBody,\n    E: Into<Error>,\n{\n    type Output = Result<ServiceResponse<BoxBody>, Error>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let res = match ready!(self.project().fut.poll(cx)) {\n            Ok(res) => res,\n            Err(err) => return Poll::Ready(Err(err.into())),\n        };\n\n        Poll::Ready(Ok(res.map_body()))\n    }\n}\n\n/// Convert `ServiceResponse`'s `ResponseBody<B>` generic type to `ResponseBody<Body>`.\npub trait MapServiceResponseBody {\n    fn map_body(self) -> ServiceResponse<BoxBody>;\n}\n\nimpl<B> MapServiceResponseBody for ServiceResponse<B>\nwhere\n    B: MessageBody + 'static,\n{\n    #[inline]\n    fn map_body(self) -> ServiceResponse<BoxBody> {\n        self.map_into_boxed_body()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    // easier to code when cookies feature is disabled\n    #![allow(unused_imports)]\n\n    use actix_service::IntoService;\n\n    use super::*;\n    use crate::{\n        dev::ServiceRequest,\n        http::StatusCode,\n        middleware::{self, Condition, Identity, Logger},\n        test::{self, call_service, init_service, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    #[actix_rt::test]\n    #[cfg(all(feature = \"cookies\", feature = \"__compress\"))]\n    async fn test_scope_middleware() {\n        use crate::middleware::Compress;\n\n        let logger = Logger::default();\n        let compress = Compress::default();\n\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .wrap(logger)\n                    .wrap(Compat::new(compress))\n                    .service(web::resource(\"/test\").route(web::get().to(HttpResponse::Ok))),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    #[cfg(all(feature = \"cookies\", feature = \"__compress\"))]\n    async fn test_resource_scope_middleware() {\n        use crate::middleware::Compress;\n\n        let logger = Logger::default();\n        let compress = Compress::default();\n\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"app/test\")\n                    .wrap(Compat::new(logger))\n                    .wrap(Compat::new(compress))\n                    .route(web::get().to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_condition_scope_middleware() {\n        let srv = |req: ServiceRequest| {\n            Box::pin(\n                async move { Ok(req.into_response(HttpResponse::InternalServerError().finish())) },\n            )\n        };\n\n        let logger = Logger::default();\n\n        let mw = Condition::new(true, Compat::new(logger))\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n        let resp = call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n\n    #[actix_rt::test]\n    async fn compat_noop_is_noop() {\n        let srv = test::ok_service();\n\n        let mw = Compat::new(Identity)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let resp = call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/compress.rs",
    "content": "//! For middleware documentation, see [`Compress`].\n\nuse std::{\n    future::Future,\n    marker::PhantomData,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::encoding::Encoder;\nuse actix_service::{Service, Transform};\nuse actix_utils::future::{ok, Either, Ready};\nuse futures_core::ready;\nuse mime::Mime;\nuse once_cell::sync::Lazy;\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::{EitherBody, MessageBody},\n    http::{\n        header::{self, AcceptEncoding, ContentEncoding, Encoding, HeaderValue},\n        StatusCode,\n    },\n    service::{ServiceRequest, ServiceResponse},\n    Error, HttpMessage, HttpResponse,\n};\n\n/// Middleware for compressing response payloads.\n///\n/// # Encoding Negotiation\n/// `Compress` will read the `Accept-Encoding` header to negotiate which compression codec to use.\n/// Payloads are not compressed if the header is not sent. The `compress-*` [feature flags] are also\n/// considered in this selection process.\n///\n/// # Pre-compressed Payload\n/// If you are serving some data that is already using a compressed representation (e.g., a gzip\n/// compressed HTML file from disk) you can signal this to `Compress` by setting an appropriate\n/// `Content-Encoding` header. In addition to preventing double compressing the payload, this header\n/// is required by the spec when using compressed representations and will inform the client that\n/// the content should be uncompressed.\n///\n/// However, it is not advised to unconditionally serve encoded representations of content because\n/// the client may not support it. The [`AcceptEncoding`] typed header has some utilities to help\n/// perform manual encoding negotiation, if required. When negotiating content encoding, it is also\n/// required by the spec to send a `Vary: Accept-Encoding` header.\n///\n/// A (naïve) example serving an pre-compressed Gzip file is included below.\n///\n/// # Examples\n/// To enable automatic payload compression just include `Compress` as a top-level middleware:\n/// ```\n/// use actix_web::{middleware, web, App, HttpResponse};\n///\n/// let app = App::new()\n///     .wrap(middleware::Compress::default())\n///     .default_service(web::to(|| async { HttpResponse::Ok().body(\"hello world\") }));\n/// ```\n///\n/// Pre-compressed Gzip file being served from disk with correct headers added to bypass middleware:\n/// ```no_run\n/// use actix_web::{middleware, http::header, web, App, HttpResponse, Responder};\n///\n/// async fn index_handler() -> actix_web::Result<impl Responder> {\n///     Ok(actix_files::NamedFile::open_async(\"./assets/index.html.gz\").await?\n///         .customize()\n///         .insert_header(header::ContentEncoding::Gzip))\n/// }\n///\n/// let app = App::new()\n///     .wrap(middleware::Compress::default())\n///     .default_service(web::to(index_handler));\n/// ```\n///\n/// [feature flags]: ../index.html#crate-features\n#[derive(Debug, Clone, Default)]\n#[non_exhaustive]\npub struct Compress;\n\nimpl<S, B> Transform<S, ServiceRequest> for Compress\nwhere\n    B: MessageBody,\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    type Response = ServiceResponse<EitherBody<Encoder<B>>>;\n    type Error = Error;\n    type Transform = CompressMiddleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ok(CompressMiddleware { service })\n    }\n}\n\npub struct CompressMiddleware<S> {\n    service: S,\n}\n\nimpl<S, B> Service<ServiceRequest> for CompressMiddleware<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n{\n    type Response = ServiceResponse<EitherBody<Encoder<B>>>;\n    type Error = Error;\n    #[allow(clippy::type_complexity)]\n    type Future = Either<CompressResponse<S, B>, Ready<Result<Self::Response, Self::Error>>>;\n\n    actix_service::forward_ready!(service);\n\n    #[allow(clippy::borrow_interior_mutable_const)]\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        // negotiate content-encoding\n        let accept_encoding = req.get_header::<AcceptEncoding>();\n\n        let accept_encoding = match accept_encoding {\n            // missing header; fallback to identity\n            None => {\n                return Either::left(CompressResponse {\n                    encoding: Encoding::identity(),\n                    fut: self.service.call(req),\n                    _phantom: PhantomData,\n                })\n            }\n\n            // valid accept-encoding header\n            Some(accept_encoding) => accept_encoding,\n        };\n\n        match accept_encoding.negotiate(SUPPORTED_ENCODINGS.iter()) {\n            None => {\n                let mut res = HttpResponse::with_body(\n                    StatusCode::NOT_ACCEPTABLE,\n                    SUPPORTED_ENCODINGS_STRING.as_str(),\n                );\n\n                res.headers_mut()\n                    .insert(header::VARY, HeaderValue::from_static(\"Accept-Encoding\"));\n\n                Either::right(ok(req\n                    .into_response(res)\n                    .map_into_boxed_body()\n                    .map_into_right_body()))\n            }\n\n            Some(encoding) => Either::left(CompressResponse {\n                fut: self.service.call(req),\n                encoding,\n                _phantom: PhantomData,\n            }),\n        }\n    }\n}\n\npin_project! {\n    pub struct CompressResponse<S, B>\n    where\n        S: Service<ServiceRequest>,\n    {\n        #[pin]\n        fut: S::Future,\n        encoding: Encoding,\n        _phantom: PhantomData<B>,\n    }\n}\n\nimpl<S, B> Future for CompressResponse<S, B>\nwhere\n    B: MessageBody,\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    type Output = Result<ServiceResponse<EitherBody<Encoder<B>>>, Error>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.as_mut().project();\n\n        match ready!(this.fut.poll(cx)) {\n            Ok(resp) => {\n                let enc = match this.encoding {\n                    Encoding::Known(enc) => *enc,\n                    Encoding::Unknown(enc) => {\n                        unimplemented!(\"encoding '{enc}' should not be here\");\n                    }\n                };\n\n                Poll::Ready(Ok(resp.map_body(move |head, body| {\n                    let content_type = head.headers.get(header::CONTENT_TYPE);\n\n                    fn default_compress_predicate(content_type: Option<&HeaderValue>) -> bool {\n                        match content_type {\n                            None => true,\n                            Some(hdr) => {\n                                match hdr.to_str().ok().and_then(|hdr| hdr.parse::<Mime>().ok()) {\n                                    Some(mime) if mime.type_() == mime::IMAGE => {\n                                        matches!(mime.subtype(), mime::SVG)\n                                    }\n                                    Some(mime) if mime.type_() == mime::VIDEO => false,\n                                    _ => true,\n                                }\n                            }\n                        }\n                    }\n\n                    let enc = if default_compress_predicate(content_type) {\n                        enc\n                    } else {\n                        ContentEncoding::Identity\n                    };\n\n                    EitherBody::left(Encoder::response(enc, head, body))\n                })))\n            }\n\n            Err(err) => Poll::Ready(Err(err)),\n        }\n    }\n}\n\nstatic SUPPORTED_ENCODINGS_STRING: Lazy<String> = Lazy::new(|| {\n    #[allow(unused_mut)] // only unused when no compress features enabled\n    let mut encoding: Vec<&str> = vec![];\n\n    #[cfg(feature = \"compress-brotli\")]\n    {\n        encoding.push(\"br\");\n    }\n\n    #[cfg(feature = \"compress-gzip\")]\n    {\n        encoding.push(\"gzip\");\n        encoding.push(\"deflate\");\n    }\n\n    #[cfg(feature = \"compress-zstd\")]\n    {\n        encoding.push(\"zstd\");\n    }\n\n    assert!(\n        !encoding.is_empty(),\n        \"encoding can not be empty unless __compress feature has been explicitly enabled by itself\"\n    );\n\n    encoding.join(\", \")\n});\n\nstatic SUPPORTED_ENCODINGS: &[Encoding] = &[\n    Encoding::identity(),\n    #[cfg(feature = \"compress-brotli\")]\n    {\n        Encoding::brotli()\n    },\n    #[cfg(feature = \"compress-gzip\")]\n    {\n        Encoding::gzip()\n    },\n    #[cfg(feature = \"compress-gzip\")]\n    {\n        Encoding::deflate()\n    },\n    #[cfg(feature = \"compress-zstd\")]\n    {\n        Encoding::zstd()\n    },\n];\n\n// move cfg(feature) to prevents_double_compressing if more tests are added\n#[cfg(feature = \"compress-gzip\")]\n#[cfg(test)]\nmod tests {\n    use std::collections::HashSet;\n\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::{http::header::ContentType, middleware::DefaultHeaders, test, web, App};\n\n    const HTML_DATA_PART: &str = \"<html><h1>hello world</h1></html\";\n    const HTML_DATA: &str = const_str::repeat!(HTML_DATA_PART, 100);\n\n    const TEXT_DATA_PART: &str = \"hello world \";\n    const TEXT_DATA: &str = const_str::repeat!(TEXT_DATA_PART, 100);\n\n    assert_impl_all!(Compress: Send, Sync);\n\n    pub fn gzip_decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        use std::io::Read as _;\n        let mut decoder = flate2::read::GzDecoder::new(bytes.as_ref());\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n\n    #[track_caller]\n    fn assert_successful_res_with_content_type<B>(res: &ServiceResponse<B>, ct: &str) {\n        assert!(res.status().is_success());\n        assert!(\n            res.headers()\n                .get(header::CONTENT_TYPE)\n                .expect(\"content-type header should be present\")\n                .to_str()\n                .expect(\"content-type header should be utf-8\")\n                .contains(ct),\n            \"response's content-type did not match {}\",\n            ct\n        );\n    }\n\n    #[track_caller]\n    fn assert_successful_gzip_res_with_content_type<B>(res: &ServiceResponse<B>, ct: &str) {\n        assert_successful_res_with_content_type(res, ct);\n        assert_eq!(\n            res.headers()\n                .get(header::CONTENT_ENCODING)\n                .expect(\"response should be gzip compressed\"),\n            \"gzip\",\n        );\n    }\n\n    #[track_caller]\n    fn assert_successful_identity_res_with_content_type<B>(res: &ServiceResponse<B>, ct: &str) {\n        assert_successful_res_with_content_type(res, ct);\n        assert!(\n            res.headers().get(header::CONTENT_ENCODING).is_none(),\n            \"response should not be compressed\",\n        );\n    }\n\n    #[actix_rt::test]\n    async fn prevents_double_compressing() {\n        let app = test::init_service({\n            App::new()\n                .wrap(Compress::default())\n                .route(\n                    \"/single\",\n                    web::get().to(move || HttpResponse::Ok().body(TEXT_DATA)),\n                )\n                .service(\n                    web::resource(\"/double\")\n                        .wrap(Compress::default())\n                        .wrap(DefaultHeaders::new().add((\"x-double\", \"true\")))\n                        .route(web::get().to(move || HttpResponse::Ok().body(TEXT_DATA))),\n                )\n        })\n        .await;\n\n        let req = test::TestRequest::default()\n            .uri(\"/single\")\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(res.headers().get(\"x-double\"), None);\n        assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"gzip\");\n        let bytes = test::read_body(res).await;\n        assert_eq!(gzip_decode(bytes), TEXT_DATA.as_bytes());\n\n        let req = test::TestRequest::default()\n            .uri(\"/double\")\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(res.headers().get(\"x-double\").unwrap(), \"true\");\n        assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"gzip\");\n        let bytes = test::read_body(res).await;\n        assert_eq!(gzip_decode(bytes), TEXT_DATA.as_bytes());\n    }\n\n    #[actix_rt::test]\n    async fn retains_previously_set_vary_header() {\n        let app = test::init_service({\n            App::new()\n                .wrap(Compress::default())\n                .default_service(web::to(move || {\n                    HttpResponse::Ok()\n                        .insert_header((header::VARY, \"x-test\"))\n                        .body(TEXT_DATA)\n                }))\n        })\n        .await;\n\n        let req = test::TestRequest::default()\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        #[allow(clippy::mutable_key_type)]\n        let vary_headers = res.headers().get_all(header::VARY).collect::<HashSet<_>>();\n        assert!(vary_headers.contains(&HeaderValue::from_static(\"x-test\")));\n        assert!(vary_headers.contains(&HeaderValue::from_static(\"accept-encoding\")));\n    }\n\n    fn configure_predicate_test(cfg: &mut web::ServiceConfig) {\n        cfg.route(\n            \"/html\",\n            web::get().to(|| {\n                HttpResponse::Ok()\n                    .content_type(ContentType::html())\n                    .body(HTML_DATA)\n            }),\n        )\n        .route(\n            \"/image\",\n            web::get().to(|| {\n                HttpResponse::Ok()\n                    .content_type(ContentType::jpeg())\n                    .body(TEXT_DATA)\n            }),\n        );\n    }\n\n    #[actix_rt::test]\n    async fn prevents_compression_jpeg() {\n        let app = test::init_service(\n            App::new()\n                .wrap(Compress::default())\n                .configure(configure_predicate_test),\n        )\n        .await;\n\n        let req =\n            test::TestRequest::with_uri(\"/html\").insert_header((header::ACCEPT_ENCODING, \"gzip\"));\n        let res = test::call_service(&app, req.to_request()).await;\n        assert_successful_gzip_res_with_content_type(&res, \"text/html\");\n        assert_ne!(test::read_body(res).await, HTML_DATA.as_bytes());\n\n        let req =\n            test::TestRequest::with_uri(\"/image\").insert_header((header::ACCEPT_ENCODING, \"gzip\"));\n        let res = test::call_service(&app, req.to_request()).await;\n        assert_successful_identity_res_with_content_type(&res, \"image/jpeg\");\n        assert_eq!(test::read_body(res).await, TEXT_DATA.as_bytes());\n    }\n\n    #[actix_rt::test]\n    async fn prevents_compression_empty() {\n        let app = test::init_service({\n            App::new()\n                .wrap(Compress::default())\n                .default_service(web::to(move || HttpResponse::Ok().finish()))\n        })\n        .await;\n\n        let req = test::TestRequest::default()\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert!(!res.headers().contains_key(header::CONTENT_ENCODING));\n        assert!(test::read_body(res).await.is_empty());\n    }\n\n    #[actix_rt::test]\n    async fn skips_compression_partial_content() {\n        let app = test::init_service({\n            App::new()\n                .wrap(Compress::default())\n                .default_service(web::to(|| {\n                    HttpResponse::PartialContent()\n                        .insert_header((header::CONTENT_TYPE, \"text/plain\"))\n                        .insert_header((header::CONTENT_RANGE, \"bytes 0-10/100\"))\n                        .body(TEXT_DATA)\n                }))\n        })\n        .await;\n\n        let req = test::TestRequest::default()\n            .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT);\n        assert!(!res.headers().contains_key(header::CONTENT_ENCODING));\n        assert_eq!(test::read_body(res).await, TEXT_DATA.as_bytes());\n    }\n}\n\n#[cfg(feature = \"compress-brotli\")]\n#[cfg(test)]\nmod tests_brotli {\n    use super::*;\n    use crate::{test, web, App};\n\n    #[actix_rt::test]\n    async fn prevents_compression_empty() {\n        let app = test::init_service({\n            App::new()\n                .wrap(Compress::default())\n                .default_service(web::to(move || HttpResponse::Ok().finish()))\n        })\n        .await;\n\n        let req = test::TestRequest::default()\n            .insert_header((header::ACCEPT_ENCODING, \"br\"))\n            .to_request();\n        let res = test::call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert!(!res.headers().contains_key(header::CONTENT_ENCODING));\n        assert!(test::read_body(res).await.is_empty());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/condition.rs",
    "content": "//! For middleware documentation, see [`Condition`].\n\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse futures_core::{future::LocalBoxFuture, ready};\nuse futures_util::FutureExt as _;\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::EitherBody,\n    dev::{Service, ServiceResponse, Transform},\n};\n\n/// Middleware for conditionally enabling other middleware.\n///\n/// # Examples\n/// ```\n/// use actix_web::middleware::{Condition, NormalizePath};\n/// use actix_web::App;\n///\n/// let enable_normalize = std::env::var(\"NORMALIZE_PATH\").is_ok();\n/// let app = App::new()\n///     .wrap(Condition::new(enable_normalize, NormalizePath::default()));\n/// ```\npub struct Condition<T> {\n    transformer: T,\n    enable: bool,\n}\n\nimpl<T> Condition<T> {\n    pub fn new(enable: bool, transformer: T) -> Self {\n        Self {\n            transformer,\n            enable,\n        }\n    }\n}\n\nimpl<S, T, Req, BE, BD, Err> Transform<S, Req> for Condition<T>\nwhere\n    S: Service<Req, Response = ServiceResponse<BD>, Error = Err> + 'static,\n    T: Transform<S, Req, Response = ServiceResponse<BE>, Error = Err>,\n    T::Future: 'static,\n    T::InitError: 'static,\n    T::Transform: 'static,\n{\n    type Response = ServiceResponse<EitherBody<BE, BD>>;\n    type Error = Err;\n    type Transform = ConditionMiddleware<T::Transform, S>;\n    type InitError = T::InitError;\n    type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        if self.enable {\n            let fut = self.transformer.new_transform(service);\n            async move {\n                let wrapped_svc = fut.await?;\n                Ok(ConditionMiddleware::Enable(wrapped_svc))\n            }\n            .boxed_local()\n        } else {\n            async move { Ok(ConditionMiddleware::Disable(service)) }.boxed_local()\n        }\n    }\n}\n\npub enum ConditionMiddleware<E, D> {\n    Enable(E),\n    Disable(D),\n}\n\nimpl<E, D, Req, BE, BD, Err> Service<Req> for ConditionMiddleware<E, D>\nwhere\n    E: Service<Req, Response = ServiceResponse<BE>, Error = Err>,\n    D: Service<Req, Response = ServiceResponse<BD>, Error = Err>,\n{\n    type Response = ServiceResponse<EitherBody<BE, BD>>;\n    type Error = Err;\n    type Future = ConditionMiddlewareFuture<E::Future, D::Future>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        match self {\n            ConditionMiddleware::Enable(service) => service.poll_ready(cx),\n            ConditionMiddleware::Disable(service) => service.poll_ready(cx),\n        }\n    }\n\n    fn call(&self, req: Req) -> Self::Future {\n        match self {\n            ConditionMiddleware::Enable(service) => ConditionMiddlewareFuture::Enabled {\n                fut: service.call(req),\n            },\n            ConditionMiddleware::Disable(service) => ConditionMiddlewareFuture::Disabled {\n                fut: service.call(req),\n            },\n        }\n    }\n}\n\npin_project! {\n    #[doc(hidden)]\n    #[project = ConditionProj]\n    pub enum ConditionMiddlewareFuture<E, D> {\n        Enabled { #[pin] fut: E, },\n        Disabled { #[pin] fut: D, },\n    }\n}\n\nimpl<E, D, BE, BD, Err> Future for ConditionMiddlewareFuture<E, D>\nwhere\n    E: Future<Output = Result<ServiceResponse<BE>, Err>>,\n    D: Future<Output = Result<ServiceResponse<BD>, Err>>,\n{\n    type Output = Result<ServiceResponse<EitherBody<BE, BD>>, Err>;\n\n    #[inline]\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let res = match self.project() {\n            ConditionProj::Enabled { fut } => ready!(fut.poll(cx))?.map_into_left_body(),\n            ConditionProj::Disabled { fut } => ready!(fut.poll(cx))?.map_into_right_body(),\n        };\n\n        Poll::Ready(Ok(res))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::IntoService as _;\n\n    use super::*;\n    use crate::{\n        body::BoxBody,\n        dev::ServiceRequest,\n        error::Result,\n        http::{\n            header::{HeaderValue, CONTENT_TYPE},\n            StatusCode,\n        },\n        middleware::{self, ErrorHandlerResponse, ErrorHandlers, Identity},\n        test::{self, TestRequest},\n        web::Bytes,\n        HttpResponse,\n    };\n\n    #[allow(clippy::unnecessary_wraps)]\n    fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n        res.response_mut()\n            .headers_mut()\n            .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n\n        Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n    }\n\n    #[test]\n    fn compat_with_builtin_middleware() {\n        let _ = Condition::new(true, middleware::Compat::new(Identity));\n        let _ = Condition::new(true, middleware::Logger::default());\n        let _ = Condition::new(true, middleware::Compress::default());\n        let _ = Condition::new(true, middleware::NormalizePath::trim());\n        let _ = Condition::new(true, middleware::DefaultHeaders::new());\n        let _ = Condition::new(true, middleware::ErrorHandlers::<BoxBody>::new());\n        let _ = Condition::new(true, middleware::ErrorHandlers::<Bytes>::new());\n    }\n\n    #[actix_rt::test]\n    async fn test_handler_enabled() {\n        let srv = |req: ServiceRequest| async move {\n            let resp = HttpResponse::InternalServerError().message_body(String::new())?;\n            Ok(req.into_response(resp))\n        };\n\n        let mw = ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);\n\n        let mw = Condition::new(true, mw)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let resp: ServiceResponse<EitherBody<EitherBody<_, _>, String>> =\n            test::call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n    }\n\n    #[actix_rt::test]\n    async fn test_handler_disabled() {\n        let srv = |req: ServiceRequest| async move {\n            let resp = HttpResponse::InternalServerError().message_body(String::new())?;\n            Ok(req.into_response(resp))\n        };\n\n        let mw = ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);\n\n        let mw = Condition::new(false, mw)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let resp: ServiceResponse<EitherBody<EitherBody<_, _>, String>> =\n            test::call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE), None);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/default_headers.rs",
    "content": "//! For middleware documentation, see [`DefaultHeaders`].\n\nuse std::{\n    future::Future,\n    marker::PhantomData,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_http::error::HttpError;\nuse actix_utils::future::{ready, Ready};\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    dev::{Service, Transform},\n    http::header::{HeaderMap, HeaderName, HeaderValue, TryIntoHeaderPair, CONTENT_TYPE},\n    service::{ServiceRequest, ServiceResponse},\n    Error,\n};\n\n/// Middleware for setting default response headers.\n///\n/// Headers with the same key that are already set in a response will *not* be overwritten.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, http, middleware, App, HttpResponse};\n///\n/// let app = App::new()\n///     .wrap(middleware::DefaultHeaders::new().add((\"X-Version\", \"0.2\")))\n///     .service(\n///         web::resource(\"/test\")\n///             .route(web::get().to(|| HttpResponse::Ok()))\n///             .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed()))\n///     );\n/// ```\n#[derive(Debug, Clone, Default)]\npub struct DefaultHeaders {\n    inner: Rc<Inner>,\n}\n\n#[derive(Debug, Default)]\nstruct Inner {\n    headers: HeaderMap,\n}\n\nimpl DefaultHeaders {\n    /// Constructs an empty `DefaultHeaders` middleware.\n    #[inline]\n    pub fn new() -> DefaultHeaders {\n        DefaultHeaders::default()\n    }\n\n    /// Adds a header to the default set.\n    ///\n    /// # Panics\n    /// Panics when resolved header name or value is invalid.\n    #[allow(clippy::should_implement_trait)]\n    pub fn add(mut self, header: impl TryIntoHeaderPair) -> Self {\n        // standard header terminology `insert` or `append` for this method would make the behavior\n        // of this middleware less obvious since it only adds the headers if they are not present\n\n        match header.try_into_pair() {\n            Ok((key, value)) => Rc::get_mut(&mut self.inner)\n                .expect(\"All default headers must be added before cloning.\")\n                .headers\n                .append(key, value),\n            Err(err) => panic!(\"Invalid header: {}\", err.into()),\n        }\n\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(\n        since = \"4.0.0\",\n        note = \"Prefer `.add((key, value))`. Will be removed in v5.\"\n    )]\n    pub fn header<K, V>(self, key: K, value: V) -> Self\n    where\n        HeaderName: TryFrom<K>,\n        <HeaderName as TryFrom<K>>::Error: Into<HttpError>,\n        HeaderValue: TryFrom<V>,\n        <HeaderValue as TryFrom<V>>::Error: Into<HttpError>,\n    {\n        self.add((\n            HeaderName::try_from(key)\n                .map_err(Into::into)\n                .expect(\"Invalid header name\"),\n            HeaderValue::try_from(value)\n                .map_err(Into::into)\n                .expect(\"Invalid header value\"),\n        ))\n    }\n\n    /// Adds a default *Content-Type* header if response does not contain one.\n    ///\n    /// Default is `application/octet-stream`.\n    pub fn add_content_type(self) -> Self {\n        #[allow(clippy::declare_interior_mutable_const)]\n        const HV_MIME: HeaderValue = HeaderValue::from_static(\"application/octet-stream\");\n        self.add((CONTENT_TYPE, HV_MIME))\n    }\n}\n\nimpl<S, B> Transform<S, ServiceRequest> for DefaultHeaders\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Transform = DefaultHeadersMiddleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ready(Ok(DefaultHeadersMiddleware {\n            service,\n            inner: Rc::clone(&self.inner),\n        }))\n    }\n}\n\npub struct DefaultHeadersMiddleware<S> {\n    service: S,\n    inner: Rc<Inner>,\n}\n\nimpl<S, B> Service<ServiceRequest> for DefaultHeadersMiddleware<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Future = DefaultHeaderFuture<S, B>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let inner = Rc::clone(&self.inner);\n        let fut = self.service.call(req);\n\n        DefaultHeaderFuture {\n            fut,\n            inner,\n            _body: PhantomData,\n        }\n    }\n}\n\npin_project! {\n    pub struct DefaultHeaderFuture<S: Service<ServiceRequest>, B> {\n        #[pin]\n        fut: S::Future,\n        inner: Rc<Inner>,\n        _body: PhantomData<B>,\n    }\n}\n\nimpl<S, B> Future for DefaultHeaderFuture<S, B>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    type Output = <S::Future as Future>::Output;\n\n    #[allow(clippy::borrow_interior_mutable_const)]\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let mut res = ready!(this.fut.poll(cx))?;\n\n        // set response headers\n        for (key, value) in this.inner.headers.iter() {\n            if !res.headers().contains_key(key) {\n                res.headers_mut().insert(key.clone(), value.clone());\n            }\n        }\n\n        Poll::Ready(Ok(res))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::IntoService;\n    use actix_utils::future::ok;\n\n    use super::*;\n    use crate::{\n        test::{self, TestRequest},\n        HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn adding_default_headers() {\n        let mw = DefaultHeaders::new()\n            .add((\"X-TEST\", \"0001\"))\n            .add((\"X-TEST-TWO\", HeaderValue::from_static(\"123\")))\n            .new_transform(test::ok_service())\n            .await\n            .unwrap();\n\n        let req = TestRequest::default().to_srv_request();\n        let res = mw.call(req).await.unwrap();\n        assert_eq!(res.headers().get(\"x-test\").unwrap(), \"0001\");\n        assert_eq!(res.headers().get(\"x-test-two\").unwrap(), \"123\");\n    }\n\n    #[actix_rt::test]\n    async fn no_override_existing() {\n        let req = TestRequest::default().to_srv_request();\n        let srv = |req: ServiceRequest| {\n            ok(req.into_response(\n                HttpResponse::Ok()\n                    .insert_header((CONTENT_TYPE, \"0002\"))\n                    .finish(),\n            ))\n        };\n        let mw = DefaultHeaders::new()\n            .add((CONTENT_TYPE, \"0001\"))\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n        let resp = mw.call(req).await.unwrap();\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0002\");\n    }\n\n    #[actix_rt::test]\n    async fn adding_content_type() {\n        let mw = DefaultHeaders::new()\n            .add_content_type()\n            .new_transform(test::ok_service())\n            .await\n            .unwrap();\n\n        let req = TestRequest::default().to_srv_request();\n        let resp = mw.call(req).await.unwrap();\n        assert_eq!(\n            resp.headers().get(CONTENT_TYPE).unwrap(),\n            \"application/octet-stream\"\n        );\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_header_name() {\n        DefaultHeaders::new().add((\":\", \"hello\"));\n    }\n\n    #[test]\n    #[should_panic]\n    fn invalid_header_value() {\n        DefaultHeaders::new().add((\"x-test\", \"\\n\"));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/err_handlers.rs",
    "content": "//! For middleware documentation, see [`ErrorHandlers`].\n\nuse std::{\n    future::Future,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_service::{Service, Transform};\nuse foldhash::HashMap as FoldHashMap;\nuse futures_core::{future::LocalBoxFuture, ready};\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::EitherBody,\n    dev::{ServiceRequest, ServiceResponse},\n    http::StatusCode,\n    Error, Result,\n};\n\n/// Return type for [`ErrorHandlers`] custom handlers.\npub enum ErrorHandlerResponse<B> {\n    /// Immediate HTTP response.\n    Response(ServiceResponse<EitherBody<B>>),\n\n    /// A future that resolves to an HTTP response.\n    Future(LocalBoxFuture<'static, Result<ServiceResponse<EitherBody<B>>, Error>>),\n}\n\ntype ErrorHandler<B> = dyn Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>>;\n\ntype DefaultHandler<B> = Option<Rc<ErrorHandler<B>>>;\n\n/// Middleware for registering custom status code based error handlers.\n///\n/// Register handlers with the [`ErrorHandlers::handler()`] method to register a custom error handler\n/// for a given status code. Handlers can modify existing responses or create completely new ones.\n///\n/// To register a default handler, use the [`ErrorHandlers::default_handler()`] method. This\n/// handler will be used only if a response has an error status code (400-599) that isn't covered by\n/// a more specific handler (set with the [`handler()`][ErrorHandlers::handler] method). See examples\n/// below.\n///\n/// To register a default for only client errors (400-499) or only server errors (500-599), use the\n/// [`ErrorHandlers::default_handler_client()`] and [`ErrorHandlers::default_handler_server()`]\n/// methods, respectively.\n///\n/// Any response with a status code that isn't covered by a specific handler or a default handler\n/// will pass by unchanged by this middleware.\n///\n/// # Examples\n///\n/// Adding a header:\n///\n/// ```\n/// use actix_web::{\n///     dev::ServiceResponse,\n///     http::{header, StatusCode},\n///     middleware::{ErrorHandlerResponse, ErrorHandlers},\n///     web, App, HttpResponse, Result,\n/// };\n///\n/// fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n///     res.response_mut().headers_mut().insert(\n///         header::CONTENT_TYPE,\n///         header::HeaderValue::from_static(\"Error\"),\n///     );\n///\n///     // body is unchanged, map to \"left\" slot\n///     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n/// }\n///\n/// let app = App::new()\n///     .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header))\n///     .service(web::resource(\"/\").route(web::get().to(HttpResponse::InternalServerError)));\n/// ```\n///\n/// Modifying response body:\n///\n/// ```\n/// use actix_web::{\n///     dev::ServiceResponse,\n///     http::{header, StatusCode},\n///     middleware::{ErrorHandlerResponse, ErrorHandlers},\n///     web, App, HttpResponse, Result,\n/// };\n///\n/// fn add_error_body<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n///     // split service response into request and response components\n///     let (req, res) = res.into_parts();\n///\n///     // set body of response to modified body\n///     let res = res.set_body(\"An error occurred.\");\n///\n///     // modified bodies need to be boxed and placed in the \"right\" slot\n///     let res = ServiceResponse::new(req, res)\n///         .map_into_boxed_body()\n///         .map_into_right_body();\n///\n///     Ok(ErrorHandlerResponse::Response(res))\n/// }\n///\n/// let app = App::new()\n///     .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_body))\n///     .service(web::resource(\"/\").route(web::get().to(HttpResponse::InternalServerError)));\n/// ```\n///\n/// Registering default handler:\n///\n/// ```\n/// # use actix_web::{\n/// #     dev::ServiceResponse,\n/// #     http::{header, StatusCode},\n/// #     middleware::{ErrorHandlerResponse, ErrorHandlers},\n/// #     web, App, HttpResponse, Result,\n/// # };\n/// fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n///     res.response_mut().headers_mut().insert(\n///         header::CONTENT_TYPE,\n///         header::HeaderValue::from_static(\"Error\"),\n///     );\n///\n///     // body is unchanged, map to \"left\" slot\n///     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n/// }\n///\n/// fn handle_bad_request<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n///     res.response_mut().headers_mut().insert(\n///         header::CONTENT_TYPE,\n///         header::HeaderValue::from_static(\"Bad Request Error\"),\n///     );\n///\n///     // body is unchanged, map to \"left\" slot\n///     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n/// }\n///\n/// // Bad Request errors will hit `handle_bad_request()`, while all other errors will hit\n/// // `add_error_header()`. The order in which the methods are called is not meaningful.\n/// let app = App::new()\n///     .wrap(\n///         ErrorHandlers::new()\n///             .default_handler(add_error_header)\n///             .handler(StatusCode::BAD_REQUEST, handle_bad_request)\n///     )\n///     .service(web::resource(\"/\").route(web::get().to(HttpResponse::InternalServerError)));\n/// ```\n///\n/// You can set default handlers for all client (4xx) or all server (5xx) errors:\n///\n/// ```\n/// # use actix_web::{\n/// #     dev::ServiceResponse,\n/// #     http::{header, StatusCode},\n/// #     middleware::{ErrorHandlerResponse, ErrorHandlers},\n/// #     web, App, HttpResponse, Result,\n/// # };\n/// # fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n/// #     res.response_mut().headers_mut().insert(\n/// #         header::CONTENT_TYPE,\n/// #         header::HeaderValue::from_static(\"Error\"),\n/// #     );\n/// #     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n/// # }\n/// # fn handle_bad_request<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n/// #     res.response_mut().headers_mut().insert(\n/// #         header::CONTENT_TYPE,\n/// #         header::HeaderValue::from_static(\"Bad Request Error\"),\n/// #     );\n/// #     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n/// # }\n/// // Bad request errors will hit `handle_bad_request()`, other client errors will hit\n/// // `add_error_header()`, and server errors will pass through unchanged\n/// let app = App::new()\n///     .wrap(\n///         ErrorHandlers::new()\n///             .default_handler_client(add_error_header) // or .default_handler_server\n///             .handler(StatusCode::BAD_REQUEST, handle_bad_request)\n///     )\n///     .service(web::resource(\"/\").route(web::get().to(HttpResponse::InternalServerError)));\n/// ```\npub struct ErrorHandlers<B> {\n    default_client: DefaultHandler<B>,\n    default_server: DefaultHandler<B>,\n    handlers: Handlers<B>,\n}\n\ntype Handlers<B> = Rc<FoldHashMap<StatusCode, Box<ErrorHandler<B>>>>;\n\nimpl<B> Default for ErrorHandlers<B> {\n    fn default() -> Self {\n        ErrorHandlers {\n            default_client: Default::default(),\n            default_server: Default::default(),\n            handlers: Default::default(),\n        }\n    }\n}\n\nimpl<B> ErrorHandlers<B> {\n    /// Construct new `ErrorHandlers` instance.\n    pub fn new() -> Self {\n        ErrorHandlers::default()\n    }\n\n    /// Register error handler for specified status code.\n    pub fn handler<F>(mut self, status: StatusCode, handler: F) -> Self\n    where\n        F: Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> + 'static,\n    {\n        Rc::get_mut(&mut self.handlers)\n            .unwrap()\n            .insert(status, Box::new(handler));\n        self\n    }\n\n    /// Register a default error handler.\n    ///\n    /// Any request with a status code that hasn't been given a specific other handler (by calling\n    /// [`.handler()`][ErrorHandlers::handler]) will fall back on this.\n    ///\n    /// Note that this will overwrite any default handlers previously set by calling\n    /// [`default_handler_client()`] or [`.default_handler_server()`], but not any set by calling\n    /// [`.handler()`].\n    ///\n    /// [`default_handler_client()`]: ErrorHandlers::default_handler_client\n    /// [`.default_handler_server()`]: ErrorHandlers::default_handler_server\n    /// [`.handler()`]: ErrorHandlers::handler\n    pub fn default_handler<F>(self, handler: F) -> Self\n    where\n        F: Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> + 'static,\n    {\n        let handler = Rc::new(handler);\n        let handler2 = Rc::clone(&handler);\n        Self {\n            default_server: Some(handler2),\n            default_client: Some(handler),\n            ..self\n        }\n    }\n\n    /// Register a handler on which to fall back for client error status codes (400-499).\n    pub fn default_handler_client<F>(self, handler: F) -> Self\n    where\n        F: Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> + 'static,\n    {\n        Self {\n            default_client: Some(Rc::new(handler)),\n            ..self\n        }\n    }\n\n    /// Register a handler on which to fall back for server error status codes (500-599).\n    pub fn default_handler_server<F>(self, handler: F) -> Self\n    where\n        F: Fn(ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> + 'static,\n    {\n        Self {\n            default_server: Some(Rc::new(handler)),\n            ..self\n        }\n    }\n\n    /// Selects the most appropriate handler for the given status code.\n    ///\n    /// If the `handlers` map has an entry for that status code, that handler is returned.\n    /// Otherwise, fall back on the appropriate default handler.\n    fn get_handler<'a>(\n        status: &StatusCode,\n        default_client: Option<&'a ErrorHandler<B>>,\n        default_server: Option<&'a ErrorHandler<B>>,\n        handlers: &'a Handlers<B>,\n    ) -> Option<&'a ErrorHandler<B>> {\n        handlers\n            .get(status)\n            .map(|h| h.as_ref())\n            .or_else(|| status.is_client_error().then_some(default_client).flatten())\n            .or_else(|| status.is_server_error().then_some(default_server).flatten())\n    }\n}\n\nimpl<S, B> Transform<S, ServiceRequest> for ErrorHandlers<B>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<EitherBody<B>>;\n    type Error = Error;\n    type Transform = ErrorHandlersMiddleware<S, B>;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        let handlers = Rc::clone(&self.handlers);\n        let default_client = self.default_client.clone();\n        let default_server = self.default_server.clone();\n        Box::pin(async move {\n            Ok(ErrorHandlersMiddleware {\n                service,\n                default_client,\n                default_server,\n                handlers,\n            })\n        })\n    }\n}\n\n#[doc(hidden)]\npub struct ErrorHandlersMiddleware<S, B> {\n    service: S,\n    default_client: DefaultHandler<B>,\n    default_server: DefaultHandler<B>,\n    handlers: Handlers<B>,\n}\n\nimpl<S, B> Service<ServiceRequest> for ErrorHandlersMiddleware<S, B>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<EitherBody<B>>;\n    type Error = Error;\n    type Future = ErrorHandlersFuture<S::Future, B>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let handlers = Rc::clone(&self.handlers);\n        let default_client = self.default_client.clone();\n        let default_server = self.default_server.clone();\n        let fut = self.service.call(req);\n        ErrorHandlersFuture::ServiceFuture {\n            fut,\n            default_client,\n            default_server,\n            handlers,\n        }\n    }\n}\n\npin_project! {\n    #[project = ErrorHandlersProj]\n    pub enum ErrorHandlersFuture<Fut, B>\n    where\n        Fut: Future,\n    {\n        ServiceFuture {\n            #[pin]\n            fut: Fut,\n            default_client: DefaultHandler<B>,\n            default_server: DefaultHandler<B>,\n            handlers: Handlers<B>,\n        },\n        ErrorHandlerFuture {\n            fut: LocalBoxFuture<'static, Result<ServiceResponse<EitherBody<B>>, Error>>,\n        },\n    }\n}\n\nimpl<Fut, B> Future for ErrorHandlersFuture<Fut, B>\nwhere\n    Fut: Future<Output = Result<ServiceResponse<B>, Error>>,\n{\n    type Output = Result<ServiceResponse<EitherBody<B>>, Error>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.as_mut().project() {\n            ErrorHandlersProj::ServiceFuture {\n                fut,\n                default_client,\n                default_server,\n                handlers,\n            } => {\n                let res = ready!(fut.poll(cx))?;\n                let status = res.status();\n\n                let handler = ErrorHandlers::get_handler(\n                    &status,\n                    default_client.as_mut().map(|f| Rc::as_ref(f)),\n                    default_server.as_mut().map(|f| Rc::as_ref(f)),\n                    handlers,\n                );\n                match handler {\n                    Some(handler) => match handler(res)? {\n                        ErrorHandlerResponse::Response(res) => Poll::Ready(Ok(res)),\n                        ErrorHandlerResponse::Future(fut) => {\n                            self.as_mut()\n                                .set(ErrorHandlersFuture::ErrorHandlerFuture { fut });\n\n                            self.poll(cx)\n                        }\n                    },\n                    None => Poll::Ready(Ok(res.map_into_left_body())),\n                }\n            }\n\n            ErrorHandlersProj::ErrorHandlerFuture { fut } => fut.as_mut().poll(cx),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::IntoService;\n    use actix_utils::future::ok;\n    use bytes::Bytes;\n    use futures_util::FutureExt as _;\n\n    use super::*;\n    use crate::{\n        body,\n        http::header::{HeaderValue, CONTENT_TYPE},\n        test::{self, TestRequest},\n    };\n\n    #[actix_rt::test]\n    async fn add_header_error_handler() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);\n\n        let mw = ErrorHandlers::new()\n            .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let resp = test::call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n    }\n\n    #[actix_rt::test]\n    async fn add_header_error_handler_async() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler<B: 'static>(\n            mut res: ServiceResponse<B>,\n        ) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n\n            Ok(ErrorHandlerResponse::Future(\n                ok(res.map_into_left_body()).boxed_local(),\n            ))\n        }\n\n        let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);\n\n        let mw = ErrorHandlers::new()\n            .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let resp = test::call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n    }\n\n    #[actix_rt::test]\n    async fn changes_body_type() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            let (req, res) = res.into_parts();\n            let res = res.set_body(Bytes::from(\"sorry, that's no bueno\"));\n\n            let res = ServiceResponse::new(req, res)\n                .map_into_boxed_body()\n                .map_into_right_body();\n\n            Ok(ErrorHandlerResponse::Response(res))\n        }\n\n        let srv = test::status_service(StatusCode::INTERNAL_SERVER_ERROR);\n\n        let mw = ErrorHandlers::new()\n            .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let res = test::call_service(&mw, TestRequest::default().to_srv_request()).await;\n        assert_eq!(test::read_body(res).await, \"sorry, that's no bueno\");\n    }\n\n    #[actix_rt::test]\n    async fn error_thrown() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler<B>(_res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            Err(crate::error::ErrorInternalServerError(\n                \"error in error handler\",\n            ))\n        }\n\n        let srv = test::status_service(StatusCode::BAD_REQUEST);\n\n        let mw = ErrorHandlers::new()\n            .handler(StatusCode::BAD_REQUEST, error_handler)\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let err = mw\n            .call(TestRequest::default().to_srv_request())\n            .await\n            .unwrap_err();\n        let res = err.error_response();\n\n        assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n        assert_eq!(\n            body::to_bytes(res.into_body()).await.unwrap(),\n            \"error in error handler\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn default_error_handler() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        let make_mw = |status| async move {\n            ErrorHandlers::new()\n                .default_handler(error_handler)\n                .new_transform(test::status_service(status).into_service())\n                .await\n                .unwrap()\n        };\n        let mw_server = make_mw(StatusCode::INTERNAL_SERVER_ERROR).await;\n        let mw_client = make_mw(StatusCode::BAD_REQUEST).await;\n\n        let resp = test::call_service(&mw_client, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n\n        let resp = test::call_service(&mw_server, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n    }\n\n    #[actix_rt::test]\n    async fn default_handlers_separate_client_server() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler_client<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler_server<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0002\"));\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        let make_mw = |status| async move {\n            ErrorHandlers::new()\n                .default_handler_server(error_handler_server)\n                .default_handler_client(error_handler_client)\n                .new_transform(test::status_service(status).into_service())\n                .await\n                .unwrap()\n        };\n        let mw_server = make_mw(StatusCode::INTERNAL_SERVER_ERROR).await;\n        let mw_client = make_mw(StatusCode::BAD_REQUEST).await;\n\n        let resp = test::call_service(&mw_client, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n\n        let resp = test::call_service(&mw_server, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0002\");\n    }\n\n    #[actix_rt::test]\n    async fn default_handlers_specialization() {\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler_client<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        #[allow(clippy::unnecessary_wraps)]\n        fn error_handler_specific<B>(\n            mut res: ServiceResponse<B>,\n        ) -> Result<ErrorHandlerResponse<B>> {\n            res.response_mut()\n                .headers_mut()\n                .insert(CONTENT_TYPE, HeaderValue::from_static(\"0003\"));\n            Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n        }\n\n        let make_mw = |status| async move {\n            ErrorHandlers::new()\n                .default_handler_client(error_handler_client)\n                .handler(StatusCode::UNPROCESSABLE_ENTITY, error_handler_specific)\n                .new_transform(test::status_service(status).into_service())\n                .await\n                .unwrap()\n        };\n        let mw_client = make_mw(StatusCode::BAD_REQUEST).await;\n        let mw_specific = make_mw(StatusCode::UNPROCESSABLE_ENTITY).await;\n\n        let resp = test::call_service(&mw_client, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0001\");\n\n        let resp = test::call_service(&mw_specific, TestRequest::default().to_srv_request()).await;\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"0003\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/from_fn.rs",
    "content": "use std::{future::Future, marker::PhantomData, rc::Rc};\n\nuse actix_service::boxed::{self, BoxFuture, RcService};\nuse actix_utils::future::{ready, Ready};\nuse futures_core::future::LocalBoxFuture;\n\nuse crate::{\n    body::MessageBody,\n    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},\n    Error, FromRequest,\n};\n\n/// Wraps an async function to be used as a middleware.\n///\n/// # Examples\n///\n/// The wrapped function should have the following form:\n///\n/// ```\n/// # use actix_web::{\n/// #     App, Error,\n/// #     body::MessageBody,\n/// #     dev::{ServiceRequest, ServiceResponse, Service as _},\n/// # };\n/// use actix_web::middleware::{self, Next};\n///\n/// async fn my_mw(\n///     req: ServiceRequest,\n///     next: Next<impl MessageBody>,\n/// ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n///     // pre-processing\n///     next.call(req).await\n///     // post-processing\n/// }\n/// # App::new().wrap(middleware::from_fn(my_mw));\n/// ```\n///\n/// Then use in an app builder like this:\n///\n/// ```\n/// use actix_web::{\n///     App, Error,\n///     dev::{ServiceRequest, ServiceResponse, Service as _},\n/// };\n/// use actix_web::middleware::from_fn;\n/// # use actix_web::middleware::Next;\n/// # async fn my_mw<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {\n/// #     next.call(req).await\n/// # }\n///\n/// App::new()\n///     .wrap(from_fn(my_mw))\n/// # ;\n/// ```\n///\n/// It is also possible to write a middleware that automatically uses extractors, similar to request\n/// handlers, by declaring them as the first parameters. As usual, **take care with extractors that\n/// consume the body stream**, since handlers will no longer be able to read it again without\n/// putting the body \"back\" into the request object within your middleware.\n///\n/// ```\n/// # use std::collections::HashMap;\n/// # use actix_web::{\n/// #     App, Error,\n/// #     body::MessageBody,\n/// #     dev::{ServiceRequest, ServiceResponse},\n/// #     http::header::{Accept, Date},\n/// #     web::{Header, Query},\n/// # };\n/// use actix_web::middleware::Next;\n///\n/// async fn my_extracting_mw(\n///     accept: Header<Accept>,\n///     query: Query<HashMap<String, String>>,\n///     req: ServiceRequest,\n///     next: Next<impl MessageBody>,\n/// ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n///     // pre-processing\n///     next.call(req).await\n///     // post-processing\n/// }\n/// # App::new().wrap(actix_web::middleware::from_fn(my_extracting_mw));\npub fn from_fn<F, Es>(mw_fn: F) -> MiddlewareFn<F, Es> {\n    MiddlewareFn {\n        mw_fn: Rc::new(mw_fn),\n        _phantom: PhantomData,\n    }\n}\n\n/// Middleware transform for [`from_fn`].\n#[allow(missing_debug_implementations)]\npub struct MiddlewareFn<F, Es> {\n    mw_fn: Rc<F>,\n    _phantom: PhantomData<Es>,\n}\n\nimpl<S, F, Fut, B, B2> Transform<S, ServiceRequest> for MiddlewareFn<F, ()>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n    F: Fn(ServiceRequest, Next<B>) -> Fut + 'static,\n    Fut: Future<Output = Result<ServiceResponse<B2>, Error>>,\n    B2: MessageBody,\n{\n    type Response = ServiceResponse<B2>;\n    type Error = Error;\n    type Transform = MiddlewareFnService<F, B, ()>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ready(Ok(MiddlewareFnService {\n            service: boxed::rc_service(service),\n            mw_fn: Rc::clone(&self.mw_fn),\n            _phantom: PhantomData,\n        }))\n    }\n}\n\n/// Middleware service for [`from_fn`].\n#[allow(missing_debug_implementations)]\npub struct MiddlewareFnService<F, B, Es> {\n    service: RcService<ServiceRequest, ServiceResponse<B>, Error>,\n    mw_fn: Rc<F>,\n    _phantom: PhantomData<(B, Es)>,\n}\n\nimpl<F, Fut, B, B2> Service<ServiceRequest> for MiddlewareFnService<F, B, ()>\nwhere\n    F: Fn(ServiceRequest, Next<B>) -> Fut,\n    Fut: Future<Output = Result<ServiceResponse<B2>, Error>>,\n    B2: MessageBody,\n{\n    type Response = ServiceResponse<B2>;\n    type Error = Error;\n    type Future = Fut;\n\n    forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        (self.mw_fn)(\n            req,\n            Next::<B> {\n                service: Rc::clone(&self.service),\n            },\n        )\n    }\n}\n\nmacro_rules! impl_middleware_fn_service {\n    ($($ext_type:ident),*) => {\n        impl<S, F, Fut, B, B2, $($ext_type),*> Transform<S, ServiceRequest> for MiddlewareFn<F, ($($ext_type),*,)>\n        where\n            S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n            F: Fn($($ext_type),*, ServiceRequest, Next<B>) -> Fut + 'static,\n            $($ext_type: FromRequest + 'static,)*\n            Fut: Future<Output = Result<ServiceResponse<B2>, Error>> + 'static,\n            B: MessageBody + 'static,\n            B2: MessageBody + 'static,\n        {\n            type Response = ServiceResponse<B2>;\n            type Error = Error;\n            type Transform = MiddlewareFnService<F, B, ($($ext_type,)*)>;\n            type InitError = ();\n            type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n            fn new_transform(&self, service: S) -> Self::Future {\n                ready(Ok(MiddlewareFnService {\n                    service: boxed::rc_service(service),\n                    mw_fn: Rc::clone(&self.mw_fn),\n                    _phantom: PhantomData,\n                }))\n            }\n        }\n\n        impl<F, $($ext_type),*, Fut, B: 'static, B2> Service<ServiceRequest>\n            for MiddlewareFnService<F, B, ($($ext_type),*,)>\n        where\n            F: Fn(\n                $($ext_type),*,\n                ServiceRequest,\n                Next<B>\n            ) -> Fut + 'static,\n            $($ext_type: FromRequest + 'static,)*\n            Fut: Future<Output = Result<ServiceResponse<B2>, Error>> + 'static,\n            B2: MessageBody + 'static,\n        {\n            type Response = ServiceResponse<B2>;\n            type Error = Error;\n            type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n            forward_ready!(service);\n\n            #[allow(nonstandard_style)]\n            fn call(&self, mut req: ServiceRequest) -> Self::Future {\n                let mw_fn = Rc::clone(&self.mw_fn);\n                let service = Rc::clone(&self.service);\n\n                Box::pin(async move {\n                    let ($($ext_type,)*) = req.extract::<($($ext_type,)*)>().await?;\n\n                    (mw_fn)($($ext_type),*, req, Next::<B> { service }).await\n                })\n            }\n        }\n    };\n}\n\nimpl_middleware_fn_service!(E1);\nimpl_middleware_fn_service!(E1, E2);\nimpl_middleware_fn_service!(E1, E2, E3);\nimpl_middleware_fn_service!(E1, E2, E3, E4);\nimpl_middleware_fn_service!(E1, E2, E3, E4, E5);\nimpl_middleware_fn_service!(E1, E2, E3, E4, E5, E6);\nimpl_middleware_fn_service!(E1, E2, E3, E4, E5, E6, E7);\nimpl_middleware_fn_service!(E1, E2, E3, E4, E5, E6, E7, E8);\nimpl_middleware_fn_service!(E1, E2, E3, E4, E5, E6, E7, E8, E9);\n\n/// Wraps the \"next\" service in the middleware chain.\n#[allow(missing_debug_implementations)]\npub struct Next<B> {\n    service: RcService<ServiceRequest, ServiceResponse<B>, Error>,\n}\n\nimpl<B> Next<B> {\n    /// Equivalent to `Service::call(self, req)`.\n    pub fn call(&self, req: ServiceRequest) -> <Self as Service<ServiceRequest>>::Future {\n        Service::call(self, req)\n    }\n}\n\nimpl<B> Service<ServiceRequest> for Next<B> {\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Future = BoxFuture<Result<Self::Response, Self::Error>>;\n\n    forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        self.service.call(req)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        http::header::{self, HeaderValue},\n        middleware::{Compat, Logger},\n        test, web, App, HttpResponse,\n    };\n\n    async fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {\n        next.call(req).await\n    }\n\n    async fn add_res_header<B>(\n        req: ServiceRequest,\n        next: Next<B>,\n    ) -> Result<ServiceResponse<B>, Error> {\n        let mut res = next.call(req).await?;\n        res.headers_mut()\n            .insert(header::WARNING, HeaderValue::from_static(\"42\"));\n        Ok(res)\n    }\n\n    async fn mutate_body_type(\n        req: ServiceRequest,\n        next: Next<impl MessageBody + 'static>,\n    ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n        let res = next.call(req).await?;\n        Ok(res.map_into_left_body::<()>())\n    }\n\n    struct MyMw(bool);\n\n    impl MyMw {\n        async fn mw_cb(\n            &self,\n            req: ServiceRequest,\n            next: Next<impl MessageBody + 'static>,\n        ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n            let mut res = match self.0 {\n                true => req.into_response(\"short-circuited\").map_into_right_body(),\n                false => next.call(req).await?.map_into_left_body(),\n            };\n            res.headers_mut()\n                .insert(header::WARNING, HeaderValue::from_static(\"42\"));\n            Ok(res)\n        }\n\n        pub fn into_middleware<S, B>(\n            self,\n        ) -> impl Transform<\n            S,\n            ServiceRequest,\n            Response = ServiceResponse<impl MessageBody>,\n            Error = Error,\n            InitError = (),\n        >\n        where\n            S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,\n            B: MessageBody + 'static,\n        {\n            let this = Rc::new(self);\n            from_fn(move |req, next| {\n                let this = Rc::clone(&this);\n                async move { Self::mw_cb(&this, req, next).await }\n            })\n        }\n    }\n\n    #[actix_rt::test]\n    async fn compat_compat() {\n        let _ = App::new().wrap(Compat::new(from_fn(noop)));\n        let _ = App::new().wrap(Compat::new(from_fn(mutate_body_type)));\n    }\n\n    #[actix_rt::test]\n    async fn permits_different_in_and_out_body_types() {\n        let app = test::init_service(\n            App::new()\n                .wrap(from_fn(mutate_body_type))\n                .wrap(from_fn(add_res_header))\n                .wrap(Logger::default())\n                .wrap(from_fn(noop))\n                .default_service(web::to(HttpResponse::NotFound)),\n        )\n        .await;\n\n        let req = test::TestRequest::default().to_request();\n        let res = test::call_service(&app, req).await;\n        assert!(res.headers().contains_key(header::WARNING));\n    }\n\n    #[actix_rt::test]\n    async fn closure_capture_and_return_from_fn() {\n        let app = test::init_service(\n            App::new()\n                .wrap(Logger::default())\n                .wrap(MyMw(true).into_middleware())\n                .wrap(Logger::default()),\n        )\n        .await;\n\n        let req = test::TestRequest::default().to_request();\n        let res = test::call_service(&app, req).await;\n        assert!(res.headers().contains_key(header::WARNING));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/identity.rs",
    "content": "//! A no-op middleware. See [Noop] for docs.\n\nuse actix_utils::future::{ready, Ready};\n\nuse crate::dev::{forward_ready, Service, Transform};\n\n/// A no-op middleware that passes through request and response untouched.\n#[derive(Debug, Clone, Default)]\n#[non_exhaustive]\npub struct Identity;\n\nimpl<S: Service<Req>, Req> Transform<S, Req> for Identity {\n    type Response = S::Response;\n    type Error = S::Error;\n    type Transform = IdentityMiddleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    #[inline]\n    fn new_transform(&self, service: S) -> Self::Future {\n        ready(Ok(IdentityMiddleware { service }))\n    }\n}\n\n#[doc(hidden)]\npub struct IdentityMiddleware<S> {\n    service: S,\n}\n\nimpl<S: Service<Req>, Req> Service<Req> for IdentityMiddleware<S> {\n    type Response = S::Response;\n    type Error = S::Error;\n    type Future = S::Future;\n\n    forward_ready!(service);\n\n    #[inline]\n    fn call(&self, req: Req) -> Self::Future {\n        self.service.call(req)\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/logger.rs",
    "content": "//! For middleware documentation, see [`Logger`].\n\nuse std::{\n    borrow::Cow,\n    collections::HashSet,\n    env,\n    fmt::{self, Display as _},\n    future::Future,\n    marker::PhantomData,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_service::{Service, Transform};\nuse actix_utils::future::{ready, Ready};\nuse bytes::Bytes;\nuse futures_core::ready;\nuse log::{debug, warn, Level};\nuse pin_project_lite::pin_project;\n#[cfg(feature = \"unicode\")]\nuse regex::Regex;\n#[cfg(not(feature = \"unicode\"))]\nuse regex_lite::Regex;\nuse time::{format_description::well_known::Rfc3339, OffsetDateTime};\n\nuse crate::{\n    body::{BodySize, MessageBody},\n    http::header::HeaderName,\n    service::{ServiceRequest, ServiceResponse},\n    Error, Result,\n};\n\n/// Middleware for logging request and response summaries to the terminal.\n///\n/// This middleware uses the `log` crate to output information. Enable `log`'s output for the\n/// \"actix_web\" scope using [`env_logger`](https://docs.rs/env_logger) or similar crate.\n///\n/// # Default Format\n/// The [`default`](Logger::default) Logger uses the following format:\n///\n/// ```plain\n/// %a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T\n///\n/// Example Output:\n/// 127.0.0.1:54278 \"GET /test HTTP/1.1\" 404 20 \"-\" \"HTTPie/2.2.0\" 0.001074\n/// ```\n///\n/// # Examples\n/// ```\n/// use actix_web::{middleware::Logger, App};\n///\n/// // access logs are printed with the INFO level so ensure it is enabled by default\n/// env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n///\n/// let app = App::new()\n///     // .wrap(Logger::default())\n///     .wrap(Logger::new(\"%a %{User-Agent}i\"));\n/// ```\n///\n/// # Format\n/// Variable | Description\n/// -------- | -----------\n/// `%%` | The percent sign\n/// `%a` | Peer IP address (or IP address of reverse proxy if used)\n/// `%t` | Time when the request started processing (in RFC 3339 format)\n/// `%r` | First line of request (Example: `GET /test HTTP/1.1`)\n/// `%s` | Response status code\n/// `%b` | Size of response in bytes, including HTTP headers\n/// `%T` | Time taken to serve the request, in seconds to 6 decimal places\n/// `%D` | Time taken to serve the request, in milliseconds\n/// `%U` | Request URL\n/// `%{r}a` | \"Real IP\" remote address **\\***\n/// `%{FOO}i` | `request.headers[\"FOO\"]`\n/// `%{FOO}o` | `response.headers[\"FOO\"]`\n/// `%{FOO}e` | `env_var[\"FOO\"]`\n/// `%{FOO}xi` | [Custom request replacement](Logger::custom_request_replace) labelled \"FOO\"\n/// `%{FOO}xo` | [Custom response replacement](Logger::custom_response_replace) labelled \"FOO\"\n///\n/// # Security\n/// **\\*** \"Real IP\" remote address is calculated using\n/// [`ConnectionInfo::realip_remote_addr()`](crate::dev::ConnectionInfo::realip_remote_addr())\n///\n/// If you use this value, ensure that all requests come from trusted hosts. Otherwise, it is\n/// trivial for the remote client to falsify their source IP address.\n#[derive(Debug)]\npub struct Logger(Rc<Inner>);\n\n#[derive(Debug, Clone)]\nstruct Inner {\n    format: Format,\n    exclude: HashSet<String>,\n    exclude_regex: Vec<Regex>,\n    log_target: Cow<'static, str>,\n    log_level: Level,\n}\n\nimpl Logger {\n    /// Create `Logger` middleware with the specified `format`.\n    pub fn new(format: &str) -> Logger {\n        Logger(Rc::new(Inner {\n            format: Format::new(format),\n            exclude: HashSet::new(),\n            exclude_regex: Vec::new(),\n            log_target: Cow::Borrowed(module_path!()),\n            log_level: Level::Info,\n        }))\n    }\n\n    /// Ignore and do not log access info for specified path.\n    pub fn exclude<T: Into<String>>(mut self, path: T) -> Self {\n        Rc::get_mut(&mut self.0)\n            .unwrap()\n            .exclude\n            .insert(path.into());\n        self\n    }\n\n    /// Ignore and do not log access info for paths that match regex.\n    pub fn exclude_regex<T: Into<String>>(mut self, path: T) -> Self {\n        let inner = Rc::get_mut(&mut self.0).unwrap();\n        inner.exclude_regex.push(Regex::new(&path.into()).unwrap());\n        self\n    }\n\n    /// Sets the logging target to `target`.\n    ///\n    /// By default, the log target is `module_path!()` of the log call location. In our case, that\n    /// would be `actix_web::middleware::logger`.\n    ///\n    /// # Examples\n    /// Using `.log_target(\"http_log\")` would have this effect on request logs:\n    /// ```diff\n    /// - [2015-10-21T07:28:00Z INFO  actix_web::middleware::logger] 127.0.0.1 \"GET / HTTP/1.1\" 200 88 \"-\" \"dmc/1.0\" 0.001985\n    /// + [2015-10-21T07:28:00Z INFO  http_log] 127.0.0.1 \"GET / HTTP/1.1\" 200 88 \"-\" \"dmc/1.0\" 0.001985\n    ///                               ^^^^^^^^\n    /// ```\n    pub fn log_target(mut self, target: impl Into<Cow<'static, str>>) -> Self {\n        let inner = Rc::get_mut(&mut self.0).unwrap();\n        inner.log_target = target.into();\n        self\n    }\n\n    /// Sets the log level to `level`.\n    ///\n    /// By default, the log level is `Level::Info`.\n    ///\n    /// # Examples\n    /// Using `.log_level(Level::Debug)` would have this effect on request logs:\n    /// ```diff\n    /// - [2015-10-21T07:28:00Z INFO  actix_web::middleware::logger] 127.0.0.1 \"GET / HTTP/1.1\" 200 88 \"-\" \"dmc/1.0\" 0.001985\n    /// + [2015-10-21T07:28:00Z DEBUG  actix_web::middleware::logger] 127.0.0.1 \"GET / HTTP/1.1\" 200 88 \"-\" \"dmc/1.0\" 0.001985\n    ///                         ^^^^^^\n    /// ```\n    pub fn log_level(mut self, level: log::Level) -> Self {\n        let inner = Rc::get_mut(&mut self.0).unwrap();\n        inner.log_level = level;\n        self\n    }\n\n    /// Register a function that receives a ServiceRequest and returns a String for use in the\n    /// log line. The label passed as the first argument should match a replacement substring in\n    /// the logger format like `%{label}xi`.\n    ///\n    /// It is convention to print \"-\" to indicate no output instead of an empty string.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::http::{header::HeaderValue};\n    /// # use actix_web::middleware::Logger;\n    /// # fn parse_jwt_id (_req: Option<&HeaderValue>) -> String { \"jwt_uid\".to_owned() }\n    /// Logger::new(\"example %{JWT_ID}xi\")\n    ///     .custom_request_replace(\"JWT_ID\", |req| parse_jwt_id(req.headers().get(\"Authorization\")));\n    /// ```\n    pub fn custom_request_replace(\n        mut self,\n        label: &str,\n        f: impl Fn(&ServiceRequest) -> String + 'static,\n    ) -> Self {\n        let inner = Rc::get_mut(&mut self.0).unwrap();\n\n        let ft = inner.format.0.iter_mut().find(\n            |ft| matches!(ft, FormatText::CustomRequest(unit_label, _) if label == unit_label),\n        );\n\n        if let Some(FormatText::CustomRequest(_, request_fn)) = ft {\n            // replace into None or previously registered fn using same label\n            request_fn.replace(CustomRequestFn {\n                inner_fn: Rc::new(f),\n            });\n        } else {\n            // non-printed request replacement function diagnostic\n            debug!(\n                \"Attempted to register custom request logging function for nonexistent label: {}\",\n                label\n            );\n        }\n\n        self\n    }\n\n    /// Register a function that receives a `ServiceResponse` and returns a string for use in the\n    /// log line.\n    ///\n    /// The label passed as the first argument should match a replacement substring in\n    /// the logger format like `%{label}xo`.\n    ///\n    /// It is convention to print \"-\" to indicate no output instead of an empty string.\n    ///\n    /// The replacement function does not have access to the response body.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::{dev::ServiceResponse, middleware::Logger};\n    /// fn log_if_error(res: &ServiceResponse) -> String {\n    ///     if res.status().as_u16() >= 400 {\n    ///         \"ERROR\".to_string()\n    ///     } else {\n    ///         \"-\".to_string()\n    ///     }\n    /// }\n    ///\n    /// Logger::new(\"example %{ERROR_STATUS}xo\")\n    ///     .custom_response_replace(\"ERROR_STATUS\", |res| log_if_error(res) );\n    /// ```\n    pub fn custom_response_replace(\n        mut self,\n        label: &str,\n        f: impl Fn(&ServiceResponse) -> String + 'static,\n    ) -> Self {\n        let inner = Rc::get_mut(&mut self.0).unwrap();\n\n        let ft = inner.format.0.iter_mut().find(\n            |ft| matches!(ft, FormatText::CustomResponse(unit_label, _) if label == unit_label),\n        );\n\n        if let Some(FormatText::CustomResponse(_, res_fn)) = ft {\n            *res_fn = Some(CustomResponseFn {\n                inner_fn: Rc::new(f),\n            });\n        } else {\n            debug!(\n                \"Attempted to register custom response logging function for non-existent label: {}\",\n                label\n            );\n        }\n\n        self\n    }\n}\n\nimpl Default for Logger {\n    /// Create `Logger` middleware with format:\n    ///\n    /// ```plain\n    /// %a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T\n    /// ```\n    fn default() -> Logger {\n        Logger(Rc::new(Inner {\n            format: Format::default(),\n            exclude: HashSet::new(),\n            exclude_regex: Vec::new(),\n            log_target: Cow::Borrowed(module_path!()),\n            log_level: Level::Info,\n        }))\n    }\n}\n\nimpl<S, B> Transform<S, ServiceRequest> for Logger\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n{\n    type Response = ServiceResponse<StreamLog<B>>;\n    type Error = Error;\n    type Transform = LoggerMiddleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        for unit in &self.0.format.0 {\n            if let FormatText::CustomRequest(label, None) = unit {\n                warn!(\n                    \"No custom request replacement function was registered for label: {}\",\n                    label\n                );\n            }\n\n            if let FormatText::CustomResponse(label, None) = unit {\n                warn!(\n                    \"No custom response replacement function was registered for label: {}\",\n                    label\n                );\n            }\n        }\n\n        ready(Ok(LoggerMiddleware {\n            service,\n            inner: Rc::clone(&self.0),\n        }))\n    }\n}\n\n/// Logger middleware service.\npub struct LoggerMiddleware<S> {\n    inner: Rc<Inner>,\n    service: S,\n}\n\nimpl<S, B> Service<ServiceRequest> for LoggerMiddleware<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n{\n    type Response = ServiceResponse<StreamLog<B>>;\n    type Error = Error;\n    type Future = LoggerResponse<S, B>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let excluded = self.inner.exclude.contains(req.path())\n            || self\n                .inner\n                .exclude_regex\n                .iter()\n                .any(|r| r.is_match(req.path()));\n\n        if excluded {\n            LoggerResponse {\n                fut: self.service.call(req),\n                format: None,\n                time: OffsetDateTime::now_utc(),\n                log_target: Cow::Borrowed(\"\"),\n                log_level: self.inner.log_level,\n                _phantom: PhantomData,\n            }\n        } else {\n            let now = OffsetDateTime::now_utc();\n            let mut format = self.inner.format.clone();\n\n            for unit in &mut format.0 {\n                unit.render_request(now, &req);\n            }\n\n            LoggerResponse {\n                fut: self.service.call(req),\n                format: Some(format),\n                time: now,\n                log_target: self.inner.log_target.clone(),\n                log_level: self.inner.log_level,\n                _phantom: PhantomData,\n            }\n        }\n    }\n}\n\npin_project! {\n    pub struct LoggerResponse<S, B>\n    where\n        B: MessageBody,\n        S: Service<ServiceRequest>,\n    {\n        #[pin]\n        fut: S::Future,\n        time: OffsetDateTime,\n        format: Option<Format>,\n        log_target: Cow<'static, str>,\n        log_level: Level,\n        _phantom: PhantomData<B>,\n    }\n}\n\nimpl<S, B> Future for LoggerResponse<S, B>\nwhere\n    B: MessageBody,\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n{\n    type Output = Result<ServiceResponse<StreamLog<B>>, Error>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n\n        let res = match ready!(this.fut.poll(cx)) {\n            Ok(res) => res,\n            Err(err) => return Poll::Ready(Err(err)),\n        };\n\n        if let Some(error) = res.response().error() {\n            debug!(\"Error in response: {:?}\", error);\n        }\n\n        let res = if let Some(ref mut format) = this.format {\n            // to avoid polluting all the Logger types with the body parameter we swap the body\n            // out temporarily since it's not usable in custom response functions anyway\n\n            let (req, res) = res.into_parts();\n            let (res, body) = res.into_parts();\n\n            let temp_res = ServiceResponse::new(req, res.map_into_boxed_body());\n\n            for unit in &mut format.0 {\n                unit.render_response(&temp_res);\n            }\n\n            // re-construct original service response\n            let (req, res) = temp_res.into_parts();\n            ServiceResponse::new(req, res.set_body(body))\n        } else {\n            res\n        };\n\n        let time = *this.time;\n        let format = this.format.take();\n        let log_target = this.log_target.clone();\n        let log_level = *this.log_level;\n\n        Poll::Ready(Ok(res.map_body(move |_, body| StreamLog {\n            body,\n            time,\n            format,\n            size: 0,\n            log_target,\n            log_level,\n        })))\n    }\n}\n\npin_project! {\n    pub struct StreamLog<B> {\n        #[pin]\n        body: B,\n        format: Option<Format>,\n        size: usize,\n        time: OffsetDateTime,\n        log_target: Cow<'static, str>,\n        log_level: Level\n    }\n\n    impl<B> PinnedDrop for StreamLog<B> {\n        fn drop(this: Pin<&mut Self>) {\n            if let Some(ref format) = this.format {\n                let render = |fmt: &mut fmt::Formatter<'_>| {\n                    for unit in &format.0 {\n                        unit.render(fmt, this.size, this.time)?;\n                    }\n                    Ok(())\n                };\n\n                log::log!(\n                    target: this.log_target.as_ref(),\n                    this.log_level,\n                    \"{}\", FormatDisplay(&render)\n                );\n            }\n        }\n    }\n}\n\nimpl<B: MessageBody> MessageBody for StreamLog<B> {\n    type Error = B::Error;\n\n    #[inline]\n    fn size(&self) -> BodySize {\n        self.body.size()\n    }\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        let this = self.project();\n\n        match ready!(this.body.poll_next(cx)) {\n            Some(Ok(chunk)) => {\n                *this.size += chunk.len();\n                Poll::Ready(Some(Ok(chunk)))\n            }\n            Some(Err(err)) => Poll::Ready(Some(Err(err))),\n            None => Poll::Ready(None),\n        }\n    }\n}\n\n/// A formatting style for the `Logger` consisting of multiple concatenated `FormatText` items.\n#[derive(Debug, Clone)]\nstruct Format(Vec<FormatText>);\n\nimpl Default for Format {\n    /// Return the default formatting style for the `Logger`:\n    fn default() -> Format {\n        Format::new(r#\"%a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T\"#)\n    }\n}\n\nimpl Format {\n    /// Create a `Format` from a format string.\n    ///\n    /// Returns `None` if the format string syntax is incorrect.\n    pub fn new(s: &str) -> Format {\n        log::trace!(\"Access log format: {}\", s);\n        let fmt = Regex::new(r\"%(\\{([A-Za-z0-9\\-_]+)\\}([aioe]|x[io])|[%atPrUsbTD]?)\").unwrap();\n\n        let mut idx = 0;\n        let mut results = Vec::new();\n        for cap in fmt.captures_iter(s) {\n            let m = cap.get(0).unwrap();\n            let pos = m.start();\n            if idx != pos {\n                results.push(FormatText::Str(s[idx..pos].to_owned()));\n            }\n            idx = m.end();\n\n            if let Some(key) = cap.get(2) {\n                results.push(match cap.get(3).unwrap().as_str() {\n                    \"a\" => {\n                        if key.as_str() == \"r\" {\n                            FormatText::RealIpRemoteAddr\n                        } else {\n                            unreachable!(\"regex and code mismatch\")\n                        }\n                    }\n                    \"i\" => FormatText::RequestHeader(HeaderName::try_from(key.as_str()).unwrap()),\n                    \"o\" => FormatText::ResponseHeader(HeaderName::try_from(key.as_str()).unwrap()),\n                    \"e\" => FormatText::EnvironHeader(key.as_str().to_owned()),\n                    \"xi\" => FormatText::CustomRequest(key.as_str().to_owned(), None),\n                    \"xo\" => FormatText::CustomResponse(key.as_str().to_owned(), None),\n                    _ => unreachable!(),\n                })\n            } else {\n                let m = cap.get(1).unwrap();\n                results.push(match m.as_str() {\n                    \"%\" => FormatText::Percent,\n                    \"a\" => FormatText::RemoteAddr,\n                    \"t\" => FormatText::RequestTime,\n                    \"r\" => FormatText::RequestLine,\n                    \"s\" => FormatText::ResponseStatus,\n                    \"b\" => FormatText::ResponseSize,\n                    \"U\" => FormatText::UrlPath,\n                    \"T\" => FormatText::Time,\n                    \"D\" => FormatText::TimeMillis,\n                    _ => FormatText::Str(m.as_str().to_owned()),\n                });\n            }\n        }\n        if idx != s.len() {\n            results.push(FormatText::Str(s[idx..].to_owned()));\n        }\n\n        Format(results)\n    }\n}\n\n/// A string of text to be logged.\n///\n/// This is either one of the data fields supported by the `Logger`, or a custom `String`.\n#[non_exhaustive]\n#[derive(Debug, Clone)]\nenum FormatText {\n    Str(String),\n    Percent,\n    RequestLine,\n    RequestTime,\n    ResponseStatus,\n    ResponseSize,\n    Time,\n    TimeMillis,\n    RemoteAddr,\n    RealIpRemoteAddr,\n    UrlPath,\n    RequestHeader(HeaderName),\n    ResponseHeader(HeaderName),\n    EnvironHeader(String),\n    CustomRequest(String, Option<CustomRequestFn>),\n    CustomResponse(String, Option<CustomResponseFn>),\n}\n\n#[derive(Clone)]\nstruct CustomRequestFn {\n    inner_fn: Rc<dyn Fn(&ServiceRequest) -> String>,\n}\n\nimpl CustomRequestFn {\n    fn call(&self, req: &ServiceRequest) -> String {\n        (self.inner_fn)(req)\n    }\n}\n\nimpl fmt::Debug for CustomRequestFn {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"custom_request_fn\")\n    }\n}\n\n#[derive(Clone)]\nstruct CustomResponseFn {\n    inner_fn: Rc<dyn Fn(&ServiceResponse) -> String>,\n}\n\nimpl CustomResponseFn {\n    fn call(&self, res: &ServiceResponse) -> String {\n        (self.inner_fn)(res)\n    }\n}\n\nimpl fmt::Debug for CustomResponseFn {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"custom_response_fn\")\n    }\n}\n\nimpl FormatText {\n    fn render(\n        &self,\n        fmt: &mut fmt::Formatter<'_>,\n        size: usize,\n        entry_time: OffsetDateTime,\n    ) -> Result<(), fmt::Error> {\n        match self {\n            FormatText::Str(ref string) => fmt.write_str(string),\n            FormatText::Percent => \"%\".fmt(fmt),\n            FormatText::ResponseSize => size.fmt(fmt),\n            FormatText::Time => {\n                let rt = OffsetDateTime::now_utc() - entry_time;\n                let rt = rt.as_seconds_f64();\n                fmt.write_fmt(format_args!(\"{:.6}\", rt))\n            }\n            FormatText::TimeMillis => {\n                let rt = OffsetDateTime::now_utc() - entry_time;\n                let rt = (rt.whole_nanoseconds() as f64) / 1_000_000.0;\n                fmt.write_fmt(format_args!(\"{:.6}\", rt))\n            }\n            FormatText::EnvironHeader(ref name) => {\n                if let Ok(val) = env::var(name) {\n                    fmt.write_fmt(format_args!(\"{}\", val))\n                } else {\n                    \"-\".fmt(fmt)\n                }\n            }\n            _ => Ok(()),\n        }\n    }\n\n    fn render_response(&mut self, res: &ServiceResponse) {\n        match self {\n            FormatText::ResponseStatus => {\n                *self = FormatText::Str(format!(\"{}\", res.status().as_u16()))\n            }\n\n            FormatText::ResponseHeader(ref name) => {\n                let s = if let Some(val) = res.headers().get(name) {\n                    String::from_utf8_lossy(val.as_bytes()).into_owned()\n                } else {\n                    \"-\".to_owned()\n                };\n                *self = FormatText::Str(s.to_string())\n            }\n\n            FormatText::CustomResponse(_, res_fn) => {\n                let text = match res_fn {\n                    Some(res_fn) => FormatText::Str(res_fn.call(res)),\n                    None => FormatText::Str(\"-\".to_owned()),\n                };\n\n                *self = text;\n            }\n\n            _ => {}\n        }\n    }\n\n    fn render_request(&mut self, now: OffsetDateTime, req: &ServiceRequest) {\n        match self {\n            FormatText::RequestLine => {\n                *self = if req.query_string().is_empty() {\n                    FormatText::Str(format!(\n                        \"{} {} {:?}\",\n                        req.method(),\n                        req.path(),\n                        req.version()\n                    ))\n                } else {\n                    FormatText::Str(format!(\n                        \"{} {}?{} {:?}\",\n                        req.method(),\n                        req.path(),\n                        req.query_string(),\n                        req.version()\n                    ))\n                };\n            }\n            FormatText::UrlPath => *self = FormatText::Str(req.path().to_string()),\n            FormatText::RequestTime => *self = FormatText::Str(now.format(&Rfc3339).unwrap()),\n            FormatText::RequestHeader(ref name) => {\n                let s = if let Some(val) = req.headers().get(name) {\n                    String::from_utf8_lossy(val.as_bytes()).into_owned()\n                } else {\n                    \"-\".to_owned()\n                };\n                *self = FormatText::Str(s);\n            }\n            FormatText::RemoteAddr => {\n                let s = if let Some(peer) = req.connection_info().peer_addr() {\n                    FormatText::Str((*peer).to_string())\n                } else {\n                    FormatText::Str(\"-\".to_string())\n                };\n                *self = s;\n            }\n            FormatText::RealIpRemoteAddr => {\n                let s = if let Some(remote) = req.connection_info().realip_remote_addr() {\n                    FormatText::Str(remote.to_string())\n                } else {\n                    FormatText::Str(\"-\".to_string())\n                };\n                *self = s;\n            }\n            FormatText::CustomRequest(_, request_fn) => {\n                let s = match request_fn {\n                    Some(f) => FormatText::Str(f.call(req)),\n                    None => FormatText::Str(\"-\".to_owned()),\n                };\n\n                *self = s;\n            }\n            _ => {}\n        }\n    }\n}\n\n/// Converter to get a String from something that writes to a Formatter.\npub(crate) struct FormatDisplay<'a>(&'a dyn Fn(&mut fmt::Formatter<'_>) -> Result<(), fmt::Error>);\n\nimpl fmt::Display for FormatDisplay<'_> {\n    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        (self.0)(fmt)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_service::IntoService;\n    use actix_utils::future::ok;\n\n    use super::*;\n    use crate::{\n        http::{header, StatusCode},\n        test::{self, TestRequest},\n        HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn test_logger() {\n        let srv = |req: ServiceRequest| {\n            ok(req.into_response(\n                HttpResponse::build(StatusCode::OK)\n                    .insert_header((\"X-Test\", \"ttt\"))\n                    .finish(),\n            ))\n        };\n        let logger = Logger::new(\"%% %{User-Agent}i %{X-Test}o %{HOME}e %D test\");\n\n        let srv = logger.new_transform(srv.into_service()).await.unwrap();\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::USER_AGENT,\n                header::HeaderValue::from_static(\"ACTIX-WEB\"),\n            ))\n            .to_srv_request();\n        let _res = srv.call(req).await;\n    }\n\n    #[actix_rt::test]\n    async fn test_logger_exclude_regex() {\n        let srv = |req: ServiceRequest| {\n            ok(req.into_response(\n                HttpResponse::build(StatusCode::OK)\n                    .insert_header((\"X-Test\", \"ttt\"))\n                    .finish(),\n            ))\n        };\n        let logger =\n            Logger::new(\"%% %{User-Agent}i %{X-Test}o %{HOME}e %D test\").exclude_regex(\"\\\\w\");\n\n        let srv = logger.new_transform(srv.into_service()).await.unwrap();\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::USER_AGENT,\n                header::HeaderValue::from_static(\"ACTIX-WEB\"),\n            ))\n            .to_srv_request();\n        let _res = srv.call(req).await.unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn test_escape_percent() {\n        let mut format = Format::new(\"%%{r}a\");\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::FORWARDED,\n                header::HeaderValue::from_static(\"for=192.0.2.60;proto=http;by=203.0.113.43\"),\n            ))\n            .to_srv_request();\n\n        let now = OffsetDateTime::now_utc();\n        for unit in &mut format.0 {\n            unit.render_request(now, &req);\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let res = ServiceResponse::new(req, HttpResponse::Ok().finish());\n        for unit in &mut format.0 {\n            unit.render_response(&res);\n        }\n\n        let entry_time = OffsetDateTime::now_utc();\n        let render = |fmt: &mut fmt::Formatter<'_>| {\n            for unit in &format.0 {\n                unit.render(fmt, 1024, entry_time)?;\n            }\n            Ok(())\n        };\n        let s = format!(\"{}\", FormatDisplay(&render));\n        assert_eq!(s, \"%{r}a\");\n    }\n\n    #[actix_rt::test]\n    async fn test_url_path() {\n        let mut format = Format::new(\"%T %U\");\n        let req = TestRequest::default()\n            .insert_header((\n                header::USER_AGENT,\n                header::HeaderValue::from_static(\"ACTIX-WEB\"),\n            ))\n            .uri(\"/test/route/yeah\")\n            .to_srv_request();\n\n        let now = OffsetDateTime::now_utc();\n        for unit in &mut format.0 {\n            unit.render_request(now, &req);\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let res = ServiceResponse::new(req, HttpResponse::Ok().force_close().finish());\n        for unit in &mut format.0 {\n            unit.render_response(&res);\n        }\n\n        let render = |fmt: &mut fmt::Formatter<'_>| {\n            for unit in &format.0 {\n                unit.render(fmt, 1024, now)?;\n            }\n            Ok(())\n        };\n        let s = format!(\"{}\", FormatDisplay(&render));\n        assert!(s.contains(\"/test/route/yeah\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_default_format() {\n        let mut format = Format::default();\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::USER_AGENT,\n                header::HeaderValue::from_static(\"ACTIX-WEB\"),\n            ))\n            .peer_addr(\"127.0.0.1:8081\".parse().unwrap())\n            .to_srv_request();\n\n        let now = OffsetDateTime::now_utc();\n        for unit in &mut format.0 {\n            unit.render_request(now, &req);\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let res = ServiceResponse::new(req, HttpResponse::Ok().force_close().finish());\n        for unit in &mut format.0 {\n            unit.render_response(&res);\n        }\n\n        let entry_time = OffsetDateTime::now_utc();\n        let render = |fmt: &mut fmt::Formatter<'_>| {\n            for unit in &format.0 {\n                unit.render(fmt, 1024, entry_time)?;\n            }\n            Ok(())\n        };\n        let s = format!(\"{}\", FormatDisplay(&render));\n        assert!(s.contains(\"GET / HTTP/1.1\"));\n        assert!(s.contains(\"127.0.0.1\"));\n        assert!(s.contains(\"200 1024\"));\n        assert!(s.contains(\"ACTIX-WEB\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_request_time_format() {\n        let mut format = Format::new(\"%t\");\n        let req = TestRequest::default().to_srv_request();\n\n        let now = OffsetDateTime::now_utc();\n        for unit in &mut format.0 {\n            unit.render_request(now, &req);\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let res = ServiceResponse::new(req, HttpResponse::Ok().force_close().finish());\n        for unit in &mut format.0 {\n            unit.render_response(&res);\n        }\n\n        let render = |fmt: &mut fmt::Formatter<'_>| {\n            for unit in &format.0 {\n                unit.render(fmt, 1024, now)?;\n            }\n            Ok(())\n        };\n        let s = format!(\"{}\", FormatDisplay(&render));\n        assert!(s.contains(&now.format(&Rfc3339).unwrap()));\n    }\n\n    #[actix_rt::test]\n    async fn test_remote_addr_format() {\n        let mut format = Format::new(\"%{r}a\");\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::FORWARDED,\n                header::HeaderValue::from_static(\"for=192.0.2.60;proto=http;by=203.0.113.43\"),\n            ))\n            .to_srv_request();\n\n        let now = OffsetDateTime::now_utc();\n        for unit in &mut format.0 {\n            unit.render_request(now, &req);\n        }\n\n        let req = TestRequest::default().to_http_request();\n        let res = ServiceResponse::new(req, HttpResponse::Ok().finish());\n        for unit in &mut format.0 {\n            unit.render_response(&res);\n        }\n\n        let entry_time = OffsetDateTime::now_utc();\n        let render = |fmt: &mut fmt::Formatter<'_>| {\n            for unit in &format.0 {\n                unit.render(fmt, 1024, entry_time)?;\n            }\n            Ok(())\n        };\n        let s = format!(\"{}\", FormatDisplay(&render));\n        assert!(s.contains(\"192.0.2.60\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_custom_closure_req_log() {\n        let mut logger = Logger::new(\"test %{CUSTOM}xi\")\n            .custom_request_replace(\"CUSTOM\", |_req: &ServiceRequest| -> String {\n                String::from(\"custom_log\")\n            });\n        let mut unit = Rc::get_mut(&mut logger.0).unwrap().format.0[1].clone();\n\n        let label = match &unit {\n            FormatText::CustomRequest(label, _) => label,\n            ft => panic!(\"expected CustomRequest, found {:?}\", ft),\n        };\n\n        assert_eq!(label, \"CUSTOM\");\n\n        let req = TestRequest::default().to_srv_request();\n        let now = OffsetDateTime::now_utc();\n\n        unit.render_request(now, &req);\n\n        let render = |fmt: &mut fmt::Formatter<'_>| unit.render(fmt, 1024, now);\n\n        let log_output = FormatDisplay(&render).to_string();\n        assert_eq!(log_output, \"custom_log\");\n    }\n\n    #[actix_rt::test]\n    async fn test_custom_closure_response_log() {\n        let mut logger = Logger::new(\"test %{CUSTOM}xo\").custom_response_replace(\n            \"CUSTOM\",\n            |res: &ServiceResponse| -> String {\n                if res.status().as_u16() == 200 {\n                    String::from(\"custom_log\")\n                } else {\n                    String::from(\"-\")\n                }\n            },\n        );\n        let mut unit = Rc::get_mut(&mut logger.0).unwrap().format.0[1].clone();\n\n        let label = match &unit {\n            FormatText::CustomResponse(label, _) => label,\n            ft => panic!(\"expected CustomResponse, found {:?}\", ft),\n        };\n\n        assert_eq!(label, \"CUSTOM\");\n\n        let req = TestRequest::default().to_http_request();\n        let resp_ok = ServiceResponse::new(req, HttpResponse::Ok().finish());\n        let now = OffsetDateTime::now_utc();\n        unit.render_response(&resp_ok);\n\n        let render = |fmt: &mut fmt::Formatter<'_>| unit.render(fmt, 1024, now);\n\n        let log_output = FormatDisplay(&render).to_string();\n        assert_eq!(log_output, \"custom_log\");\n    }\n\n    #[actix_rt::test]\n    async fn test_closure_logger_in_middleware() {\n        let captured = \"custom log replacement\";\n\n        let logger = Logger::new(\"%{CUSTOM}xi\")\n            .custom_request_replace(\"CUSTOM\", move |_req: &ServiceRequest| -> String {\n                captured.to_owned()\n            });\n\n        let srv = logger.new_transform(test::ok_service()).await.unwrap();\n\n        let req = TestRequest::default().to_srv_request();\n        srv.call(req).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/mod.rs",
    "content": "//! A collection of common middleware.\n//!\n//! # What Is Middleware?\n//!\n//! Actix Web's middleware system allows us to add additional behavior to request/response\n//! processing. Middleware can hook into incoming request and outgoing response processes, enabling\n//! us to modify requests and responses as well as halt request processing to return a response\n//! early.\n//!\n//! Typically, middleware is involved in the following actions:\n//!\n//! - Pre-process the request (e.g., [normalizing paths](NormalizePath))\n//! - Post-process a response (e.g., [logging][Logger])\n//! - Modify application state (through [`ServiceRequest`][crate::dev::ServiceRequest])\n//! - Access external services (e.g., [sessions](https://docs.rs/actix-session), etc.)\n//!\n//! Middleware is registered for each [`App`], [`Scope`](crate::Scope), or\n//! [`Resource`](crate::Resource) and executed in opposite order as registration.\n//!\n//! # Simple Middleware\n//!\n//! In many cases, you can model your middleware as an async function via the [`from_fn()`] helper\n//! that provides a natural interface for implementing your desired behaviors.\n//!\n//! ```\n//! # use actix_web::{\n//! #     App, Error,\n//! #     body::MessageBody,\n//! #     dev::{ServiceRequest, ServiceResponse, Service as _},\n//! # };\n//! use actix_web::middleware::{self, Next};\n//!\n//! async fn my_mw(\n//!     req: ServiceRequest,\n//!     next: Next<impl MessageBody>,\n//! ) -> Result<ServiceResponse<impl MessageBody>, Error> {\n//!     // pre-processing\n//!\n//!     // invoke the wrapped middleware or service\n//!     let res = next.call(req).await?;\n//!\n//!     // post-processing\n//!\n//!     Ok(res)\n//! }\n//!\n//! App::new()\n//!     .wrap(middleware::from_fn(my_mw));\n//! ```\n//!\n//! ## Complex Middleware\n//!\n//! In the more general ase, a middleware is a pair of types that implements the [`Service`] trait\n//! and [`Transform`] trait, respectively. The [`new_transform`] and [`call`] methods must return a\n//! [`Future`], though it can often be [an immediately-ready one](actix_utils::future::Ready).\n//!\n//! All the built-in middleware use this pattern with pairs of builder (`Transform`) +\n//! implementation (`Service`) types.\n//!\n//! # Ordering\n//!\n//! ```\n//! # use actix_web::{web, middleware, get, App, Responder};\n//! #\n//! # // some basic types to make sure this compiles\n//! # type ExtractorA = web::Json<String>;\n//! # type ExtractorB = ExtractorA;\n//! #[get(\"/\")]\n//! async fn service(a: ExtractorA, b: ExtractorB) -> impl Responder { \"Hello, World!\" }\n//!\n//! # fn main() {\n//! # // These aren't snake_case, because they are supposed to be unit structs.\n//! # type MiddlewareA = middleware::Compress;\n//! # type MiddlewareB = middleware::Compress;\n//! # type MiddlewareC = middleware::Compress;\n//! let app = App::new()\n//!     .wrap(MiddlewareA::default())\n//!     .wrap(MiddlewareB::default())\n//!     .wrap(MiddlewareC::default())\n//!     .service(service);\n//! # }\n//! ```\n//!\n//! ```plain\n//!                   Request\n//!                      ⭣\n//! ╭────────────────────┼────╮\n//! │ MiddlewareC        │    │\n//! │ ╭──────────────────┼───╮│\n//! │ │ MiddlewareB      │   ││\n//! │ │ ╭────────────────┼──╮││\n//! │ │ │ MiddlewareA    │  │││\n//! │ │ │ ╭──────────────┼─╮│││\n//! │ │ │ │ ExtractorA   │ ││││\n//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││\n//! │ │ │ │ ExtractorB   │ ││││\n//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││\n//! │ │ │ │ service      │ ││││\n//! │ │ │ ╰──────────────┼─╯│││\n//! │ │ ╰────────────────┼──╯││\n//! │ ╰──────────────────┼───╯│\n//! ╰────────────────────┼────╯\n//!                      ⭣\n//!                   Response\n//! ```\n//! The request _first_ gets processed by the middleware specified _last_ - `MiddlewareC`. It passes\n//! the request (possibly a modified one) to the next middleware - `MiddlewareB` - _or_ directly\n//! responds to the request (e.g. when the request was invalid or an error occurred). `MiddlewareB`\n//! processes the request as well and passes it to `MiddlewareA`, which then passes it to the\n//! [`Service`]. In the [`Service`], the extractors will run first. They don't pass the request on,\n//! but only view it (see [`FromRequest`]). After the [`Service`] responds to the request, the\n//! response is passed back through `MiddlewareA`, `MiddlewareB`, and `MiddlewareC`.\n//!\n//! As you register middleware using [`wrap`][crate::App::wrap] and [`wrap_fn`][crate::App::wrap_fn]\n//! in the [`App`] builder, imagine wrapping layers around an inner [`App`]. The first middleware\n//! layer exposed to a Request is the outermost layer (i.e., the _last_ registered in the builder\n//! chain, in the example above: `MiddlewareC`). Consequently, the _first_ middleware registered in\n//! the builder chain is the _last_ to start executing during request processing (`MiddlewareA`).\n//! Ordering is less obvious when wrapped services also have middleware applied. In this case,\n//! middleware are run in reverse order for [`App`] _and then_ in reverse order for the wrapped\n//! service.\n//!\n//! # Middleware Traits\n//!\n//! ## `Transform<S, Req>`\n//!\n//! The [`Transform`] trait is the builder for the actual [`Service`]s that handle the requests. All\n//! the middleware you pass to the `wrap` methods implement this trait. During construction, each\n//! thread assembles a chain of [`Service`]s by calling [`new_transform`] and passing the next\n//! [`Service`] (`S`) in the chain. The created [`Service`] handles requests of type `Req`.\n//!\n//! In the example from the [ordering](#ordering) section, the chain would be:\n//!\n//! ```plain\n//! MiddlewareCService {\n//!     next: MiddlewareBService {\n//!         next: MiddlewareAService { ... }\n//!     }\n//! }\n//! ```\n//!\n//! ## `Service<Req>`\n//!\n//! A [`Service`] `S` represents an asynchronous operation that turns a request of type `Req` into a\n//! response of type [`S::Response`](crate::dev::Service::Response) or an error of type\n//! [`S::Error`](crate::dev::Service::Error). You can think of the service of being roughly:\n//!\n//! ```ignore\n//! async fn(&self, req: Req) -> Result<S::Response, S::Error>\n//! ```\n//!\n//! In most cases the [`Service`] implementation will, at some point, call the wrapped [`Service`]\n//! in its [`call`] implementation.\n//!\n//! Note that the [`Service`]s created by [`new_transform`] don't need to be [`Send`] or [`Sync`].\n//!\n//! # Example\n//!\n//! ```\n//! use std::{future::{ready, Ready, Future}, pin::Pin};\n//!\n//! use actix_web::{\n//!     dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},\n//!     web, Error,\n//! #   App\n//! };\n//!\n//! pub struct SayHi;\n//!\n//! // `S` - type of the next service\n//! // `B` - type of response's body\n//! impl<S, B> Transform<S, ServiceRequest> for SayHi\n//! where\n//!     S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n//!     S::Future: 'static,\n//!     B: 'static,\n//! {\n//!     type Response = ServiceResponse<B>;\n//!     type Error = Error;\n//!     type InitError = ();\n//!     type Transform = SayHiMiddleware<S>;\n//!     type Future = Ready<Result<Self::Transform, Self::InitError>>;\n//!\n//!     fn new_transform(&self, service: S) -> Self::Future {\n//!         ready(Ok(SayHiMiddleware { service }))\n//!     }\n//! }\n//!\n//! pub struct SayHiMiddleware<S> {\n//!     /// The next service to call\n//!     service: S,\n//! }\n//!\n//! // This future doesn't have the requirement of being `Send`.\n//! // See: futures_util::future::LocalBoxFuture\n//! type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;\n//!\n//! // `S`: type of the wrapped service\n//! // `B`: type of the body - try to be generic over the body where possible\n//! impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>\n//! where\n//!     S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n//!     S::Future: 'static,\n//!     B: 'static,\n//! {\n//!     type Response = ServiceResponse<B>;\n//!     type Error = Error;\n//!     type Future = LocalBoxFuture<Result<Self::Response, Self::Error>>;\n//!\n//!     // This service is ready when its next service is ready\n//!     forward_ready!(service);\n//!\n//!     fn call(&self, req: ServiceRequest) -> Self::Future {\n//!         println!(\"Hi from start. You requested: {}\", req.path());\n//!\n//!         // A more complex middleware, could return an error or an early response here.\n//!\n//!         let fut = self.service.call(req);\n//!\n//!         Box::pin(async move {\n//!             let res = fut.await?;\n//!\n//!             println!(\"Hi from response\");\n//!             Ok(res)\n//!         })\n//!     }\n//! }\n//!\n//! # fn main() {\n//! let app = App::new()\n//!     .wrap(SayHi)\n//!     .route(\"/\", web::get().to(|| async { \"Hello, middleware!\" }));\n//! # }\n//! ```\n//!\n//! [`Future`]: std::future::Future\n//! [`App`]: crate::App\n//! [`FromRequest`]: crate::FromRequest\n//! [`Service`]: crate::dev::Service\n//! [`Transform`]: crate::dev::Transform\n//! [`call`]: crate::dev::Service::call()\n//! [`new_transform`]: crate::dev::Transform::new_transform()\n//! [`from_fn`]: crate\n\nmod compat;\n#[cfg(feature = \"__compress\")]\nmod compress;\nmod condition;\nmod default_headers;\nmod err_handlers;\nmod from_fn;\nmod identity;\nmod logger;\nmod normalize;\n\n#[cfg(feature = \"__compress\")]\npub use self::compress::Compress;\npub use self::{\n    compat::Compat,\n    condition::Condition,\n    default_headers::DefaultHeaders,\n    err_handlers::{ErrorHandlerResponse, ErrorHandlers},\n    from_fn::{from_fn, Next},\n    identity::Identity,\n    logger::Logger,\n    normalize::{NormalizePath, TrailingSlash},\n};\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{http::StatusCode, App};\n\n    #[test]\n    fn common_combinations() {\n        // ensure there's no reason that the built-in middleware cannot compose\n\n        let _ = App::new()\n            .wrap(Compat::new(Logger::default()))\n            .wrap(Condition::new(true, DefaultHeaders::new()))\n            .wrap(DefaultHeaders::new().add((\"X-Test2\", \"X-Value2\")))\n            .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {\n                Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n            }))\n            .wrap(Logger::default())\n            .wrap(NormalizePath::new(TrailingSlash::Trim));\n\n        let _ = App::new()\n            .wrap(NormalizePath::new(TrailingSlash::Trim))\n            .wrap(Logger::default())\n            .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {\n                Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))\n            }))\n            .wrap(DefaultHeaders::new().add((\"X-Test2\", \"X-Value2\")))\n            .wrap(Condition::new(true, DefaultHeaders::new()))\n            .wrap(Compat::new(Logger::default()));\n\n        #[cfg(feature = \"__compress\")]\n        {\n            let _ = App::new().wrap(Compress::default()).wrap(Logger::default());\n            let _ = App::new().wrap(Logger::default()).wrap(Compress::default());\n            let _ = App::new().wrap(Compat::new(Compress::default()));\n            let _ = App::new().wrap(Condition::new(true, Compat::new(Compress::default())));\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/middleware/normalize.rs",
    "content": "//! For middleware documentation, see [`NormalizePath`].\n\nuse actix_http::uri::{PathAndQuery, Uri};\nuse actix_router::Url;\nuse actix_service::{Service, Transform};\nuse actix_utils::future::{ready, Ready};\nuse bytes::Bytes;\n#[cfg(feature = \"unicode\")]\nuse regex::Regex;\n#[cfg(not(feature = \"unicode\"))]\nuse regex_lite::Regex;\n\nuse crate::{\n    service::{ServiceRequest, ServiceResponse},\n    Error,\n};\n\nfn build_byte_index_map(old_path: &str, new_path: &str) -> Vec<u16> {\n    let old_path = old_path.as_bytes();\n    let new_path = new_path.as_bytes();\n\n    let mut map = Vec::with_capacity(old_path.len() + 1);\n    map.push(0);\n\n    let mut old_idx = 0usize;\n    let mut new_idx = 0usize;\n\n    while old_idx < old_path.len() {\n        if new_idx < new_path.len() && old_path[old_idx] == new_path[new_idx] {\n            new_idx += 1;\n        }\n\n        old_idx += 1;\n        map.push(new_idx.min(u16::MAX as usize) as u16);\n    }\n\n    map\n}\n\n/// Determines the behavior of the [`NormalizePath`] middleware.\n///\n/// The default is `TrailingSlash::Trim`.\n#[non_exhaustive]\n#[derive(Debug, Clone, Copy, Default)]\npub enum TrailingSlash {\n    /// Trim trailing slashes from the end of the path.\n    ///\n    /// Using this will require all routes to omit trailing slashes for them to be accessible.\n    #[default]\n    Trim,\n\n    /// Only merge any present multiple trailing slashes.\n    ///\n    /// This option provides the best compatibility with behavior in actix-web v2.0.\n    MergeOnly,\n\n    /// Always add a trailing slash to the end of the path.\n    ///\n    /// Using this will require all routes have a trailing slash for them to be accessible.\n    Always,\n}\n\n/// Middleware for normalizing a request's path so that routes can be matched more flexibly.\n///\n/// # Normalization Steps\n/// - Merges consecutive slashes into one. (For example, `/path//one` always becomes `/path/one`.)\n/// - Appends a trailing slash if one is not present, removes one if present, or keeps trailing\n///   slashes as-is, depending on which [`TrailingSlash`] variant is supplied\n///   to [`new`](NormalizePath::new()).\n///\n/// # Default Behavior\n/// The default constructor chooses to strip trailing slashes from the end of paths with them\n/// ([`TrailingSlash::Trim`]). The implication is that route definitions should be defined without\n/// trailing slashes or else they will be inaccessible (or vice versa when using the\n/// `TrailingSlash::Always` behavior), as shown in the example tests below.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, middleware, App};\n///\n/// # actix_web::rt::System::new().block_on(async {\n/// let app = App::new()\n///     .wrap(middleware::NormalizePath::trim())\n///     .route(\"/test\", web::get().to(|| async { \"test\" }))\n///     .route(\"/unmatchable/\", web::get().to(|| async { \"unmatchable\" }));\n///\n/// use actix_web::http::StatusCode;\n/// use actix_web::test::{call_service, init_service, TestRequest};\n///\n/// let app = init_service(app).await;\n///\n/// let req = TestRequest::with_uri(\"/test\").to_request();\n/// let res = call_service(&app, req).await;\n/// assert_eq!(res.status(), StatusCode::OK);\n///\n/// let req = TestRequest::with_uri(\"/test/\").to_request();\n/// let res = call_service(&app, req).await;\n/// assert_eq!(res.status(), StatusCode::OK);\n///\n/// let req = TestRequest::with_uri(\"/unmatchable\").to_request();\n/// let res = call_service(&app, req).await;\n/// assert_eq!(res.status(), StatusCode::NOT_FOUND);\n///\n/// let req = TestRequest::with_uri(\"/unmatchable/\").to_request();\n/// let res = call_service(&app, req).await;\n/// assert_eq!(res.status(), StatusCode::NOT_FOUND);\n/// # })\n/// ```\n#[derive(Debug, Clone, Copy)]\npub struct NormalizePath(TrailingSlash);\n\nimpl Default for NormalizePath {\n    fn default() -> Self {\n        log::warn!(\n            \"`NormalizePath::default()` is deprecated. The default trailing slash behavior changed \\\n            in v4 from `Always` to `Trim`. Update your call to `NormalizePath::new(...)`.\"\n        );\n\n        Self(TrailingSlash::Trim)\n    }\n}\n\nimpl NormalizePath {\n    /// Create new `NormalizePath` middleware with the specified trailing slash style.\n    pub fn new(trailing_slash_style: TrailingSlash) -> Self {\n        Self(trailing_slash_style)\n    }\n\n    /// Constructs a new `NormalizePath` middleware with [trim](TrailingSlash::Trim) semantics.\n    ///\n    /// Use this instead of `NormalizePath::default()` to avoid deprecation warning.\n    pub fn trim() -> Self {\n        Self::new(TrailingSlash::Trim)\n    }\n}\n\nimpl<S, B> Transform<S, ServiceRequest> for NormalizePath\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Transform = NormalizePathNormalization<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ready(Ok(NormalizePathNormalization {\n            service,\n            merge_slash: Regex::new(\"//+\").unwrap(),\n            trailing_slash_behavior: self.0,\n        }))\n    }\n}\n\npub struct NormalizePathNormalization<S> {\n    service: S,\n    merge_slash: Regex,\n    trailing_slash_behavior: TrailingSlash,\n}\n\nimpl<S, B> Service<ServiceRequest> for NormalizePathNormalization<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Future = S::Future;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, mut req: ServiceRequest) -> Self::Future {\n        let head = req.head_mut();\n\n        let original_path = head.uri.path();\n\n        // An empty path here means that the URI has no valid path. We skip normalization in this\n        // case, because adding a path can make the URI invalid\n        if !original_path.is_empty() {\n            // Either adds a string to the end (duplicates will be removed anyways) or trims all\n            // slashes from the end\n            let path = match self.trailing_slash_behavior {\n                TrailingSlash::Always => format!(\"{}/\", original_path),\n                TrailingSlash::MergeOnly => original_path.to_string(),\n                TrailingSlash::Trim => original_path.trim_end_matches('/').to_string(),\n            };\n\n            // normalize multiple /'s to one /\n            let path = self.merge_slash.replace_all(&path, \"/\");\n\n            // Ensure root paths are still resolvable. If resulting path is blank after previous\n            // step it means the path was one or more slashes. Reduce to single slash.\n            let path = if path.is_empty() { \"/\" } else { path.as_ref() };\n\n            // Check whether the path has been changed\n            //\n            // This check was previously implemented as string length comparison\n            //\n            // That approach fails when a trailing slash is added,\n            // and a duplicate slash is removed,\n            // since the length of the strings remains the same\n            //\n            // For example, the path \"/v1//s\" will be normalized to \"/v1/s/\"\n            // Both of the paths have the same length,\n            // so the change can not be deduced from the length comparison\n            if path != original_path {\n                let reindex = build_byte_index_map(original_path, path);\n                let mut parts = head.uri.clone().into_parts();\n                let query = parts.path_and_query.as_ref().and_then(|pq| pq.query());\n\n                let path = match query {\n                    Some(q) => Bytes::from(format!(\"{}?{}\", path, q)),\n                    None => Bytes::copy_from_slice(path.as_bytes()),\n                };\n                parts.path_and_query = Some(PathAndQuery::from_maybe_shared(path).unwrap());\n\n                let uri = Uri::from_parts(parts).unwrap();\n                req.match_info_mut()\n                    .update_with_reindex(Url::new(uri.clone()), |idx| {\n                        let idx = usize::from(idx).min(reindex.len() - 1);\n                        reindex[idx]\n                    });\n                req.head_mut().uri = uri;\n            }\n        }\n        self.service.call(req)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::StatusCode;\n    use actix_service::IntoService;\n\n    use super::*;\n    use crate::{\n        guard::fn_guard,\n        test::{call_service, init_service, read_body, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn test_wrap() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath::default())\n                .service(web::resource(\"/\").to(HttpResponse::Ok))\n                .service(web::resource(\"/v1/something\").to(HttpResponse::Ok))\n                .service(\n                    web::resource(\"/v2/something\")\n                        .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                        .to(HttpResponse::Ok),\n                ),\n        )\n        .await;\n\n        let test_uris = vec![\n            \"/\",\n            \"/?query=test\",\n            \"///\",\n            \"/v1//something\",\n            \"/v1//something////\",\n            \"//v1/something\",\n            \"//v1//////something\",\n            \"/v2//something?query=test\",\n            \"/v2//something////?query=test\",\n            \"//v2/something?query=test\",\n            \"//v2//////something?query=test\",\n        ];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn trim_trailing_slashes() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath(TrailingSlash::Trim))\n                .service(web::resource(\"/\").to(HttpResponse::Ok))\n                .service(web::resource(\"/v1/something\").to(HttpResponse::Ok))\n                .service(\n                    web::resource(\"/v2/something\")\n                        .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                        .to(HttpResponse::Ok),\n                ),\n        )\n        .await;\n\n        let test_uris = vec![\n            \"/\",\n            \"///\",\n            \"/v1/something\",\n            \"/v1/something/\",\n            \"/v1/something////\",\n            \"//v1//something\",\n            \"//v1//something//\",\n            \"/v2/something?query=test\",\n            \"/v2/something/?query=test\",\n            \"/v2/something////?query=test\",\n            \"//v2//something?query=test\",\n            \"//v2//something//?query=test\",\n        ];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn trim_root_trailing_slashes_with_query() {\n        let app = init_service(\n            App::new().wrap(NormalizePath(TrailingSlash::Trim)).service(\n                web::resource(\"/\")\n                    .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                    .to(HttpResponse::Ok),\n            ),\n        )\n        .await;\n\n        let test_uris = vec![\"/?query=test\", \"//?query=test\", \"///?query=test\"];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn ensure_trailing_slash() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath(TrailingSlash::Always))\n                .service(web::resource(\"/\").to(HttpResponse::Ok))\n                .service(web::resource(\"/v1/something/\").to(HttpResponse::Ok))\n                .service(\n                    web::resource(\"/v2/something/\")\n                        .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                        .to(HttpResponse::Ok),\n                ),\n        )\n        .await;\n\n        let test_uris = vec![\n            \"/\",\n            \"///\",\n            \"/v1/something\",\n            \"/v1/something/\",\n            \"/v1/something////\",\n            \"//v1//something\",\n            \"//v1//something//\",\n            \"/v2/something?query=test\",\n            \"/v2/something/?query=test\",\n            \"/v2/something////?query=test\",\n            \"//v2//something?query=test\",\n            \"//v2//something//?query=test\",\n        ];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn ensure_root_trailing_slash_with_query() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath(TrailingSlash::Always))\n                .service(\n                    web::resource(\"/\")\n                        .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                        .to(HttpResponse::Ok),\n                ),\n        )\n        .await;\n\n        let test_uris = vec![\"/?query=test\", \"//?query=test\", \"///?query=test\"];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn keep_trailing_slash_unchanged() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath(TrailingSlash::MergeOnly))\n                .service(web::resource(\"/\").to(HttpResponse::Ok))\n                .service(web::resource(\"/v1/something\").to(HttpResponse::Ok))\n                .service(web::resource(\"/v1/\").to(HttpResponse::Ok))\n                .service(\n                    web::resource(\"/v2/something\")\n                        .guard(fn_guard(|ctx| ctx.head().uri.query() == Some(\"query=test\")))\n                        .to(HttpResponse::Ok),\n                ),\n        )\n        .await;\n\n        let tests = vec![\n            (\"/\", true), // root paths should still work\n            (\"/?query=test\", true),\n            (\"///\", true),\n            (\"/v1/something////\", false),\n            (\"/v1/something/\", false),\n            (\"//v1//something\", true),\n            (\"/v1/\", true),\n            (\"/v1\", false),\n            (\"/v1////\", true),\n            (\"//v1//\", true),\n            (\"///v1\", false),\n            (\"/v2/something?query=test\", true),\n            (\"/v2/something/?query=test\", false),\n            (\"/v2/something//?query=test\", false),\n            (\"//v2//something?query=test\", true),\n        ];\n\n        for (uri, success) in tests {\n            let req = TestRequest::with_uri(uri).to_request();\n            let res = call_service(&app, req).await;\n            assert_eq!(res.status().is_success(), success, \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn scope_dynamic_tail_path_is_reindexed() {\n        async fn handler(path: web::Path<String>) -> HttpResponse {\n            HttpResponse::Ok().body(path.into_inner())\n        }\n\n        let app = init_service(\n            App::new().service(\n                web::scope(\"{tail:.*}\")\n                    .wrap(NormalizePath::trim())\n                    .default_service(web::to(handler)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/uaie//iuaei\").to_request();\n        let res = call_service(&app, req).await;\n\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(read_body(res).await, Bytes::from_static(b\"uaie/iuaei\"));\n    }\n\n    #[actix_rt::test]\n    async fn scope_static_prefix_skip_is_reindexed() {\n        let app = init_service(\n            App::new().service(\n                web::scope(\"/api\")\n                    .wrap(NormalizePath::trim())\n                    .service(web::resource(\"/v1\").to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/api//v1\").to_request();\n        let res = call_service(&app, req).await;\n\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn no_path() {\n        let app = init_service(\n            App::new()\n                .wrap(NormalizePath::default())\n                .service(web::resource(\"/\").to(HttpResponse::Ok)),\n        )\n        .await;\n\n        // This URI will be interpreted as an authority form, i.e. there is no path nor scheme\n        // (https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3)\n        let req = TestRequest::with_uri(\"eh\").to_request();\n        let res = call_service(&app, req).await;\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_in_place_normalization() {\n        let srv = |req: ServiceRequest| {\n            assert_eq!(\"/v1/something\", req.path());\n            ready(Ok(req.into_response(HttpResponse::Ok().finish())))\n        };\n\n        let normalize = NormalizePath::default()\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let test_uris = vec![\n            \"/v1//something////\",\n            \"///v1/something\",\n            \"//v1///something\",\n            \"/v1//something\",\n        ];\n\n        for uri in test_uris {\n            let req = TestRequest::with_uri(uri).to_srv_request();\n            let res = normalize.call(req).await.unwrap();\n            assert!(res.status().is_success(), \"Failed uri: {}\", uri);\n        }\n    }\n\n    #[actix_rt::test]\n    async fn should_normalize_nothing() {\n        const URI: &str = \"/v1/something\";\n\n        let srv = |req: ServiceRequest| {\n            assert_eq!(URI, req.path());\n            ready(Ok(req.into_response(HttpResponse::Ok().finish())))\n        };\n\n        let normalize = NormalizePath::default()\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let req = TestRequest::with_uri(URI).to_srv_request();\n        let res = normalize.call(req).await.unwrap();\n        assert!(res.status().is_success());\n    }\n\n    #[actix_rt::test]\n    async fn should_normalize_no_trail() {\n        let srv = |req: ServiceRequest| {\n            assert_eq!(\"/v1/something\", req.path());\n            ready(Ok(req.into_response(HttpResponse::Ok().finish())))\n        };\n\n        let normalize = NormalizePath::default()\n            .new_transform(srv.into_service())\n            .await\n            .unwrap();\n\n        let req = TestRequest::with_uri(\"/v1/something/\").to_srv_request();\n        let res = normalize.call(req).await.unwrap();\n        assert!(res.status().is_success());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/redirect.rs",
    "content": "//! See [`Redirect`] for service/responder documentation.\n\nuse std::borrow::Cow;\n\nuse actix_utils::future::ready;\n\nuse crate::{\n    dev::{fn_service, AppService, HttpServiceFactory, ResourceDef, ServiceRequest},\n    http::{header::LOCATION, StatusCode},\n    HttpRequest, HttpResponse, Responder,\n};\n\n/// An HTTP service for redirecting one path to another path or URL.\n///\n/// By default, the \"307 Temporary Redirect\" status is used when responding. See [this MDN\n/// article][mdn-redirects] on why 307 is preferred over 302.\n///\n/// # Examples\n/// As service:\n/// ```\n/// use actix_web::{web, App};\n///\n/// App::new()\n///     // redirect \"/duck\" to DuckDuckGo\n///     .service(web::redirect(\"/duck\", \"https://duck.com\"))\n///     .service(\n///         // redirect \"/api/old\" to \"/api/new\"\n///         web::scope(\"/api\").service(web::redirect(\"/old\", \"/new\"))\n///     );\n/// ```\n///\n/// As responder:\n/// ```\n/// use actix_web::{web::Redirect, Responder};\n///\n/// async fn handler() -> impl Responder {\n///     // sends a permanent (308) redirect to duck.com\n///     Redirect::to(\"https://duck.com\").permanent()\n/// }\n/// # actix_web::web::to(handler);\n/// ```\n///\n/// [mdn-redirects]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections#temporary_redirections\n#[derive(Debug, Clone)]\npub struct Redirect {\n    from: Cow<'static, str>,\n    to: Cow<'static, str>,\n    status_code: StatusCode,\n}\n\nimpl Redirect {\n    /// Construct a new `Redirect` service that matches a path.\n    ///\n    /// This service will match exact paths equal to `from` within the current scope. I.e., when\n    /// registered on the root `App`, it will match exact, whole paths. But when registered on a\n    /// `Scope`, it will match paths under that scope, ignoring the defined scope prefix, just like\n    /// a normal `Resource` or `Route`.\n    ///\n    /// The `to` argument can be path or URL; whatever is provided shall be used verbatim when\n    /// setting the redirect location. This means that relative paths can be used to navigate\n    /// relatively to matched paths.\n    ///\n    /// Prefer [`Redirect::to()`](Self::to) when using `Redirect` as a responder since `from` has\n    /// no meaning in that context.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::{web::Redirect, App};\n    /// App::new()\n    ///     // redirects \"/oh/hi/mark\" to \"/oh/bye/johnny\"\n    ///     .service(Redirect::new(\"/oh/hi/mark\", \"../../bye/johnny\"));\n    /// ```\n    pub fn new(from: impl Into<Cow<'static, str>>, to: impl Into<Cow<'static, str>>) -> Self {\n        Self {\n            from: from.into(),\n            to: to.into(),\n            status_code: StatusCode::TEMPORARY_REDIRECT,\n        }\n    }\n\n    /// Construct a new `Redirect` to use as a responder.\n    ///\n    /// Only receives the `to` argument since responders do not need to do route matching.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{web::Redirect, Responder};\n    ///\n    /// async fn admin_page() -> impl Responder {\n    ///     // sends a temporary 307 redirect to the login path\n    ///     Redirect::to(\"/login\")\n    /// }\n    /// # actix_web::web::to(admin_page);\n    /// ```\n    pub fn to(to: impl Into<Cow<'static, str>>) -> Self {\n        Self {\n            from: \"/\".into(),\n            to: to.into(),\n            status_code: StatusCode::TEMPORARY_REDIRECT,\n        }\n    }\n\n    /// Use the \"308 Permanent Redirect\" status when responding.\n    ///\n    /// See [this MDN article][mdn-redirects] on why 308 is preferred over 301.\n    ///\n    /// [mdn-redirects]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections#permanent_redirections\n    pub fn permanent(self) -> Self {\n        self.using_status_code(StatusCode::PERMANENT_REDIRECT)\n    }\n\n    /// Use the \"307 Temporary Redirect\" status when responding.\n    ///\n    /// See [this MDN article][mdn-redirects] on why 307 is preferred over 302.\n    ///\n    /// [mdn-redirects]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections#temporary_redirections\n    pub fn temporary(self) -> Self {\n        self.using_status_code(StatusCode::TEMPORARY_REDIRECT)\n    }\n\n    /// Use the \"303 See Other\" status when responding.\n    ///\n    /// This status code is semantically correct as the response to a successful login, for example.\n    pub fn see_other(self) -> Self {\n        self.using_status_code(StatusCode::SEE_OTHER)\n    }\n\n    /// Allows the use of custom status codes for less common redirect types.\n    ///\n    /// In most cases, the default status (\"308 Permanent Redirect\") or using the `temporary`\n    /// method, which uses the \"307 Temporary Redirect\" status have more consistent behavior than\n    /// 301 and 302 codes, respectively.\n    ///\n    /// ```\n    /// # use actix_web::{http::StatusCode, web::Redirect};\n    /// // redirects would use \"301 Moved Permanently\" status code\n    /// Redirect::new(\"/old\", \"/new\")\n    ///     .using_status_code(StatusCode::MOVED_PERMANENTLY);\n    ///\n    /// // redirects would use \"302 Found\" status code\n    /// Redirect::new(\"/old\", \"/new\")\n    ///     .using_status_code(StatusCode::FOUND);\n    /// ```\n    pub fn using_status_code(mut self, status: StatusCode) -> Self {\n        self.status_code = status;\n        self\n    }\n}\n\nimpl HttpServiceFactory for Redirect {\n    fn register(self, config: &mut AppService) {\n        let redirect = self.clone();\n        let rdef = ResourceDef::new(self.from.into_owned());\n        let redirect_factory = fn_service(move |mut req: ServiceRequest| {\n            let res = redirect.clone().respond_to(req.parts_mut().0);\n            ready(Ok(req.into_response(res.map_into_boxed_body())))\n        });\n\n        config.register_service(rdef, None, redirect_factory, None)\n    }\n}\n\nimpl Responder for Redirect {\n    type Body = ();\n\n    fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {\n        let mut res = HttpResponse::with_body(self.status_code, ());\n\n        if let Ok(hdr_val) = self.to.parse() {\n            res.headers_mut().insert(LOCATION, hdr_val);\n        } else {\n            log::error!(\n                \"redirect target location can not be converted to header value: {:?}\",\n                self.to,\n            );\n        }\n\n        res\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{dev::Service, test, App};\n\n    #[actix_rt::test]\n    async fn absolute_redirects() {\n        let redirector = Redirect::new(\"/one\", \"/two\").permanent();\n\n        let svc = test::init_service(App::new().service(redirector)).await;\n\n        let req = test::TestRequest::default().uri(\"/one\").to_request();\n        let res = svc.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::from_u16(308).unwrap());\n        let hdr = res.headers().get(&LOCATION).unwrap();\n        assert_eq!(hdr.to_str().unwrap(), \"/two\");\n    }\n\n    #[actix_rt::test]\n    async fn relative_redirects() {\n        let redirector = Redirect::new(\"/one\", \"two\").permanent();\n\n        let svc = test::init_service(App::new().service(redirector)).await;\n\n        let req = test::TestRequest::default().uri(\"/one\").to_request();\n        let res = svc.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::from_u16(308).unwrap());\n        let hdr = res.headers().get(&LOCATION).unwrap();\n        assert_eq!(hdr.to_str().unwrap(), \"two\");\n    }\n\n    #[actix_rt::test]\n    async fn temporary_redirects() {\n        let external_service = Redirect::new(\"/external\", \"https://duck.com\");\n\n        let svc = test::init_service(App::new().service(external_service)).await;\n\n        let req = test::TestRequest::default().uri(\"/external\").to_request();\n        let res = svc.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::from_u16(307).unwrap());\n        let hdr = res.headers().get(&LOCATION).unwrap();\n        assert_eq!(hdr.to_str().unwrap(), \"https://duck.com\");\n    }\n\n    #[actix_rt::test]\n    async fn as_responder() {\n        let responder = Redirect::to(\"https://duck.com\");\n\n        let req = test::TestRequest::default().to_http_request();\n        let res = responder.respond_to(&req);\n\n        assert_eq!(res.status(), StatusCode::from_u16(307).unwrap());\n        let hdr = res.headers().get(&LOCATION).unwrap();\n        assert_eq!(hdr.to_str().unwrap(), \"https://duck.com\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/request.rs",
    "content": "use std::{\n    cell::{Ref, RefCell, RefMut},\n    collections::HashMap,\n    fmt,\n    hash::{BuildHasher, Hash},\n    net,\n    rc::Rc,\n    str,\n};\n\nuse actix_http::{Message, RequestHead};\nuse actix_router::{Path, Url};\nuse actix_utils::future::{ok, Ready};\n#[cfg(feature = \"cookies\")]\nuse cookie::{Cookie, ParseError as CookieParseError};\nuse smallvec::SmallVec;\n\nuse crate::{\n    app_service::AppInitServiceState,\n    config::AppConfig,\n    dev::{Extensions, Payload},\n    error::UrlGenerationError,\n    http::{header::HeaderMap, Method, Uri, Version},\n    info::ConnectionInfo,\n    rmap::ResourceMap,\n    Error, FromRequest, HttpMessage,\n};\n\n#[cfg(feature = \"cookies\")]\nstruct Cookies(Vec<Cookie<'static>>);\n\n/// An incoming request.\n#[derive(Clone)]\npub struct HttpRequest {\n    /// # Invariant\n    /// `Rc<HttpRequestInner>` is used exclusively and NO `Weak<HttpRequestInner>`\n    /// is allowed anywhere in the code. Weak pointer is purposely ignored when\n    /// doing `Rc`'s ref counter check. Expect panics if this invariant is violated.\n    pub(crate) inner: Rc<HttpRequestInner>,\n}\n\npub(crate) struct HttpRequestInner {\n    pub(crate) head: Message<RequestHead>,\n    pub(crate) path: Path<Url>,\n    pub(crate) resource_path: SmallVec<[u16; 4]>,\n    pub(crate) resource_path_matched: bool,\n    pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,\n    pub(crate) conn_data: Option<Rc<Extensions>>,\n    pub(crate) extensions: Rc<RefCell<Extensions>>,\n    app_state: Rc<AppInitServiceState>,\n}\n\nimpl HttpRequest {\n    #[inline]\n    pub(crate) fn new(\n        path: Path<Url>,\n        head: Message<RequestHead>,\n        app_state: Rc<AppInitServiceState>,\n        app_data: Rc<Extensions>,\n        conn_data: Option<Rc<Extensions>>,\n        extensions: Rc<RefCell<Extensions>>,\n    ) -> HttpRequest {\n        let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();\n        data.push(app_data);\n\n        HttpRequest {\n            inner: Rc::new(HttpRequestInner {\n                head,\n                path,\n                resource_path: SmallVec::new(),\n                resource_path_matched: false,\n                app_state,\n                app_data: data,\n                conn_data,\n                extensions,\n            }),\n        }\n    }\n}\n\nimpl HttpRequest {\n    /// This method returns reference to the request head\n    #[inline]\n    pub fn head(&self) -> &RequestHead {\n        &self.inner.head\n    }\n\n    /// This method returns mutable reference to the request head.\n    /// panics if multiple references of HTTP request exists.\n    #[inline]\n    pub(crate) fn head_mut(&mut self) -> &mut RequestHead {\n        &mut Rc::get_mut(&mut self.inner).unwrap().head\n    }\n\n    /// Request's uri.\n    #[inline]\n    pub fn uri(&self) -> &Uri {\n        &self.head().uri\n    }\n\n    /// Returns request's original full URL.\n    ///\n    /// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())\n    /// to get forwarded scheme & host.\n    ///\n    /// ```\n    /// use actix_web::test::TestRequest;\n    /// let req = TestRequest::with_uri(\"http://10.1.2.3:8443/api?id=4&name=foo\")\n    ///     .insert_header((\"host\", \"example.com\"))\n    ///     .to_http_request();\n    ///\n    /// assert_eq!(\n    ///     req.full_url().as_str(),\n    ///     \"http://example.com/api?id=4&name=foo\",\n    /// );\n    /// ```\n    pub fn full_url(&self) -> url::Url {\n        let info = self.connection_info();\n        let scheme = info.scheme();\n        let host = info.host();\n        let path_and_query = self\n            .uri()\n            .path_and_query()\n            .map(|paq| paq.as_str())\n            .unwrap_or(\"/\");\n\n        url::Url::parse(&format!(\"{scheme}://{host}{path_and_query}\")).unwrap()\n    }\n\n    /// Read the Request method.\n    #[inline]\n    pub fn method(&self) -> &Method {\n        &self.head().method\n    }\n\n    /// Read the Request Version.\n    #[inline]\n    pub fn version(&self) -> Version {\n        self.head().version\n    }\n\n    #[inline]\n    /// Returns request's headers.\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    /// The target path of this request.\n    #[inline]\n    pub fn path(&self) -> &str {\n        self.head().uri.path()\n    }\n\n    /// The query string in the URL.\n    ///\n    /// Example: `id=10`\n    #[inline]\n    pub fn query_string(&self) -> &str {\n        self.uri().query().unwrap_or_default()\n    }\n\n    /// Returns a reference to the URL parameters container.\n    ///\n    /// A URL parameter is specified in the form `{identifier}`, where the identifier can be used\n    /// later in a request handler to access the matched value for that parameter.\n    ///\n    /// # Percent Encoding and URL Parameters\n    /// Because each URL parameter is able to capture multiple path segments, none of\n    /// `[\"%2F\", \"%25\", \"%2B\"]` found in the request URI are decoded into `[\"/\", \"%\", \"+\"]` in order\n    /// to preserve path integrity. If a URL parameter is expected to contain these characters, then\n    /// it is on the user to decode them or use the [`web::Path`](crate::web::Path) extractor which\n    /// _will_ decode these special sequences.\n    #[inline]\n    pub fn match_info(&self) -> &Path<Url> {\n        &self.inner.path\n    }\n\n    /// Returns a mutable reference to the URL parameters container.\n    ///\n    /// # Panics\n    /// Panics if this `HttpRequest` has been cloned.\n    #[inline]\n    pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {\n        &mut Rc::get_mut(&mut self.inner).unwrap().path\n    }\n\n    #[inline]\n    pub(crate) fn push_resource_id(&mut self, id: u16) {\n        Rc::get_mut(&mut self.inner).unwrap().resource_path.push(id);\n    }\n\n    #[inline]\n    pub(crate) fn mark_resource_path(&mut self, is_matched: bool) {\n        Rc::get_mut(&mut self.inner).unwrap().resource_path_matched = is_matched;\n    }\n\n    #[inline]\n    pub(crate) fn resource_path(&self) -> &[u16] {\n        &self.inner.resource_path\n    }\n\n    #[inline]\n    pub(crate) fn is_resource_path_matched(&self) -> bool {\n        self.inner.resource_path_matched\n    }\n\n    /// The resource definition pattern that matched the path. Useful for logging and metrics.\n    ///\n    /// For example, when a resource with pattern `/user/{id}/profile` is defined and a call is made\n    /// to `/user/123/profile` this function would return `Some(\"/user/{id}/profile\")`.\n    ///\n    /// Returns a None when no resource is fully matched, including default services.\n    #[inline]\n    pub fn match_pattern(&self) -> Option<String> {\n        if self.is_resource_path_matched() {\n            if let Some(pattern) = self\n                .resource_map()\n                .match_pattern_by_resource_path(self.resource_path())\n            {\n                return Some(pattern);\n            }\n        }\n\n        self.resource_map().match_pattern(self.path())\n    }\n\n    /// The resource name that matched the path. Useful for logging and metrics.\n    ///\n    /// Returns a None when no resource is fully matched, including default services.\n    #[inline]\n    pub fn match_name(&self) -> Option<&str> {\n        if self.is_resource_path_matched() {\n            if let Some(name) = self\n                .resource_map()\n                .match_name_by_resource_path(self.resource_path())\n            {\n                return Some(name);\n            }\n        }\n\n        self.resource_map().match_name(self.path())\n    }\n\n    /// Returns a reference a piece of connection data set in an [on-connect] callback.\n    ///\n    /// ```ignore\n    /// let opt_t = req.conn_data::<PeerCertificate>();\n    /// ```\n    ///\n    /// [on-connect]: crate::HttpServer::on_connect\n    pub fn conn_data<T: 'static>(&self) -> Option<&T> {\n        self.inner\n            .conn_data\n            .as_deref()\n            .and_then(|container| container.get::<T>())\n    }\n\n    /// Generates URL for a named resource.\n    ///\n    /// This substitutes in sequence all URL parameters that appear in the resource itself and in\n    /// parent [scopes](crate::web::scope), if any.\n    ///\n    /// It is worth noting that the characters `['/', '%']` are not escaped and therefore a single\n    /// URL parameter may expand into multiple path segments and `elements` can be percent-encoded\n    /// beforehand without worrying about double encoding. Any other character that is not valid in\n    /// a URL path context is escaped using percent-encoding.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::{web, App, HttpRequest, HttpResponse};\n    /// fn index(req: HttpRequest) -> HttpResponse {\n    ///     let url = req.url_for(\"foo\", &[\"1\", \"2\", \"3\"]); // <- generate URL for \"foo\" resource\n    ///     HttpResponse::Ok().into()\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(web::resource(\"/test/{one}/{two}/{three}\")\n    ///          .name(\"foo\")  // <- set resource name so it can be used in `url_for`\n    ///          .route(web::get().to(|| HttpResponse::Ok()))\n    ///     );\n    /// ```\n    pub fn url_for<U, I>(&self, name: &str, elements: U) -> Result<url::Url, UrlGenerationError>\n    where\n        U: IntoIterator<Item = I>,\n        I: AsRef<str>,\n    {\n        self.resource_map().url_for(self, name, elements)\n    }\n\n    /// Generates URL for a named resource using a map of dynamic segment values.\n    ///\n    /// This substitutes URL parameters by name from `elements`, including parameters from parent\n    /// scopes.\n    ///\n    /// # Examples\n    /// ```\n    /// # use std::collections::HashMap;\n    /// # use actix_web::{web, App, HttpRequest, HttpResponse};\n    /// fn index(req: HttpRequest) -> HttpResponse {\n    ///     let mut params = HashMap::new();\n    ///     params.insert(\"one\", \"1\");\n    ///     params.insert(\"two\", \"2\");\n    ///     let url = req.url_for_map(\"foo\", &params); // <- generate URL for \"foo\" resource\n    ///     HttpResponse::Ok().into()\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(web::resource(\"/test/{one}/{two}\")\n    ///          .name(\"foo\")  // <- set resource name so it can be used in `url_for_map`\n    ///          .route(web::get().to(|| HttpResponse::Ok()))\n    ///     );\n    /// ```\n    pub fn url_for_map<K, V, S>(\n        &self,\n        name: &str,\n        elements: &HashMap<K, V, S>,\n    ) -> Result<url::Url, UrlGenerationError>\n    where\n        K: std::borrow::Borrow<str> + Eq + Hash,\n        V: AsRef<str>,\n        S: BuildHasher,\n    {\n        self.resource_map().url_for_map(self, name, elements)\n    }\n\n    /// Generates URL for a named resource using an iterator of key-value pairs.\n    ///\n    /// This is a convenience wrapper around [`HttpRequest::url_for_map`].\n    ///\n    /// Note: passing a borrowed map (e.g. `&HashMap<String, String>`) directly does not satisfy the\n    /// trait bounds because the iterator yields `(&String, &String)`. Prefer `url_for_map` for\n    /// borrowed maps, or map entries to `&str`:\n    ///\n    /// ```\n    /// # use std::collections::HashMap;\n    /// # use actix_web::{web, App, HttpRequest, HttpResponse};\n    /// fn index(req: HttpRequest) -> HttpResponse {\n    ///     let mut params = HashMap::new();\n    ///     params.insert(\"one\".to_string(), \"1\".to_string());\n    ///     params.insert(\"two\".to_string(), \"2\".to_string());\n    ///\n    ///     let iter = params.iter().map(|(k, v)| (k.as_str(), v.as_str()));\n    ///     let url = req.url_for_iter(\"foo\", iter);\n    ///     HttpResponse::Ok().into()\n    /// }\n    /// ```\n    pub fn url_for_iter<K, V, I>(\n        &self,\n        name: &str,\n        elements: I,\n    ) -> Result<url::Url, UrlGenerationError>\n    where\n        I: IntoIterator<Item = (K, V)>,\n        K: std::borrow::Borrow<str> + Eq + Hash,\n        V: AsRef<str>,\n    {\n        self.resource_map().url_for_iter(self, name, elements)\n    }\n\n    /// Generate URL for named resource\n    ///\n    /// This method is similar to `HttpRequest::url_for()` but it can be used\n    /// for urls that do not contain variable parts.\n    pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {\n        const NO_PARAMS: [&str; 0] = [];\n        self.url_for(name, NO_PARAMS)\n    }\n\n    /// Get a reference to a `ResourceMap` of current application.\n    #[inline]\n    pub fn resource_map(&self) -> &ResourceMap {\n        self.app_state().rmap()\n    }\n\n    /// Returns peer socket address.\n    ///\n    /// Peer address is the directly connected peer's socket address. If a proxy is used in front of\n    /// the Actix Web server, then it would be address of this proxy.\n    ///\n    /// For expanded client connection information, use [`connection_info`] instead.\n    ///\n    /// Will only return `None` when server is listening on [UDS socket] or when called in unit\n    /// tests unless [`TestRequest::peer_addr`] is used.\n    ///\n    /// [UDS socket]: crate::HttpServer::bind_uds\n    /// [`TestRequest::peer_addr`]: crate::test::TestRequest::peer_addr\n    /// [`connection_info`]: Self::connection_info\n    #[inline]\n    pub fn peer_addr(&self) -> Option<net::SocketAddr> {\n        self.head().peer_addr\n    }\n\n    /// Returns connection info for the current request.\n    ///\n    /// The return type, [`ConnectionInfo`], can also be used as an extractor.\n    ///\n    /// # Panics\n    /// Panics if request's extensions container is already borrowed.\n    #[inline]\n    pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {\n        if !self.extensions().contains::<ConnectionInfo>() {\n            let info = ConnectionInfo::new(self.head(), self.app_config());\n            self.extensions_mut().insert(info);\n        }\n\n        Ref::map(self.extensions(), |data| data.get().unwrap())\n    }\n\n    /// Returns a reference to the application's connection configuration.\n    #[inline]\n    pub fn app_config(&self) -> &AppConfig {\n        self.app_state().config()\n    }\n\n    /// Retrieves a piece of application state.\n    ///\n    /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the\n    /// counterpart methods on [`Scope`](crate::Scope::app_data) and\n    /// [`Resource`](crate::Resource::app_data)) during application configuration.\n    ///\n    /// Since the Actix Web router layers application data, the returned object will reference the\n    /// \"closest\" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`\n    /// also stores a `u32`, and the delegated request handler falls within that `Scope`, then\n    /// calling `.app_data::<u32>()` on an `HttpRequest` within that handler will return the\n    /// `Scope`'s instance. However, using the same router set up and a request that does not get\n    /// captured by the `Scope`, `.app_data::<u32>()` would return the `App`'s instance.\n    ///\n    /// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using\n    /// this same type.\n    ///\n    /// See also the [`Data`] extractor.\n    ///\n    /// # Examples\n    /// ```no_run\n    /// # use actix_web::{test::TestRequest, web::Data};\n    /// # let req = TestRequest::default().to_http_request();\n    /// # type T = u32;\n    /// let opt_t: Option<&Data<T>> = req.app_data::<Data<T>>();\n    /// ```\n    ///\n    /// [`Data`]: crate::web::Data\n    #[doc(alias = \"state\")]\n    pub fn app_data<T: 'static>(&self) -> Option<&T> {\n        for container in self.inner.app_data.iter().rev() {\n            if let Some(data) = container.get::<T>() {\n                return Some(data);\n            }\n        }\n\n        None\n    }\n\n    #[inline]\n    fn app_state(&self) -> &AppInitServiceState {\n        &self.inner.app_state\n    }\n\n    /// Load request cookies.\n    ///\n    /// Any cookie that cannot be parsed is omitted from the result.\n    /// This includes cookies with an empty name (e.g. `document.cookie = \"=value\"`).\n    #[cfg(feature = \"cookies\")]\n    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {\n        use actix_http::header::COOKIE;\n\n        if self.extensions().get::<Cookies>().is_none() {\n            let mut cookies = Vec::new();\n            for hdr in self.headers().get_all(COOKIE) {\n                let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;\n                for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {\n                    if let Ok(cookie) = Cookie::parse_encoded(cookie_str) {\n                        cookies.push(cookie.into_owned());\n                    }\n                }\n            }\n            self.extensions_mut().insert(Cookies(cookies));\n        }\n\n        Ok(Ref::map(self.extensions(), |ext| {\n            &ext.get::<Cookies>().unwrap().0\n        }))\n    }\n\n    /// Return request cookie.\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {\n        if let Ok(cookies) = self.cookies() {\n            for cookie in cookies.iter() {\n                if cookie.name() == name {\n                    return Some(cookie.to_owned());\n                }\n            }\n        }\n        None\n    }\n}\n\nimpl HttpMessage for HttpRequest {\n    type Stream = ();\n\n    #[inline]\n    fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    #[inline]\n    fn extensions(&self) -> Ref<'_, Extensions> {\n        self.inner.extensions.borrow()\n    }\n\n    #[inline]\n    fn extensions_mut(&self) -> RefMut<'_, Extensions> {\n        self.inner.extensions.borrow_mut()\n    }\n\n    #[inline]\n    fn take_payload(&mut self) -> Payload<Self::Stream> {\n        Payload::None\n    }\n}\n\nimpl Drop for HttpRequest {\n    fn drop(&mut self) {\n        // if possible, contribute to current worker's HttpRequest allocation pool\n\n        // This relies on no weak references to inner existing anywhere within the codebase.\n        if let Some(inner) = Rc::get_mut(&mut self.inner) {\n            if inner.app_state.pool().is_available() {\n                // clear additional app_data and keep the root one for reuse.\n                inner.app_data.truncate(1);\n\n                // Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also\n                // we know the req_data Rc will not have any clones at this point to unwrap is okay.\n                Rc::get_mut(&mut inner.extensions)\n                    .unwrap()\n                    .get_mut()\n                    .clear();\n\n                // We can't use the same trick as req data because the conn_data is held by the\n                // dispatcher, too.\n                inner.conn_data = None;\n\n                // a re-borrow of pool is necessary here.\n                let req = Rc::clone(&self.inner);\n                self.app_state().pool().push(req);\n            }\n        }\n    }\n}\n\n/// It is possible to get `HttpRequest` as an extractor handler parameter\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App, HttpRequest};\n/// use serde::Deserialize;\n///\n/// /// extract `Thing` from request\n/// async fn index(req: HttpRequest) -> String {\n///    format!(\"Got thing: {:?}\", req)\n/// }\n///\n/// let app = App::new().service(\n///     web::resource(\"/users/{first}\").route(\n///         web::get().to(index))\n/// );\n/// ```\nimpl FromRequest for HttpRequest {\n    type Error = Error;\n    type Future = Ready<Result<Self, Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        ok(req.clone())\n    }\n}\n\nimpl fmt::Debug for HttpRequest {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(\n            f,\n            \"\\nHttpRequest {:?} {}:{}\",\n            self.inner.head.version,\n            self.inner.head.method,\n            self.path()\n        )?;\n\n        if !self.query_string().is_empty() {\n            writeln!(f, \"  query: ?{:?}\", self.query_string())?;\n        }\n\n        if !self.match_info().is_empty() {\n            writeln!(f, \"  params: {:?}\", self.match_info())?;\n        }\n\n        writeln!(f, \"  headers:\")?;\n\n        for (key, val) in self.headers().iter() {\n            match key {\n                // redact sensitive header values from debug output\n                &crate::http::header::AUTHORIZATION\n                | &crate::http::header::PROXY_AUTHORIZATION\n                | &crate::http::header::COOKIE => writeln!(f, \"    {:?}: {:?}\", key, \"*redacted*\")?,\n\n                _ => writeln!(f, \"    {:?}: {:?}\", key, val)?,\n            }\n        }\n\n        Ok(())\n    }\n}\n\n/// Slab-allocated `HttpRequest` Pool\n///\n/// Since request processing may yield for asynchronous events to complete, a worker may have many\n/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory\n/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.\n///\n/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used\n/// in `<AppInitService as Service>::call` when there are available objects in the list.\n///\n/// The pool's default capacity is 128 items.\npub(crate) struct HttpRequestPool {\n    inner: RefCell<Vec<Rc<HttpRequestInner>>>,\n    cap: usize,\n}\n\nimpl Default for HttpRequestPool {\n    fn default() -> Self {\n        Self::with_capacity(128)\n    }\n}\n\nimpl HttpRequestPool {\n    pub(crate) fn with_capacity(cap: usize) -> Self {\n        HttpRequestPool {\n            inner: RefCell::new(Vec::with_capacity(cap)),\n            cap,\n        }\n    }\n\n    /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.\n    #[inline]\n    pub(crate) fn pop(&self) -> Option<HttpRequest> {\n        self.inner\n            .borrow_mut()\n            .pop()\n            .map(|inner| HttpRequest { inner })\n    }\n\n    /// Check if the pool still has capacity for request storage.\n    #[inline]\n    pub(crate) fn is_available(&self) -> bool {\n        self.inner.borrow_mut().len() < self.cap\n    }\n\n    /// Push a request to pool.\n    #[inline]\n    pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {\n        self.inner.borrow_mut().push(req);\n    }\n\n    /// Clears all allocated HttpRequest objects.\n    pub(crate) fn clear(&self) {\n        self.inner.borrow_mut().clear()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::collections::HashMap;\n\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::{\n        dev::{ResourceDef, Service},\n        guard,\n        http::{header, StatusCode},\n        test::{self, call_service, init_service, read_body, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    #[test]\n    fn test_debug() {\n        let req = TestRequest::default()\n            .insert_header((\"content-type\", \"text/plain\"))\n            .to_http_request();\n        let dbg = format!(\"{:?}\", req);\n        assert!(dbg.contains(\"HttpRequest\"));\n    }\n\n    #[test]\n    #[cfg(feature = \"cookies\")]\n    fn test_no_request_cookies() {\n        let req = TestRequest::default().to_http_request();\n        assert!(req.cookies().unwrap().is_empty());\n    }\n\n    #[test]\n    #[cfg(feature = \"cookies\")]\n    fn test_request_cookies() {\n        let req = TestRequest::default()\n            .append_header((header::COOKIE, \"cookie1=value1\"))\n            .append_header((header::COOKIE, \"cookie2=value2\"))\n            .to_http_request();\n        {\n            let cookies = req.cookies().unwrap();\n            assert_eq!(cookies.len(), 2);\n            assert_eq!(cookies[0].name(), \"cookie1\");\n            assert_eq!(cookies[0].value(), \"value1\");\n            assert_eq!(cookies[1].name(), \"cookie2\");\n            assert_eq!(cookies[1].value(), \"value2\");\n        }\n\n        let cookie = req.cookie(\"cookie1\");\n        assert!(cookie.is_some());\n        let cookie = cookie.unwrap();\n        assert_eq!(cookie.name(), \"cookie1\");\n        assert_eq!(cookie.value(), \"value1\");\n\n        let cookie = req.cookie(\"cookie-unknown\");\n        assert!(cookie.is_none());\n    }\n\n    #[test]\n    #[cfg(feature = \"cookies\")]\n    fn test_empty_key() {\n        let req = TestRequest::default()\n            .append_header((header::COOKIE, \"cookie1=value1; value2; cookie3=value3\"))\n            .to_http_request();\n        {\n            let cookies = req.cookies().unwrap();\n            assert_eq!(cookies.len(), 2);\n            assert_eq!(cookies[0].name(), \"cookie1\");\n            assert_eq!(cookies[0].value(), \"value1\");\n            assert_eq!(cookies[1].name(), \"cookie3\");\n            assert_eq!(cookies[1].value(), \"value3\");\n        }\n    }\n\n    #[test]\n    fn test_request_query() {\n        let req = TestRequest::with_uri(\"/?id=test\").to_http_request();\n        assert_eq!(req.query_string(), \"id=test\");\n    }\n\n    #[test]\n    fn test_url_for() {\n        let mut res = ResourceDef::new(\"/user/{name}.{ext}\");\n        res.set_name(\"index\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut res, None);\n        assert!(rmap.has_resource(\"/user/test.html\"));\n        assert!(!rmap.has_resource(\"/test/unknown\"));\n\n        let req = TestRequest::default()\n            .insert_header((header::HOST, \"www.rust-lang.org\"))\n            .rmap(rmap)\n            .to_http_request();\n\n        assert_eq!(\n            req.url_for(\"unknown\", [\"test\"]),\n            Err(UrlGenerationError::ResourceNotFound)\n        );\n        assert_eq!(\n            req.url_for(\"index\", [\"test\"]),\n            Err(UrlGenerationError::NotEnoughElements)\n        );\n        let url = req.url_for(\"index\", [\"test\", \"html\"]);\n        assert_eq!(\n            url.ok().unwrap().as_str(),\n            \"http://www.rust-lang.org/user/test.html\"\n        );\n    }\n\n    #[test]\n    fn test_url_for_map() {\n        let mut res = ResourceDef::new(\"/user/{name}.{ext}\");\n        res.set_name(\"index\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut res, None);\n\n        let req = TestRequest::default()\n            .insert_header((header::HOST, \"www.actix.rs\"))\n            .rmap(rmap)\n            .to_http_request();\n\n        let mut params = HashMap::new();\n        params.insert(\"name\", \"test\");\n        params.insert(\"ext\", \"html\");\n\n        let url = req.url_for_map(\"index\", &params);\n        assert_eq!(\n            url.ok().unwrap().as_str(),\n            \"http://www.actix.rs/user/test.html\"\n        );\n\n        params.remove(\"ext\");\n        assert_eq!(\n            req.url_for_map(\"index\", &params),\n            Err(UrlGenerationError::NotEnoughElements)\n        );\n    }\n\n    #[test]\n    fn test_url_for_iter() {\n        let mut res = ResourceDef::new(\"/user/{name}.{ext}\");\n        res.set_name(\"index\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut res, None);\n\n        let req = TestRequest::default()\n            .insert_header((header::HOST, \"www.actix.rs\"))\n            .rmap(rmap)\n            .to_http_request();\n\n        let url = req.url_for_iter(\"index\", [(\"ext\", \"html\"), (\"name\", \"test\")]);\n        assert_eq!(\n            url.ok().unwrap().as_str(),\n            \"http://www.actix.rs/user/test.html\"\n        );\n\n        let url = req.url_for_iter(\"index\", [(\"name\", \"test\")]);\n        assert_eq!(url, Err(UrlGenerationError::NotEnoughElements));\n    }\n\n    #[test]\n    fn test_url_for_static() {\n        let mut rdef = ResourceDef::new(\"/index.html\");\n        rdef.set_name(\"index\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut rdef, None);\n\n        assert!(rmap.has_resource(\"/index.html\"));\n\n        let req = TestRequest::with_uri(\"/test\")\n            .insert_header((header::HOST, \"www.rust-lang.org\"))\n            .rmap(rmap)\n            .to_http_request();\n        let url = req.url_for_static(\"index\");\n        assert_eq!(\n            url.ok().unwrap().as_str(),\n            \"http://www.rust-lang.org/index.html\"\n        );\n    }\n\n    #[test]\n    fn test_match_name() {\n        let mut rdef = ResourceDef::new(\"/index.html\");\n        rdef.set_name(\"index\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut rdef, None);\n\n        assert!(rmap.has_resource(\"/index.html\"));\n\n        let req = TestRequest::default()\n            .uri(\"/index.html\")\n            .rmap(rmap)\n            .to_http_request();\n\n        assert_eq!(req.match_name(), Some(\"index\"));\n    }\n\n    #[test]\n    fn test_url_for_external() {\n        let mut rdef = ResourceDef::new(\"https://youtube.com/watch/{video_id}\");\n\n        rdef.set_name(\"youtube\");\n\n        let mut rmap = ResourceMap::new(ResourceDef::prefix(\"\"));\n        rmap.add(&mut rdef, None);\n\n        let req = TestRequest::default().rmap(rmap).to_http_request();\n        let url = req.url_for(\"youtube\", [\"oHg5SJYRHA0\"]);\n        assert_eq!(\n            url.ok().unwrap().as_str(),\n            \"https://youtube.com/watch/oHg5SJYRHA0\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_drop_http_request_pool() {\n        let srv = init_service(\n            App::new().service(web::resource(\"/\").to(|req: HttpRequest| {\n                HttpResponse::Ok()\n                    .insert_header((\"pool_cap\", req.app_state().pool().cap))\n                    .finish()\n            })),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = call_service(&srv, req).await;\n\n        drop(srv);\n\n        assert_eq!(resp.headers().get(\"pool_cap\").unwrap(), \"128\");\n    }\n\n    #[actix_rt::test]\n    async fn test_data() {\n        let srv = init_service(App::new().app_data(10usize).service(web::resource(\"/\").to(\n            |req: HttpRequest| {\n                if req.app_data::<usize>().is_some() {\n                    HttpResponse::Ok()\n                } else {\n                    HttpResponse::BadRequest()\n                }\n            },\n        )))\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let srv = init_service(App::new().app_data(10u32).service(web::resource(\"/\").to(\n            |req: HttpRequest| {\n                if req.app_data::<usize>().is_some() {\n                    HttpResponse::Ok()\n                } else {\n                    HttpResponse::BadRequest()\n                }\n            },\n        )))\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[actix_rt::test]\n    async fn test_cascading_data() {\n        #[allow(dead_code)]\n        fn echo_usize(req: HttpRequest) -> HttpResponse {\n            let num = req.app_data::<usize>().unwrap();\n            HttpResponse::Ok().body(num.to_string())\n        }\n\n        let srv = init_service(\n            App::new()\n                .app_data(88usize)\n                .service(web::resource(\"/\").route(web::get().to(echo_usize)))\n                .service(\n                    web::resource(\"/one\")\n                        .app_data(1u32)\n                        .route(web::get().to(echo_usize)),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"88\"));\n\n        let req = TestRequest::get().uri(\"/one\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"88\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_overwrite_data() {\n        #[allow(dead_code)]\n        fn echo_usize(req: HttpRequest) -> HttpResponse {\n            let num = req.app_data::<usize>().unwrap();\n            HttpResponse::Ok().body(num.to_string())\n        }\n\n        let srv = init_service(\n            App::new()\n                .app_data(88usize)\n                .service(web::resource(\"/\").route(web::get().to(echo_usize)))\n                .service(\n                    web::resource(\"/one\")\n                        .app_data(1usize)\n                        .route(web::get().to(echo_usize)),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"88\"));\n\n        let req = TestRequest::get().uri(\"/one\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"1\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_app_data_dropped() {\n        struct Tracker {\n            pub dropped: bool,\n        }\n        struct Foo {\n            tracker: Rc<RefCell<Tracker>>,\n        }\n        impl Drop for Foo {\n            fn drop(&mut self) {\n                self.tracker.borrow_mut().dropped = true;\n            }\n        }\n\n        let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));\n        {\n            let tracker2 = Rc::clone(&tracker);\n            let srv = init_service(App::new().service(web::resource(\"/\").to(\n                move |req: HttpRequest| {\n                    req.extensions_mut().insert(Foo {\n                        tracker: Rc::clone(&tracker2),\n                    });\n                    HttpResponse::Ok()\n                },\n            )))\n            .await;\n\n            let req = TestRequest::default().to_request();\n            let resp = call_service(&srv, req).await;\n            assert_eq!(resp.status(), StatusCode::OK);\n        }\n\n        assert!(tracker.borrow().dropped);\n    }\n\n    #[actix_rt::test]\n    async fn extract_path_pattern() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/user/{id}\")\n                    .service(web::resource(\"/profile\").route(web::get().to(\n                        move |req: HttpRequest| {\n                            assert_eq!(req.match_pattern(), Some(\"/user/{id}/profile\".to_owned()));\n\n                            HttpResponse::Ok().finish()\n                        },\n                    )))\n                    .default_service(web::to(move |req: HttpRequest| {\n                        assert!(req.match_pattern().is_none());\n                        HttpResponse::Ok().finish()\n                    })),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/user/22/profile\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let req = TestRequest::get().uri(\"/user/22/not-exist\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn extract_path_pattern_with_guards() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/widgets\")\n                    .service(\n                        web::resource(\"/{id}\")\n                            .name(\"get_widget\")\n                            .guard(guard::Get())\n                            .to(|req: HttpRequest| {\n                                assert_eq!(req.match_pattern(), Some(\"/widgets/{id}\".to_owned()));\n                                assert_eq!(req.match_name(), Some(\"get_widget\"));\n                                HttpResponse::Ok().finish()\n                            }),\n                    )\n                    .service(\n                        web::resource(\"/action\")\n                            .name(\"widget_action\")\n                            .guard(guard::Post())\n                            .to(|req: HttpRequest| {\n                                assert_eq!(req.match_pattern(), Some(\"/widgets/action\".to_owned()));\n                                assert_eq!(req.match_name(), Some(\"widget_action\"));\n                                HttpResponse::Ok().finish()\n                            }),\n                    ),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/widgets/42\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let req = TestRequest::post().uri(\"/widgets/action\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn extract_path_pattern_complex() {\n        let srv = init_service(\n            App::new()\n                .service(web::scope(\"/user\").service(web::scope(\"/{id}\").service(\n                    web::resource(\"\").to(move |req: HttpRequest| {\n                        assert_eq!(req.match_pattern(), Some(\"/user/{id}\".to_owned()));\n\n                        HttpResponse::Ok().finish()\n                    }),\n                )))\n                .service(web::resource(\"/\").to(move |req: HttpRequest| {\n                    assert_eq!(req.match_pattern(), Some(\"/\".to_owned()));\n\n                    HttpResponse::Ok().finish()\n                }))\n                .default_service(web::to(move |req: HttpRequest| {\n                    assert!(req.match_pattern().is_none());\n                    HttpResponse::Ok().finish()\n                })),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/user/test\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let req = TestRequest::get().uri(\"/\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let req = TestRequest::get().uri(\"/not-exist\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn url_for_closest_named_resource() {\n        // we mount the route named 'nested' on 2 different scopes, 'a' and 'b'\n        let srv = test::init_service(\n            App::new()\n                .service(\n                    web::scope(\"/foo\")\n                        .service(web::resource(\"/nested\").name(\"nested\").route(web::get().to(\n                            |req: HttpRequest| {\n                                HttpResponse::Ok()\n                                    .body(format!(\"{}\", req.url_for_static(\"nested\").unwrap()))\n                            },\n                        )))\n                        .service(web::scope(\"/baz\").service(web::resource(\"deep\")))\n                        .service(web::resource(\"{foo_param}\")),\n                )\n                .service(web::scope(\"/bar\").service(\n                    web::resource(\"/nested\").name(\"nested\").route(web::get().to(\n                        |req: HttpRequest| {\n                            HttpResponse::Ok()\n                                .body(format!(\"{}\", req.url_for_static(\"nested\").unwrap()))\n                        },\n                    )),\n                )),\n        )\n        .await;\n\n        let foo_resp =\n            test::call_service(&srv, TestRequest::with_uri(\"/foo/nested\").to_request()).await;\n        assert_eq!(foo_resp.status(), StatusCode::OK);\n        let body = read_body(foo_resp).await;\n        // `body` equals http://localhost:8080/bar/nested\n        // because nested from /bar overrides /foo's\n        // to do this any other way would require something like a custom tree search\n        // see https://github.com/actix/actix-web/issues/1763\n        assert_eq!(body, \"http://localhost:8080/bar/nested\");\n\n        let bar_resp =\n            test::call_service(&srv, TestRequest::with_uri(\"/bar/nested\").to_request()).await;\n        assert_eq!(bar_resp.status(), StatusCode::OK);\n        let body = read_body(bar_resp).await;\n        assert_eq!(body, \"http://localhost:8080/bar/nested\");\n    }\n\n    #[test]\n    fn authorization_header_hidden_in_debug() {\n        let authorization_header = \"Basic bXkgdXNlcm5hbWU6bXkgcGFzc3dvcmQK\";\n        let req = TestRequest::get()\n            .insert_header((crate::http::header::AUTHORIZATION, authorization_header))\n            .to_http_request();\n\n        assert!(!format!(\"{:?}\", req).contains(authorization_header));\n    }\n\n    #[test]\n    fn proxy_authorization_header_hidden_in_debug() {\n        let proxy_authorization_header = \"secret value\";\n        let req = TestRequest::get()\n            .insert_header((\n                crate::http::header::PROXY_AUTHORIZATION,\n                proxy_authorization_header,\n            ))\n            .to_http_request();\n\n        assert!(!format!(\"{:?}\", req).contains(proxy_authorization_header));\n    }\n\n    #[test]\n    fn cookie_header_hidden_in_debug() {\n        let cookie_header = \"secret\";\n        let req = TestRequest::get()\n            .insert_header((crate::http::header::COOKIE, cookie_header))\n            .to_http_request();\n\n        assert!(!format!(\"{:?}\", req).contains(cookie_header));\n    }\n\n    #[test]\n    fn other_header_visible_in_debug() {\n        let location_header = \"192.0.0.1\";\n        let req = TestRequest::get()\n            .insert_header((crate::http::header::LOCATION, location_header))\n            .to_http_request();\n\n        assert!(format!(\"{:?}\", req).contains(location_header));\n    }\n\n    #[test]\n    fn check_full_url() {\n        let req = TestRequest::with_uri(\"/api?id=4&name=foo\").to_http_request();\n        assert_eq!(\n            req.full_url().as_str(),\n            \"http://localhost:8080/api?id=4&name=foo\",\n        );\n\n        let req = TestRequest::with_uri(\"https://example.com/api?id=4&name=foo\").to_http_request();\n        assert_eq!(\n            req.full_url().as_str(),\n            \"https://example.com/api?id=4&name=foo\",\n        );\n\n        let req = TestRequest::with_uri(\"http://10.1.2.3:8443/api?id=4&name=foo\")\n            .insert_header((\"host\", \"example.com\"))\n            .to_http_request();\n        assert_eq!(\n            req.full_url().as_str(),\n            \"http://example.com/api?id=4&name=foo\",\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/request_data.rs",
    "content": "use std::{any::type_name, ops::Deref};\n\nuse actix_utils::future::{err, ok, Ready};\n\nuse crate::{\n    dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpMessage as _,\n    HttpRequest,\n};\n\n/// Request-local data extractor.\n///\n/// Request-local data is arbitrary data attached to an individual request, usually\n/// by middleware. It can be set via `extensions_mut` on [`HttpRequest`][htr_ext_mut]\n/// or [`ServiceRequest`][srv_ext_mut].\n///\n/// Unlike app data, request data is dropped when the request has finished processing. This makes it\n/// useful as a kind of messaging system between middleware and request handlers. It uses the same\n/// types-as-keys storage system as app data.\n///\n/// # Mutating Request Data\n/// Note that since extractors must output owned data, only types that `impl Clone` can use this\n/// extractor. A clone is taken of the required request data and can, therefore, not be directly\n/// mutated in-place. To mutate request data, continue to use [`HttpRequest::extensions_mut`] or\n/// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not\n/// provided to make this potential foot-gun more obvious.\n///\n/// # Examples\n/// ```no_run\n/// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _};\n/// #[derive(Debug, Clone, PartialEq)]\n/// struct FlagFromMiddleware(String);\n///\n/// /// Use the `ReqData<T>` extractor to access request data in a handler.\n/// async fn handler(\n///     req: HttpRequest,\n///     opt_flag: Option<web::ReqData<FlagFromMiddleware>>,\n/// ) -> impl Responder {\n///     // use an option extractor if middleware is not guaranteed to add this type of req data\n///     if let Some(flag) = opt_flag {\n///         assert_eq!(&flag.into_inner(), req.extensions().get::<FlagFromMiddleware>().unwrap());\n///     }\n///\n///     HttpResponse::Ok()\n/// }\n/// ```\n///\n/// [htr_ext_mut]: crate::HttpRequest::extensions_mut\n/// [srv_ext_mut]: crate::dev::ServiceRequest::extensions_mut\n#[derive(Debug, Clone)]\npub struct ReqData<T: Clone + 'static>(T);\n\nimpl<T: Clone + 'static> ReqData<T> {\n    /// Consumes the `ReqData`, returning its wrapped data.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T: Clone + 'static> Deref for ReqData<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T: Clone + 'static> FromRequest for ReqData<T> {\n    type Error = Error;\n    type Future = Ready<Result<Self, Error>>;\n\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        if let Some(st) = req.extensions().get::<T>() {\n            ok(ReqData(st.clone()))\n        } else {\n            log::debug!(\n                \"Failed to construct App-level ReqData extractor. \\\n                 Request path: {:?} (type: {})\",\n                req.path(),\n                type_name::<T>(),\n            );\n            err(ErrorInternalServerError(\n                \"Missing expected request extension data\",\n            ))\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{cell::RefCell, rc::Rc};\n\n    use futures_util::TryFutureExt as _;\n\n    use super::*;\n    use crate::{\n        dev::Service,\n        http::{Method, StatusCode},\n        test::{init_service, TestRequest},\n        web, App, HttpMessage, HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn req_data_extractor() {\n        let srv = init_service(\n            App::new()\n                .wrap_fn(|req, srv| {\n                    if req.method() == Method::POST {\n                        req.extensions_mut().insert(42u32);\n                    }\n\n                    srv.call(req)\n                })\n                .service(web::resource(\"/test\").to(\n                    |req: HttpRequest, data: Option<ReqData<u32>>| {\n                        if req.method() != Method::POST {\n                            assert!(data.is_none());\n                        }\n\n                        if let Some(data) = data {\n                            assert_eq!(*data, 42);\n                            assert_eq!(\n                                Some(data.into_inner()),\n                                req.extensions().get::<u32>().copied()\n                            );\n                        }\n\n                        HttpResponse::Ok()\n                    },\n                )),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::post().uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn req_data_internal_mutability() {\n        let srv = init_service(\n            App::new()\n                .wrap_fn(|req, srv| {\n                    let data_before = Rc::new(RefCell::new(42u32));\n                    req.extensions_mut().insert(data_before);\n\n                    srv.call(req).map_ok(|res| {\n                        {\n                            let ext = res.request().extensions();\n                            let data_after = ext.get::<Rc<RefCell<u32>>>().unwrap();\n                            assert_eq!(*data_after.borrow(), 53u32);\n                        }\n\n                        res\n                    })\n                })\n                .default_service(web::to(|data: ReqData<Rc<RefCell<u32>>>| {\n                    assert_eq!(*data.borrow(), 42);\n                    *data.borrow_mut() += 11;\n                    assert_eq!(*data.borrow(), 53);\n\n                    HttpResponse::Ok()\n                })),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/resource.rs",
    "content": "use std::{cell::RefCell, fmt, future::Future, rc::Rc};\n\nuse actix_http::Extensions;\nuse actix_router::{IntoPatterns, Patterns};\nuse actix_service::{\n    apply, apply_fn_factory, boxed, fn_service, IntoServiceFactory, Service, ServiceFactory,\n    ServiceFactoryExt, Transform,\n};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::future::join_all;\n\nuse crate::{\n    body::MessageBody,\n    data::Data,\n    dev::{ensure_leading_slash, AppService, ResourceDef},\n    guard::{self, Guard},\n    handler::Handler,\n    http::header,\n    route::{Route, RouteService},\n    service::{\n        BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest,\n        ServiceResponse,\n    },\n    web, Error, FromRequest, HttpResponse, Responder,\n};\n\n/// A collection of [`Route`]s that respond to the same path pattern.\n///\n/// Resource in turn has at least one route. Route consists of an handlers objects and list of\n/// guards (objects that implement `Guard` trait). Resources and routes uses builder-like pattern\n/// for configuration. During request handling, the resource object iterates through all routes\n/// and checks guards for the specific route, if the request matches all the guards, then the route\n/// is considered matched and the route handler gets called.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App, HttpResponse};\n///\n/// let app = App::new().service(\n///     web::resource(\"/\")\n///         .get(|| HttpResponse::Ok())\n///         .post(|| async { \"Hello World!\" })\n/// );\n/// ```\n///\n/// If no matching route is found, an empty 405 response is returned which includes an\n/// [appropriate Allow header][RFC 9110 §15.5.6]. This default behavior can be overridden using\n/// [`default_service()`](Self::default_service).\n///\n/// [RFC 9110 §15.5.6]: https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.6\npub struct Resource<T = ResourceEndpoint> {\n    endpoint: T,\n    rdef: Patterns,\n    name: Option<String>,\n    routes: Vec<Route>,\n    app_data: Option<Extensions>,\n    guards: Vec<Box<dyn Guard>>,\n    default: BoxedHttpServiceFactory,\n    factory_ref: Rc<RefCell<Option<ResourceFactory>>>,\n}\n\nimpl Resource {\n    /// Constructs new resource that matches a `path` pattern.\n    pub fn new<T: IntoPatterns>(path: T) -> Resource {\n        let factory_ref = Rc::new(RefCell::new(None));\n\n        Resource {\n            routes: Vec::new(),\n            rdef: path.patterns(),\n            name: None,\n            endpoint: ResourceEndpoint::new(Rc::clone(&factory_ref)),\n            factory_ref,\n            guards: Vec::new(),\n            app_data: None,\n            default: boxed::factory(fn_service(|req: ServiceRequest| async {\n                use crate::HttpMessage as _;\n\n                let allowed = req.extensions().get::<guard::RegisteredMethods>().cloned();\n\n                if let Some(methods) = allowed {\n                    Ok(req.into_response(\n                        HttpResponse::MethodNotAllowed()\n                            .insert_header(header::Allow(methods.0))\n                            .finish(),\n                    ))\n                } else {\n                    Ok(req.into_response(HttpResponse::MethodNotAllowed()))\n                }\n            })),\n        }\n    }\n}\n\nimpl<T> Resource<T>\nwhere\n    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,\n{\n    /// Set resource name.\n    ///\n    /// Name is used for url generation.\n    pub fn name(mut self, name: &str) -> Self {\n        self.name = Some(name.to_string());\n        self\n    }\n\n    /// Add match guard to a resource.\n    ///\n    /// ```\n    /// use actix_web::{web, guard, App, HttpResponse};\n    ///\n    /// async fn index(data: web::Path<(String, String)>) -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(\n    ///         web::resource(\"/app\")\n    ///             .guard(guard::Header(\"content-type\", \"text/plain\"))\n    ///             .route(web::get().to(index))\n    ///     )\n    ///     .service(\n    ///         web::resource(\"/app\")\n    ///             .guard(guard::Header(\"content-type\", \"text/json\"))\n    ///             .route(web::get().to(|| HttpResponse::MethodNotAllowed()))\n    ///     );\n    /// ```\n    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {\n        self.guards.push(Box::new(guard));\n        self\n    }\n\n    pub(crate) fn add_guards(mut self, guards: Vec<Box<dyn Guard>>) -> Self {\n        self.guards.extend(guards);\n        self\n    }\n\n    /// Register a new route.\n    ///\n    /// ```\n    /// use actix_web::{web, guard, App, HttpResponse};\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/\").route(\n    ///         web::route()\n    ///             .guard(guard::Any(guard::Get()).or(guard::Put()))\n    ///             .guard(guard::Header(\"Content-Type\", \"text/plain\"))\n    ///             .to(|| HttpResponse::Ok()))\n    /// );\n    /// ```\n    ///\n    /// Multiple routes could be added to a resource. Resource object uses\n    /// match guards for route selection.\n    ///\n    /// ```\n    /// use actix_web::{web, guard, App};\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/container/\")\n    ///          .route(web::get().to(get_handler))\n    ///          .route(web::post().to(post_handler))\n    ///          .route(web::delete().to(delete_handler))\n    /// );\n    ///\n    /// # async fn get_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }\n    /// # async fn post_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }\n    /// # async fn delete_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() }\n    /// ```\n    pub fn route(mut self, route: Route) -> Self {\n        self.routes.push(route);\n        self\n    }\n\n    /// Add resource data.\n    ///\n    /// Data of different types from parent contexts will still be accessible. Any `Data<T>` types\n    /// set here can be extracted in handlers using the `Data<T>` extractor.\n    ///\n    /// # Examples\n    /// ```\n    /// use std::cell::Cell;\n    /// use actix_web::{web, App, HttpRequest, HttpResponse, Responder};\n    ///\n    /// struct MyData {\n    ///     count: std::cell::Cell<usize>,\n    /// }\n    ///\n    /// async fn handler(req: HttpRequest, counter: web::Data<MyData>) -> impl Responder {\n    ///     // note this cannot use the Data<T> extractor because it was not added with it\n    ///     let incr = *req.app_data::<usize>().unwrap();\n    ///     assert_eq!(incr, 3);\n    ///\n    ///     // update counter using other value from app data\n    ///     counter.count.set(counter.count.get() + incr);\n    ///\n    ///     HttpResponse::Ok().body(counter.count.get().to_string())\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/\")\n    ///         .app_data(3usize)\n    ///         .app_data(web::Data::new(MyData { count: Default::default() }))\n    ///         .route(web::get().to(handler))\n    /// );\n    /// ```\n    #[doc(alias = \"manage\")]\n    pub fn app_data<U: 'static>(mut self, data: U) -> Self {\n        self.app_data\n            .get_or_insert_with(Extensions::new)\n            .insert(data);\n\n        self\n    }\n\n    /// Add resource data after wrapping in `Data<T>`.\n    ///\n    /// Deprecated in favor of [`app_data`](Self::app_data).\n    #[deprecated(since = \"4.0.0\", note = \"Use `.app_data(Data::new(val))` instead.\")]\n    pub fn data<U: 'static>(self, data: U) -> Self {\n        self.app_data(Data::new(data))\n    }\n\n    /// Register a new route and add handler. This route matches all requests.\n    ///\n    /// ```\n    /// use actix_web::{App, HttpRequest, HttpResponse, web};\n    ///\n    /// async fn index(req: HttpRequest) -> HttpResponse {\n    ///     todo!()\n    /// }\n    ///\n    /// App::new().service(web::resource(\"/\").to(index));\n    /// ```\n    ///\n    /// This is shortcut for:\n    ///\n    /// ```\n    /// # use actix_web::*;\n    /// # async fn index(req: HttpRequest) -> HttpResponse { todo!() }\n    /// App::new().service(web::resource(\"/\").route(web::route().to(index)));\n    /// ```\n    pub fn to<F, Args>(mut self, handler: F) -> Self\n    where\n        F: Handler<Args>,\n        Args: FromRequest + 'static,\n        F::Output: Responder + 'static,\n    {\n        self.routes.push(Route::new().to(handler));\n        self\n    }\n\n    /// Registers a resource middleware.\n    ///\n    /// `mw` is a middleware component (type), that can modify the request and response across all\n    /// routes managed by this `Resource`.\n    ///\n    /// See [`App::wrap`](crate::App::wrap) for more details.\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap<M, B>(\n        self,\n        mw: M,\n    ) -> Resource<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        M: Transform<\n                T::Service,\n                ServiceRequest,\n                Response = ServiceResponse<B>,\n                Error = Error,\n                InitError = (),\n            > + 'static,\n        B: MessageBody,\n    {\n        Resource {\n            endpoint: apply(mw, self.endpoint),\n            rdef: self.rdef,\n            name: self.name,\n            guards: self.guards,\n            routes: self.routes,\n            default: self.default,\n            app_data: self.app_data,\n            factory_ref: self.factory_ref,\n        }\n    }\n\n    /// Registers a resource function middleware.\n    ///\n    /// `mw` is a closure that runs during inbound and/or outbound processing in the request\n    /// life-cycle (request -> response), modifying request/response as necessary, across all\n    /// requests handled by the `Resource`.\n    ///\n    /// See [`App::wrap_fn`](crate::App::wrap_fn) for examples and more details.\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap_fn<F, R, B>(\n        self,\n        mw: F,\n    ) -> Resource<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,\n        R: Future<Output = Result<ServiceResponse<B>, Error>>,\n        B: MessageBody,\n    {\n        Resource {\n            endpoint: apply_fn_factory(self.endpoint, mw),\n            rdef: self.rdef,\n            name: self.name,\n            guards: self.guards,\n            routes: self.routes,\n            default: self.default,\n            app_data: self.app_data,\n            factory_ref: self.factory_ref,\n        }\n    }\n\n    /// Sets the default service to be used if no matching route is found.\n    ///\n    /// Unlike [`Scope`]s, a `Resource` does _not_ inherit its parent's default service. You can\n    /// use a [`Route`] as default service.\n    ///\n    /// If a custom default service is not registered, an empty `405 Method Not Allowed` response\n    /// with an appropriate Allow header will be sent instead.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{App, HttpResponse, web};\n    ///\n    /// let resource = web::resource(\"/test\")\n    ///     .route(web::get().to(HttpResponse::Ok))\n    ///     .default_service(web::to(|| {\n    ///         HttpResponse::BadRequest()\n    ///     }));\n    ///\n    /// App::new().service(resource);\n    /// ```\n    ///\n    /// [`Scope`]: crate::Scope\n    pub fn default_service<F, U>(mut self, f: F) -> Self\n    where\n        F: IntoServiceFactory<U, ServiceRequest>,\n        U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>\n            + 'static,\n        U::InitError: fmt::Debug,\n    {\n        // create and configure default resource\n        self.default = boxed::factory(f.into_factory().map_init_err(|err| {\n            log::error!(\"Can not construct default service: {err:?}\");\n        }));\n\n        self\n    }\n}\n\nmacro_rules! route_shortcut {\n    ($method_fn:ident, $method_upper:literal) => {\n        #[doc = concat!(\" Adds a \", $method_upper, \" route.\")]\n        ///\n        /// Use [`route`](Self::route) if you need to add additional guards.\n        ///\n        /// # Examples\n        ///\n        /// ```\n        /// # use actix_web::web;\n        /// web::resource(\"/\")\n        #[doc = concat!(\"    .\", stringify!($method_fn), \"(|| async { \\\"Hello World!\\\" })\")]\n        /// # ;\n        /// ```\n        pub fn $method_fn<F, Args>(self, handler: F) -> Self\n        where\n            F: Handler<Args>,\n            Args: FromRequest + 'static,\n            F::Output: Responder + 'static,\n        {\n            self.route(web::$method_fn().to(handler))\n        }\n    };\n}\n\n/// Concise routes for well-known HTTP methods.\nimpl<T> Resource<T>\nwhere\n    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,\n{\n    route_shortcut!(get, \"GET\");\n    route_shortcut!(post, \"POST\");\n    route_shortcut!(put, \"PUT\");\n    route_shortcut!(patch, \"PATCH\");\n    route_shortcut!(delete, \"DELETE\");\n    route_shortcut!(head, \"HEAD\");\n    route_shortcut!(trace, \"TRACE\");\n}\n\nimpl<T, B> HttpServiceFactory for Resource<T>\nwhere\n    T: ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        > + 'static,\n    B: MessageBody + 'static,\n{\n    fn register(mut self, config: &mut AppService) {\n        let routes = std::mem::take(&mut self.routes);\n\n        let guards = if self.guards.is_empty() {\n            None\n        } else {\n            Some(std::mem::take(&mut self.guards))\n        };\n\n        let mut rdef = if config.is_root() || !self.rdef.is_empty() {\n            ResourceDef::new(ensure_leading_slash(self.rdef.clone()))\n        } else {\n            ResourceDef::new(self.rdef.clone())\n        };\n        #[cfg(feature = \"experimental-introspection\")]\n        {\n            use crate::http::Method;\n\n            let full_paths = crate::introspection::expand_patterns(&config.current_prefix, &rdef);\n            let patterns = rdef\n                .pattern_iter()\n                .map(|pattern| pattern.to_string())\n                .collect::<Vec<_>>();\n            let guards_routes = routes.iter().map(|r| r.guards()).collect::<Vec<_>>();\n            let scope_id = config.scope_id_stack.last().copied();\n            let resource_guards: &[Box<dyn Guard>] = guards.as_deref().unwrap_or(&[]);\n            let resource_name = self.name.clone();\n\n            for route_guards in guards_routes {\n                // Log the guards and methods for introspection\n                let mut guard_names = Vec::new();\n                let mut methods = Vec::new();\n\n                for guard in resource_guards.iter().chain(route_guards.iter()) {\n                    guard_names.push(guard.name());\n                    methods.extend(\n                        guard\n                            .details()\n                            .unwrap_or_default()\n                            .into_iter()\n                            .flat_map(|d| {\n                                if let crate::guard::GuardDetail::HttpMethods(v) = d {\n                                    v.into_iter()\n                                        .filter_map(|s| s.parse::<Method>().ok())\n                                        .collect::<Vec<_>>()\n                                } else {\n                                    Vec::new()\n                                }\n                            }),\n                    );\n                }\n\n                let guard_details = crate::introspection::guard_reports_from_iter(\n                    resource_guards.iter().chain(route_guards.iter()),\n                );\n\n                for full_path in &full_paths {\n                    let info = crate::introspection::RouteInfo::new(\n                        full_path.clone(),\n                        methods.clone(),\n                        guard_names.clone(),\n                        guard_details.clone(),\n                        patterns.clone(),\n                        resource_name.clone(),\n                    );\n                    config\n                        .introspector\n                        .borrow_mut()\n                        .register_route(info, scope_id);\n                }\n            }\n        }\n\n        if let Some(ref name) = self.name {\n            rdef.set_name(name);\n        }\n\n        *self.factory_ref.borrow_mut() = Some(ResourceFactory {\n            routes,\n            default: self.default,\n        });\n\n        let resource_data = self.app_data.map(Rc::new);\n\n        // wraps endpoint service (including middleware) call and injects app data for this scope\n        let endpoint = apply_fn_factory(self.endpoint, move |mut req: ServiceRequest, srv| {\n            if let Some(ref data) = resource_data {\n                req.add_data_container(Rc::clone(data));\n            }\n\n            let fut = srv.call(req);\n\n            async { Ok(fut.await?.map_into_boxed_body()) }\n        });\n\n        config.register_service(rdef, guards, endpoint, None)\n    }\n}\n\npub struct ResourceFactory {\n    routes: Vec<Route>,\n    default: BoxedHttpServiceFactory,\n}\n\nimpl ServiceFactory<ServiceRequest> for ResourceFactory {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = ResourceService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        // construct default service factory future.\n        let default_fut = self.default.new_service(());\n\n        // construct route service factory futures\n        let factory_fut = join_all(self.routes.iter().map(|route| route.new_service(())));\n\n        Box::pin(async move {\n            let default = default_fut.await?;\n            let routes = factory_fut\n                .await\n                .into_iter()\n                .collect::<Result<Vec<_>, _>>()?;\n\n            Ok(ResourceService { routes, default })\n        })\n    }\n}\n\npub struct ResourceService {\n    routes: Vec<RouteService>,\n    default: BoxedHttpService,\n}\n\nimpl Service<ServiceRequest> for ResourceService {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, mut req: ServiceRequest) -> Self::Future {\n        for route in &self.routes {\n            if route.check(&mut req) {\n                return route.call(req);\n            }\n        }\n\n        self.default.call(req)\n    }\n}\n\n#[doc(hidden)]\npub struct ResourceEndpoint {\n    factory: Rc<RefCell<Option<ResourceFactory>>>,\n}\n\nimpl ResourceEndpoint {\n    fn new(factory: Rc<RefCell<Option<ResourceFactory>>>) -> Self {\n        ResourceEndpoint { factory }\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for ResourceEndpoint {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = ResourceService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        self.factory.borrow().as_ref().unwrap().new_service(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use actix_rt::time::sleep;\n    use actix_utils::future::ok;\n\n    use super::*;\n    use crate::{\n        http::{header::HeaderValue, Method, StatusCode},\n        middleware::DefaultHeaders,\n        test::{call_service, init_service, TestRequest},\n        App, HttpMessage,\n    };\n\n    #[test]\n    fn can_be_returned_from_fn() {\n        fn my_resource_1() -> Resource {\n            web::resource(\"/test1\").route(web::get().to(|| async { \"hello\" }))\n        }\n\n        fn my_resource_2() -> Resource<\n            impl ServiceFactory<\n                ServiceRequest,\n                Config = (),\n                Response = ServiceResponse<impl MessageBody>,\n                Error = Error,\n                InitError = (),\n            >,\n        > {\n            web::resource(\"/test2\")\n                .wrap_fn(|req, srv| {\n                    let fut = srv.call(req);\n                    async { Ok(fut.await?.map_into_right_body::<()>()) }\n                })\n                .route(web::get().to(|| async { \"hello\" }))\n        }\n\n        fn my_resource_3() -> impl HttpServiceFactory {\n            web::resource(\"/test3\").route(web::get().to(|| async { \"hello\" }))\n        }\n\n        App::new()\n            .service(my_resource_1())\n            .service(my_resource_2())\n            .service(my_resource_3());\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware() {\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/test\")\n                    .name(\"test\")\n                    .wrap(\n                        DefaultHeaders::new()\n                            .add((header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"))),\n                    )\n                    .route(web::get().to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_fn() {\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/test\")\n                    .wrap_fn(|req, srv| {\n                        let fut = srv.call(req);\n                        async {\n                            fut.await.map(|mut res| {\n                                res.headers_mut()\n                                    .insert(header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n                                res\n                            })\n                        }\n                    })\n                    .route(web::get().to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_to() {\n        let srv = init_service(App::new().service(web::resource(\"/test\").to(|| async {\n            sleep(Duration::from_millis(100)).await;\n            Ok::<_, Error>(HttpResponse::Ok())\n        })))\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_pattern() {\n        let srv = init_service(App::new().service(\n            web::resource([\"/test\", \"/test2\"]).to(|| async { Ok::<_, Error>(HttpResponse::Ok()) }),\n        ))\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let req = TestRequest::with_uri(\"/test2\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_default_resource() {\n        let srv = init_service(\n            App::new()\n                .service(\n                    web::resource(\"/test\")\n                        .route(web::get().to(HttpResponse::Ok))\n                        .route(web::delete().to(HttpResponse::Ok)),\n                )\n                .default_service(|r: ServiceRequest| {\n                    ok(r.into_response(HttpResponse::BadRequest()))\n                }),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::POST)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n        assert_eq!(\n            resp.headers().get(header::ALLOW).unwrap().as_bytes(),\n            b\"GET, DELETE\"\n        );\n\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/test\")\n                    .route(web::get().to(HttpResponse::Ok))\n                    .default_service(|r: ServiceRequest| {\n                        ok(r.into_response(HttpResponse::BadRequest()))\n                    }),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::POST)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[actix_rt::test]\n    async fn test_resource_guards() {\n        let srv = init_service(\n            App::new()\n                .service(\n                    web::resource(\"/test/{p}\")\n                        .guard(guard::Get())\n                        .to(HttpResponse::Ok),\n                )\n                .service(\n                    web::resource(\"/test/{p}\")\n                        .guard(guard::Put())\n                        .to(HttpResponse::Created),\n                )\n                .service(\n                    web::resource(\"/test/{p}\")\n                        .guard(guard::Delete())\n                        .to(HttpResponse::NoContent),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/test/it\")\n            .method(Method::GET)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test/it\")\n            .method(Method::PUT)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::CREATED);\n\n        let req = TestRequest::with_uri(\"/test/it\")\n            .method(Method::DELETE)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NO_CONTENT);\n    }\n\n    // allow deprecated `{App, Resource}::data`\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data() {\n        let srv = init_service(\n            App::new()\n                .data(1.0f64)\n                .data(1usize)\n                .app_data(web::Data::new('-'))\n                .service(\n                    web::resource(\"/test\")\n                        .data(10usize)\n                        .app_data(web::Data::new('*'))\n                        .guard(guard::Get())\n                        .to(\n                            |data1: web::Data<usize>,\n                             data2: web::Data<char>,\n                             data3: web::Data<f64>| {\n                                assert_eq!(**data1, 10);\n                                assert_eq!(**data2, '*');\n                                let error = f64::EPSILON;\n                                assert!((**data3 - 1.0).abs() < error);\n                                HttpResponse::Ok()\n                            },\n                        ),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    // allow deprecated `{App, Resource}::data`\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_data_default_service() {\n        let srv =\n            init_service(\n                App::new().data(1usize).service(\n                    web::resource(\"/test\")\n                        .data(10usize)\n                        .default_service(web::to(|data: web::Data<usize>| {\n                            assert_eq!(**data, 10);\n                            HttpResponse::Ok()\n                        })),\n                ),\n            )\n            .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_app_data() {\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"test\")\n                    .app_data(1usize)\n                    .wrap_fn(|req, srv| {\n                        assert_eq!(req.app_data::<usize>(), Some(&1usize));\n                        req.extensions_mut().insert(1usize);\n                        srv.call(req)\n                    })\n                    .route(web::get().to(HttpResponse::Ok))\n                    .default_service(|req: ServiceRequest| async move {\n                        let (req, _) = req.into_parts();\n\n                        assert_eq!(req.extensions().get::<usize>(), Some(&1));\n\n                        Ok(ServiceResponse::new(\n                            req,\n                            HttpResponse::BadRequest().finish(),\n                        ))\n                    }),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::post().uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_body_type() {\n        let srv = init_service(\n            App::new().service(\n                web::resource(\"/test\")\n                    .wrap_fn(|req, srv| {\n                        let fut = srv.call(req);\n                        async { Ok(fut.await?.map_into_right_body::<()>()) }\n                    })\n                    .route(web::get().to(|| async { \"hello\" })),\n            ),\n        )\n        .await;\n\n        // test if `try_into_bytes()` is preserved across scope layer\n        use actix_http::body::MessageBody as _;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        let body = resp.into_body();\n        assert_eq!(body.try_into_bytes().unwrap(), b\"hello\".as_ref());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/response/builder.rs",
    "content": "use std::{\n    cell::{Ref, RefMut},\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::{error::HttpError, Response, ResponseHead};\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse serde::Serialize;\n\nuse crate::{\n    body::{BodyStream, BoxBody, MessageBody, SizedStream},\n    dev::Extensions,\n    error::{Error, JsonPayloadError},\n    http::{\n        header::{self, HeaderName, TryIntoHeaderPair, TryIntoHeaderValue},\n        ConnectionType, StatusCode,\n    },\n    BoxError, HttpRequest, HttpResponse, Responder,\n};\n\n/// An HTTP response builder.\n///\n/// This type can be used to construct an instance of `Response` through a builder-like pattern.\npub struct HttpResponseBuilder {\n    res: Option<Response<BoxBody>>,\n    error: Option<HttpError>,\n}\n\nimpl HttpResponseBuilder {\n    #[inline]\n    /// Create response builder\n    pub fn new(status: StatusCode) -> Self {\n        Self {\n            res: Some(Response::with_body(status, BoxBody::new(()))),\n            error: None,\n        }\n    }\n\n    /// Set HTTP status code of this response.\n    #[inline]\n    pub fn status(&mut self, status: StatusCode) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.status = status;\n        }\n        self\n    }\n\n    /// Insert a header, replacing any that were set with an equivalent field name.\n    ///\n    /// ```\n    /// use actix_web::{HttpResponse, http::header};\n    ///\n    /// HttpResponse::Ok()\n    ///     .insert_header(header::ContentType(mime::APPLICATION_JSON))\n    ///     .insert_header((\"X-TEST\", \"value\"))\n    ///     .finish();\n    /// ```\n    pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => {\n                    parts.headers.insert(key, value);\n                }\n                Err(err) => self.error = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    /// Append a header, keeping any that were set with an equivalent field name.\n    ///\n    /// ```\n    /// use actix_web::{HttpResponse, http::header};\n    ///\n    /// HttpResponse::Ok()\n    ///     .append_header(header::ContentType(mime::APPLICATION_JSON))\n    ///     .append_header((\"X-TEST\", \"value1\"))\n    ///     .append_header((\"X-TEST\", \"value2\"))\n    ///     .finish();\n    /// ```\n    pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => parts.headers.append(key, value),\n                Err(err) => self.error = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    /// Replaced with [`Self::insert_header()`].\n    #[doc(hidden)]\n    #[deprecated(\n        since = \"4.0.0\",\n        note = \"Replaced with `insert_header((key, value))`. Will be removed in v5.\"\n    )]\n    pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self\n    where\n        K: TryInto<HeaderName>,\n        K::Error: Into<HttpError>,\n        V: TryIntoHeaderValue,\n    {\n        if self.error.is_some() {\n            return self;\n        }\n\n        match (key.try_into(), value.try_into_value()) {\n            (Ok(name), Ok(value)) => return self.insert_header((name, value)),\n            (Err(err), _) => self.error = Some(err.into()),\n            (_, Err(err)) => self.error = Some(err.into()),\n        }\n\n        self\n    }\n\n    /// Replaced with [`Self::append_header()`].\n    #[doc(hidden)]\n    #[deprecated(\n        since = \"4.0.0\",\n        note = \"Replaced with `append_header((key, value))`. Will be removed in v5.\"\n    )]\n    pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self\n    where\n        K: TryInto<HeaderName>,\n        K::Error: Into<HttpError>,\n        V: TryIntoHeaderValue,\n    {\n        if self.error.is_some() {\n            return self;\n        }\n\n        match (key.try_into(), value.try_into_value()) {\n            (Ok(name), Ok(value)) => return self.append_header((name, value)),\n            (Err(err), _) => self.error = Some(err.into()),\n            (_, Err(err)) => self.error = Some(err.into()),\n        }\n\n        self\n    }\n\n    /// Set the custom reason for the response.\n    #[inline]\n    pub fn reason(&mut self, reason: &'static str) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.reason = Some(reason);\n        }\n        self\n    }\n\n    /// Set connection type to KeepAlive\n    #[inline]\n    pub fn keep_alive(&mut self) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::KeepAlive);\n        }\n        self\n    }\n\n    /// Set connection type to Upgrade\n    #[inline]\n    pub fn upgrade<V>(&mut self, value: V) -> &mut Self\n    where\n        V: TryIntoHeaderValue,\n    {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::Upgrade);\n        }\n\n        if let Ok(value) = value.try_into_value() {\n            self.insert_header((header::UPGRADE, value));\n        }\n\n        self\n    }\n\n    /// Force close connection, even if it is marked as keep-alive\n    #[inline]\n    pub fn force_close(&mut self) -> &mut Self {\n        if let Some(parts) = self.inner() {\n            parts.set_connection_type(ConnectionType::Close);\n        }\n        self\n    }\n\n    /// Disable chunked transfer encoding for HTTP/1.1 streaming responses.\n    #[inline]\n    pub fn no_chunking(&mut self, len: u64) -> &mut Self {\n        let mut buf = itoa::Buffer::new();\n        self.insert_header((header::CONTENT_LENGTH, buf.format(len)));\n\n        if let Some(parts) = self.inner() {\n            parts.no_chunking(true);\n        }\n        self\n    }\n\n    /// Set response content type.\n    #[inline]\n    pub fn content_type<V>(&mut self, value: V) -> &mut Self\n    where\n        V: TryIntoHeaderValue,\n    {\n        if let Some(parts) = self.inner() {\n            match value.try_into_value() {\n                Ok(value) => {\n                    parts.headers.insert(header::CONTENT_TYPE, value);\n                }\n                Err(err) => self.error = Some(err.into()),\n            };\n        }\n        self\n    }\n\n    /// Add a cookie to the response.\n    ///\n    /// To send a \"removal\" cookie, call [`.make_removal()`](cookie::Cookie::make_removal) on the\n    /// given cookie. See [`HttpResponse::add_removal_cookie()`] to learn more.\n    ///\n    /// # Examples\n    /// Send a new cookie:\n    /// ```\n    /// use actix_web::{HttpResponse, cookie::Cookie};\n    ///\n    /// let res = HttpResponse::Ok()\n    ///     .cookie(\n    ///         Cookie::build(\"name\", \"value\")\n    ///             .domain(\"www.rust-lang.org\")\n    ///             .path(\"/\")\n    ///             .secure(true)\n    ///             .http_only(true)\n    ///             .finish(),\n    ///     )\n    ///     .finish();\n    /// ```\n    ///\n    /// Send a removal cookie:\n    /// ```\n    /// use actix_web::{HttpResponse, cookie::Cookie};\n    ///\n    /// // the name, domain and path match the cookie created in the previous example\n    /// let mut cookie = Cookie::build(\"name\", \"value-does-not-matter\")\n    ///     .domain(\"www.rust-lang.org\")\n    ///     .path(\"/\")\n    ///     .finish();\n    /// cookie.make_removal();\n    ///\n    /// let res = HttpResponse::Ok()\n    ///     .cookie(cookie)\n    ///     .finish();\n    /// ```\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(&mut self, cookie: cookie::Cookie<'_>) -> &mut Self {\n        match cookie.to_string().try_into_value() {\n            Ok(hdr_val) => self.append_header((header::SET_COOKIE, hdr_val)),\n            Err(err) => {\n                self.error = Some(err.into());\n                self\n            }\n        }\n    }\n\n    /// Returns a reference to the response-local data/extensions container.\n    #[inline]\n    pub fn extensions(&self) -> Ref<'_, Extensions> {\n        self.res\n            .as_ref()\n            .expect(\"cannot reuse response builder\")\n            .extensions()\n    }\n\n    /// Returns a mutable reference to the response-local data/extensions container.\n    #[inline]\n    pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {\n        self.res\n            .as_mut()\n            .expect(\"cannot reuse response builder\")\n            .extensions_mut()\n    }\n\n    /// Set a body and build the `HttpResponse`.\n    ///\n    /// Unlike [`message_body`](Self::message_body), errors are converted into error\n    /// responses immediately.\n    ///\n    /// `HttpResponseBuilder` can not be used after this call.\n    pub fn body<B>(&mut self, body: B) -> HttpResponse<BoxBody>\n    where\n        B: MessageBody + 'static,\n    {\n        match self.message_body(body) {\n            Ok(res) => res.map_into_boxed_body(),\n            Err(err) => HttpResponse::from_error(err),\n        }\n    }\n\n    /// Set a body and build the `HttpResponse`.\n    ///\n    /// `HttpResponseBuilder` can not be used after this call.\n    pub fn message_body<B>(&mut self, body: B) -> Result<HttpResponse<B>, Error> {\n        if let Some(err) = self.error.take() {\n            return Err(err.into());\n        }\n\n        let res = self\n            .res\n            .take()\n            .expect(\"cannot reuse response builder\")\n            .set_body(body);\n\n        Ok(HttpResponse::from(res))\n    }\n\n    /// Set a streaming body and build the `HttpResponse`.\n    ///\n    /// `HttpResponseBuilder` can not be used after this call.\n    ///\n    /// If `Content-Type` is not set, then it is automatically set to `application/octet-stream`.\n    ///\n    /// If `Content-Length` is set, then [`no_chunking()`](Self::no_chunking) is automatically called.\n    #[inline]\n    pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse\n    where\n        S: Stream<Item = Result<Bytes, E>> + 'static,\n        E: Into<BoxError> + 'static,\n    {\n        // Set mime type to application/octet-stream if it is not set\n        if let Some(parts) = self.inner() {\n            if !parts.headers.contains_key(header::CONTENT_TYPE) {\n                self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));\n            }\n        }\n\n        let content_length = self\n            .inner()\n            .and_then(|parts| parts.headers.get(header::CONTENT_LENGTH))\n            .and_then(|value| value.to_str().ok())\n            .and_then(|value| value.parse::<u64>().ok());\n\n        if let Some(len) = content_length {\n            self.no_chunking(len);\n            self.body(SizedStream::new(len, stream))\n        } else {\n            self.body(BodyStream::new(stream))\n        }\n    }\n\n    /// Set a JSON body and build the `HttpResponse`.\n    ///\n    /// `HttpResponseBuilder` can not be used after this call.\n    pub fn json(&mut self, value: impl Serialize) -> HttpResponse {\n        match serde_json::to_string(&value) {\n            Ok(body) => {\n                let contains = if let Some(parts) = self.inner() {\n                    parts.headers.contains_key(header::CONTENT_TYPE)\n                } else {\n                    true\n                };\n\n                if !contains {\n                    self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));\n                }\n\n                self.body(body)\n            }\n            Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),\n        }\n    }\n\n    /// Set an empty body and build the `HttpResponse`.\n    ///\n    /// `HttpResponseBuilder` can not be used after this call.\n    #[inline]\n    pub fn finish(&mut self) -> HttpResponse {\n        self.body(())\n    }\n\n    /// This method construct new `HttpResponseBuilder`\n    pub fn take(&mut self) -> Self {\n        Self {\n            res: self.res.take(),\n            error: self.error.take(),\n        }\n    }\n\n    fn inner(&mut self) -> Option<&mut ResponseHead> {\n        if self.error.is_some() {\n            return None;\n        }\n\n        self.res.as_mut().map(Response::head_mut)\n    }\n}\n\nimpl From<HttpResponseBuilder> for HttpResponse {\n    fn from(mut builder: HttpResponseBuilder) -> Self {\n        builder.finish()\n    }\n}\n\nimpl From<HttpResponseBuilder> for Response<BoxBody> {\n    fn from(mut builder: HttpResponseBuilder) -> Self {\n        builder.finish().into()\n    }\n}\n\nimpl Future for HttpResponseBuilder {\n    type Output = Result<HttpResponse, Error>;\n\n    fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {\n        Poll::Ready(Ok(self.finish()))\n    }\n}\n\nimpl Responder for HttpResponseBuilder {\n    type Body = BoxBody;\n\n    #[inline]\n    fn respond_to(mut self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        self.finish()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        body,\n        http::header::{HeaderValue, CONTENT_TYPE},\n        test::assert_body_eq,\n    };\n\n    #[test]\n    fn test_basic_builder() {\n        let resp = HttpResponse::Ok()\n            .insert_header((\"X-TEST\", \"value\"))\n            .finish();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[test]\n    fn test_upgrade() {\n        let resp = HttpResponseBuilder::new(StatusCode::OK)\n            .upgrade(\"websocket\")\n            .finish();\n        assert!(resp.upgrade());\n        assert_eq!(\n            resp.headers().get(header::UPGRADE).unwrap(),\n            HeaderValue::from_static(\"websocket\")\n        );\n    }\n\n    #[test]\n    fn test_force_close() {\n        let resp = HttpResponseBuilder::new(StatusCode::OK)\n            .force_close()\n            .finish();\n        assert!(!resp.keep_alive())\n    }\n\n    #[test]\n    fn test_content_type() {\n        let resp = HttpResponseBuilder::new(StatusCode::OK)\n            .content_type(\"text/plain\")\n            .body(Bytes::new());\n        assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), \"text/plain\")\n    }\n\n    #[actix_rt::test]\n    async fn test_json() {\n        let res = HttpResponse::Ok().json(vec![\"v1\", \"v2\", \"v3\"]);\n        let ct = res.headers().get(CONTENT_TYPE).unwrap();\n        assert_eq!(ct, HeaderValue::from_static(\"application/json\"));\n        assert_body_eq!(res, br#\"[\"v1\",\"v2\",\"v3\"]\"#);\n\n        let res = HttpResponse::Ok().json([\"v1\", \"v2\", \"v3\"]);\n        let ct = res.headers().get(CONTENT_TYPE).unwrap();\n        assert_eq!(ct, HeaderValue::from_static(\"application/json\"));\n        assert_body_eq!(res, br#\"[\"v1\",\"v2\",\"v3\"]\"#);\n\n        // content type override\n        let res = HttpResponse::Ok()\n            .insert_header((CONTENT_TYPE, \"text/json\"))\n            .json([\"v1\", \"v2\", \"v3\"]);\n        let ct = res.headers().get(CONTENT_TYPE).unwrap();\n        assert_eq!(ct, HeaderValue::from_static(\"text/json\"));\n        assert_body_eq!(res, br#\"[\"v1\",\"v2\",\"v3\"]\"#);\n    }\n\n    #[actix_rt::test]\n    async fn test_serde_json_in_body() {\n        let resp = HttpResponse::Ok()\n            .body(serde_json::to_vec(&serde_json::json!({ \"test-key\": \"test-value\" })).unwrap());\n\n        assert_eq!(\n            body::to_bytes(resp.into_body()).await.unwrap().as_ref(),\n            br#\"{\"test-key\":\"test-value\"}\"#\n        );\n    }\n\n    #[test]\n    fn response_builder_header_insert_kv() {\n        let mut res = HttpResponse::Ok();\n        res.insert_header((\"Content-Type\", \"application/octet-stream\"));\n        let res = res.finish();\n\n        assert_eq!(\n            res.headers().get(\"Content-Type\"),\n            Some(&HeaderValue::from_static(\"application/octet-stream\"))\n        );\n    }\n\n    #[test]\n    fn response_builder_header_insert_typed() {\n        let mut res = HttpResponse::Ok();\n        res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));\n        let res = res.finish();\n\n        assert_eq!(\n            res.headers().get(\"Content-Type\"),\n            Some(&HeaderValue::from_static(\"application/octet-stream\"))\n        );\n    }\n\n    #[test]\n    fn response_builder_header_append_kv() {\n        let mut res = HttpResponse::Ok();\n        res.append_header((\"Content-Type\", \"application/octet-stream\"));\n        res.append_header((\"Content-Type\", \"application/json\"));\n        let res = res.finish();\n\n        let headers: Vec<_> = res.headers().get_all(\"Content-Type\").cloned().collect();\n        assert_eq!(headers.len(), 2);\n        assert!(headers.contains(&HeaderValue::from_static(\"application/octet-stream\")));\n        assert!(headers.contains(&HeaderValue::from_static(\"application/json\")));\n    }\n\n    #[test]\n    fn response_builder_header_append_typed() {\n        let mut res = HttpResponse::Ok();\n        res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));\n        res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));\n        let res = res.finish();\n\n        let headers: Vec<_> = res.headers().get_all(\"Content-Type\").cloned().collect();\n        assert_eq!(headers.len(), 2);\n        assert!(headers.contains(&HeaderValue::from_static(\"application/octet-stream\")));\n        assert!(headers.contains(&HeaderValue::from_static(\"application/json\")));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/response/customize_responder.rs",
    "content": "use actix_http::{\n    body::EitherBody,\n    error::HttpError,\n    header::{HeaderMap, TryIntoHeaderPair},\n    StatusCode,\n};\n\nuse crate::{HttpRequest, HttpResponse, Responder};\n\n/// Allows overriding status code and headers (including cookies) for a [`Responder`].\n///\n/// Created by calling the [`customize`](Responder::customize) method on a [`Responder`] type.\npub struct CustomizeResponder<R> {\n    inner: CustomizeResponderInner<R>,\n    error: Option<HttpError>,\n}\n\nstruct CustomizeResponderInner<R> {\n    responder: R,\n    status: Option<StatusCode>,\n    override_headers: HeaderMap,\n    append_headers: HeaderMap,\n}\n\nimpl<R: Responder> CustomizeResponder<R> {\n    pub(crate) fn new(responder: R) -> Self {\n        CustomizeResponder {\n            inner: CustomizeResponderInner {\n                responder,\n                status: None,\n                override_headers: HeaderMap::new(),\n                append_headers: HeaderMap::new(),\n            },\n            error: None,\n        }\n    }\n\n    /// Override a status code for the Responder's response.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{Responder, http::StatusCode, test::TestRequest};\n    ///\n    /// let responder = \"Welcome!\".customize().with_status(StatusCode::ACCEPTED);\n    ///\n    /// let request = TestRequest::default().to_http_request();\n    /// let response = responder.respond_to(&request);\n    /// assert_eq!(response.status(), StatusCode::ACCEPTED);\n    /// ```\n    pub fn with_status(mut self, status: StatusCode) -> Self {\n        if let Some(inner) = self.inner() {\n            inner.status = Some(status);\n        }\n\n        self\n    }\n\n    /// Insert (override) header in the final response.\n    ///\n    /// Overrides other headers with the same name.\n    /// See [`HeaderMap::insert`](crate::http::header::HeaderMap::insert).\n    ///\n    /// Headers added with this method will be inserted before those added\n    /// with [`append_header`](Self::append_header). As such, header(s) can be overridden with more\n    /// than one new header by first calling `insert_header` followed by `append_header`.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{Responder, test::TestRequest};\n    ///\n    /// let responder = \"Hello world!\"\n    ///     .customize()\n    ///     .insert_header((\"x-version\", \"1.2.3\"));\n    ///\n    /// let request = TestRequest::default().to_http_request();\n    /// let response = responder.respond_to(&request);\n    /// assert_eq!(response.headers().get(\"x-version\").unwrap(), \"1.2.3\");\n    /// ```\n    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        if let Some(inner) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => {\n                    inner.override_headers.insert(key, value);\n                }\n                Err(err) => self.error = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    /// Append header to the final response.\n    ///\n    /// Unlike [`insert_header`](Self::insert_header), this will not override existing headers.\n    /// See [`HeaderMap::append`](crate::http::header::HeaderMap::append).\n    ///\n    /// Headers added here are appended _after_ additions/overrides from `insert_header`.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{Responder, test::TestRequest};\n    ///\n    /// let responder = \"Hello world!\"\n    ///     .customize()\n    ///     .append_header((\"x-version\", \"1.2.3\"));\n    ///\n    /// let request = TestRequest::default().to_http_request();\n    /// let response = responder.respond_to(&request);\n    /// assert_eq!(response.headers().get(\"x-version\").unwrap(), \"1.2.3\");\n    /// ```\n    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        if let Some(inner) = self.inner() {\n            match header.try_into_pair() {\n                Ok((key, value)) => {\n                    inner.append_headers.append(key, value);\n                }\n                Err(err) => self.error = Some(err.into()),\n            };\n        }\n\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Renamed to `insert_header`.\")]\n    pub fn with_header(self, header: impl TryIntoHeaderPair) -> Self\n    where\n        Self: Sized,\n    {\n        self.insert_header(header)\n    }\n\n    fn inner(&mut self) -> Option<&mut CustomizeResponderInner<R>> {\n        if self.error.is_some() {\n            None\n        } else {\n            Some(&mut self.inner)\n        }\n    }\n\n    /// Appends a `cookie` to the final response.\n    ///\n    /// # Errors\n    ///\n    /// Final response will be an error if `cookie` cannot be converted into a valid header value.\n    #[cfg(feature = \"cookies\")]\n    pub fn add_cookie(mut self, cookie: &crate::cookie::Cookie<'_>) -> Self {\n        use actix_http::header::{TryIntoHeaderValue as _, SET_COOKIE};\n\n        if let Some(inner) = self.inner() {\n            match cookie.to_string().try_into_value() {\n                Ok(val) => {\n                    inner.append_headers.append(SET_COOKIE, val);\n                }\n                Err(err) => {\n                    self.error = Some(err.into());\n                }\n            }\n        }\n\n        self\n    }\n}\n\nimpl<T> Responder for CustomizeResponder<T>\nwhere\n    T: Responder,\n{\n    type Body = EitherBody<T::Body>;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        if let Some(err) = self.error {\n            return HttpResponse::from_error(err).map_into_right_body();\n        }\n\n        let mut res = self.inner.responder.respond_to(req);\n\n        if let Some(status) = self.inner.status {\n            *res.status_mut() = status;\n        }\n\n        for (k, v) in self.inner.override_headers {\n            res.headers_mut().insert(k, v);\n        }\n\n        for (k, v) in self.inner.append_headers {\n            res.headers_mut().append(k, v);\n        }\n\n        res.map_into_left_body()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::body::to_bytes;\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::{\n        cookie::Cookie,\n        http::header::{HeaderValue, CONTENT_TYPE},\n        test::TestRequest,\n    };\n\n    #[actix_rt::test]\n    async fn customize_responder() {\n        let req = TestRequest::default().to_http_request();\n        let res = \"test\"\n            .to_string()\n            .customize()\n            .with_status(StatusCode::BAD_REQUEST)\n            .respond_to(&req);\n\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = \"test\"\n            .to_string()\n            .customize()\n            .insert_header((\"content-type\", \"json\"))\n            .respond_to(&req);\n\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"json\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = \"test\"\n            .to_string()\n            .customize()\n            .add_cookie(&Cookie::new(\"name\", \"value\"))\n            .respond_to(&req);\n\n        assert!(res.status().is_success());\n        assert_eq!(\n            res.cookies().collect::<Vec<Cookie<'_>>>(),\n            vec![Cookie::new(\"name\", \"value\")],\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n    }\n\n    #[actix_rt::test]\n    async fn tuple_responder_with_status_code() {\n        let req = TestRequest::default().to_http_request();\n        let res = (\"test\".to_string(), StatusCode::BAD_REQUEST).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let req = TestRequest::default().to_http_request();\n        let res = (\"test\".to_string(), StatusCode::OK)\n            .customize()\n            .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON))\n            .respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/json\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/response/http_codes.rs",
    "content": "//! Status code based HTTP response builders.\n\nuse actix_http::StatusCode;\n\nuse crate::{HttpResponse, HttpResponseBuilder};\n\nmacro_rules! static_resp {\n    ($name:ident, $status:expr) => {\n        #[allow(non_snake_case)]\n        #[doc = concat!(\"Creates a new response builder with the status code `\", stringify!($status), \"`.\")]\n        pub fn $name() -> HttpResponseBuilder {\n            HttpResponseBuilder::new($status)\n        }\n    };\n}\n\nimpl HttpResponse {\n    static_resp!(Continue, StatusCode::CONTINUE);\n    static_resp!(SwitchingProtocols, StatusCode::SWITCHING_PROTOCOLS);\n    static_resp!(Processing, StatusCode::PROCESSING);\n\n    static_resp!(Ok, StatusCode::OK);\n    static_resp!(Created, StatusCode::CREATED);\n    static_resp!(Accepted, StatusCode::ACCEPTED);\n    static_resp!(\n        NonAuthoritativeInformation,\n        StatusCode::NON_AUTHORITATIVE_INFORMATION\n    );\n    static_resp!(NoContent, StatusCode::NO_CONTENT);\n    static_resp!(ResetContent, StatusCode::RESET_CONTENT);\n    static_resp!(PartialContent, StatusCode::PARTIAL_CONTENT);\n    static_resp!(MultiStatus, StatusCode::MULTI_STATUS);\n    static_resp!(AlreadyReported, StatusCode::ALREADY_REPORTED);\n    static_resp!(ImUsed, StatusCode::IM_USED);\n\n    static_resp!(MultipleChoices, StatusCode::MULTIPLE_CHOICES);\n    static_resp!(MovedPermanently, StatusCode::MOVED_PERMANENTLY);\n    static_resp!(Found, StatusCode::FOUND);\n    static_resp!(SeeOther, StatusCode::SEE_OTHER);\n    static_resp!(NotModified, StatusCode::NOT_MODIFIED);\n    static_resp!(UseProxy, StatusCode::USE_PROXY);\n    static_resp!(TemporaryRedirect, StatusCode::TEMPORARY_REDIRECT);\n    static_resp!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT);\n\n    static_resp!(BadRequest, StatusCode::BAD_REQUEST);\n    static_resp!(Unauthorized, StatusCode::UNAUTHORIZED);\n    static_resp!(PaymentRequired, StatusCode::PAYMENT_REQUIRED);\n    static_resp!(Forbidden, StatusCode::FORBIDDEN);\n    static_resp!(NotFound, StatusCode::NOT_FOUND);\n    static_resp!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED);\n    static_resp!(NotAcceptable, StatusCode::NOT_ACCEPTABLE);\n    static_resp!(\n        ProxyAuthenticationRequired,\n        StatusCode::PROXY_AUTHENTICATION_REQUIRED\n    );\n    static_resp!(RequestTimeout, StatusCode::REQUEST_TIMEOUT);\n    static_resp!(Conflict, StatusCode::CONFLICT);\n    static_resp!(Gone, StatusCode::GONE);\n    static_resp!(LengthRequired, StatusCode::LENGTH_REQUIRED);\n    static_resp!(PreconditionFailed, StatusCode::PRECONDITION_FAILED);\n    static_resp!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE);\n    static_resp!(UriTooLong, StatusCode::URI_TOO_LONG);\n    static_resp!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE);\n    static_resp!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);\n    static_resp!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);\n    static_resp!(ImATeapot, StatusCode::IM_A_TEAPOT);\n    static_resp!(MisdirectedRequest, StatusCode::MISDIRECTED_REQUEST);\n    static_resp!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY);\n    static_resp!(Locked, StatusCode::LOCKED);\n    static_resp!(FailedDependency, StatusCode::FAILED_DEPENDENCY);\n    static_resp!(UpgradeRequired, StatusCode::UPGRADE_REQUIRED);\n    static_resp!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED);\n    static_resp!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS);\n    static_resp!(\n        RequestHeaderFieldsTooLarge,\n        StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE\n    );\n    static_resp!(\n        UnavailableForLegalReasons,\n        StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS\n    );\n\n    static_resp!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR);\n    static_resp!(NotImplemented, StatusCode::NOT_IMPLEMENTED);\n    static_resp!(BadGateway, StatusCode::BAD_GATEWAY);\n    static_resp!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE);\n    static_resp!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT);\n    static_resp!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED);\n    static_resp!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES);\n    static_resp!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE);\n    static_resp!(LoopDetected, StatusCode::LOOP_DETECTED);\n    static_resp!(NotExtended, StatusCode::NOT_EXTENDED);\n    static_resp!(\n        NetworkAuthenticationRequired,\n        StatusCode::NETWORK_AUTHENTICATION_REQUIRED\n    );\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{http::StatusCode, HttpResponse};\n\n    #[test]\n    fn test_build() {\n        let resp = HttpResponse::Ok().finish();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/response/mod.rs",
    "content": "mod builder;\nmod customize_responder;\nmod http_codes;\nmod responder;\n#[allow(clippy::module_inception)]\nmod response;\n\npub use self::{\n    builder::HttpResponseBuilder, customize_responder::CustomizeResponder, responder::Responder,\n    response::HttpResponse,\n};\n"
  },
  {
    "path": "actix-web/src/response/responder.rs",
    "content": "use std::borrow::Cow;\n\nuse actix_http::{\n    body::{BoxBody, EitherBody, MessageBody},\n    header::TryIntoHeaderPair,\n    StatusCode,\n};\nuse bytes::{Bytes, BytesMut};\n\nuse super::CustomizeResponder;\nuse crate::{Error, HttpRequest, HttpResponse};\n\n/// Trait implemented by types that can be converted to an HTTP response.\n///\n/// Any types that implement this trait can be used in the return type of a handler. Since handlers\n/// will only have one return type, it is idiomatic to use opaque return types `-> impl Responder`.\n///\n/// # Implementations\n/// It is often not required to implement `Responder` for your own types due to a broad base of\n/// built-in implementations:\n/// - `HttpResponse` and `HttpResponseBuilder`\n/// - `Option<R>` where `R: Responder`\n/// - `Result<R, E>` where `R: Responder` and [`E: ResponseError`](crate::ResponseError)\n/// - `(R, StatusCode)` where `R: Responder`\n/// - `&'static str`, `String`, `&'_ String`, `Cow<'_, str>`, [`ByteString`](bytestring::ByteString)\n/// - `&'static [u8]`, `Vec<u8>`, `Bytes`, `BytesMut`\n/// - [`Json<T>`](crate::web::Json) and [`Form<T>`](crate::web::Form) where `T: Serialize`\n/// - [`Either<L, R>`](crate::web::Either) where `L: Serialize` and `R: Serialize`\n/// - [`CustomizeResponder<R>`]\n/// - [`actix_files::NamedFile`](https://docs.rs/actix-files/latest/actix_files/struct.NamedFile.html)\n/// - [Experimental responders from `actix-web-lab`](https://docs.rs/actix-web-lab/latest/actix_web_lab/respond/index.html)\n/// - Third party integrations may also have implemented `Responder` where appropriate. For example,\n///   HTML templating engines.\n///\n/// # Customizing Responder Output\n/// Calling [`.customize()`](Responder::customize) on any responder type will wrap it in a\n/// [`CustomizeResponder`] capable of overriding various parts of the response such as the status\n/// code and header map.\npub trait Responder {\n    type Body: MessageBody + 'static;\n\n    /// Convert self to `HttpResponse`.\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body>;\n\n    /// Wraps responder to allow alteration of its response.\n    ///\n    /// See [`CustomizeResponder`] docs for more details on its capabilities.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{Responder, http::StatusCode, test::TestRequest};\n    ///\n    /// let responder = \"Hello world!\"\n    ///     .customize()\n    ///     .with_status(StatusCode::BAD_REQUEST)\n    ///     .insert_header((\"x-hello\", \"world\"));\n    ///\n    /// let request = TestRequest::default().to_http_request();\n    /// let response = responder.respond_to(&request);\n    /// assert_eq!(response.status(), StatusCode::BAD_REQUEST);\n    /// assert_eq!(response.headers().get(\"x-hello\").unwrap(), \"world\");\n    /// ```\n    #[inline]\n    fn customize(self) -> CustomizeResponder<Self>\n    where\n        Self: Sized,\n    {\n        CustomizeResponder::new(self)\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Prefer `.customize().with_status(header)`.\")]\n    fn with_status(self, status: StatusCode) -> CustomizeResponder<Self>\n    where\n        Self: Sized,\n    {\n        self.customize().with_status(status)\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Prefer `.customize().insert_header(header)`.\")]\n    fn with_header(self, header: impl TryIntoHeaderPair) -> CustomizeResponder<Self>\n    where\n        Self: Sized,\n    {\n        self.customize().insert_header(header)\n    }\n}\n\nimpl Responder for actix_http::Response<BoxBody> {\n    type Body = BoxBody;\n\n    #[inline]\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        HttpResponse::from(self)\n    }\n}\n\nimpl Responder for actix_http::ResponseBuilder {\n    type Body = BoxBody;\n\n    #[inline]\n    fn respond_to(mut self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        self.finish().map_into_boxed_body().respond_to(req)\n    }\n}\n\nimpl<R: Responder> Responder for Option<R> {\n    type Body = EitherBody<R::Body>;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        match self {\n            Some(val) => val.respond_to(req).map_into_left_body(),\n            None => HttpResponse::new(StatusCode::NOT_FOUND).map_into_right_body(),\n        }\n    }\n}\n\nimpl<R, E> Responder for Result<R, E>\nwhere\n    R: Responder,\n    E: Into<Error>,\n{\n    type Body = EitherBody<R::Body>;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        match self {\n            Ok(val) => val.respond_to(req).map_into_left_body(),\n            Err(err) => HttpResponse::from_error(err.into()).map_into_right_body(),\n        }\n    }\n}\n\n// Note: see https://github.com/actix/actix-web/issues/1108 for reasoning why Responder is not\n// implemented for `()`, and https://github.com/actix/actix-web/pull/3560 for discussion about this\n// impl and the decision not to include a similar one for `Option<()>`.\nimpl<E> Responder for Result<(), E>\nwhere\n    E: Into<Error>,\n{\n    type Body = BoxBody;\n\n    fn respond_to(self, _req: &HttpRequest) -> HttpResponse {\n        match self {\n            Ok(()) => HttpResponse::new(StatusCode::NO_CONTENT),\n            Err(err) => HttpResponse::from_error(err.into()),\n        }\n    }\n}\n\nimpl<R: Responder> Responder for (R, StatusCode) {\n    type Body = R::Body;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        let mut res = self.0.respond_to(req);\n        *res.status_mut() = self.1;\n        res\n    }\n}\n\nmacro_rules! impl_responder_by_forward_into_base_response {\n    ($res:ty, $body:ty) => {\n        impl Responder for $res {\n            type Body = $body;\n\n            fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n                let res: actix_http::Response<_> = self.into();\n                res.into()\n            }\n        }\n    };\n\n    ($res:ty) => {\n        impl_responder_by_forward_into_base_response!($res, $res);\n    };\n}\n\nimpl_responder_by_forward_into_base_response!(&'static [u8]);\nimpl_responder_by_forward_into_base_response!(Vec<u8>);\nimpl_responder_by_forward_into_base_response!(Bytes);\nimpl_responder_by_forward_into_base_response!(BytesMut);\n\nimpl_responder_by_forward_into_base_response!(&'static str);\nimpl_responder_by_forward_into_base_response!(String);\nimpl_responder_by_forward_into_base_response!(bytestring::ByteString);\n\nmacro_rules! impl_into_string_responder {\n    ($res:ty) => {\n        impl Responder for $res {\n            type Body = String;\n\n            fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n                let string: String = self.into();\n                let res: actix_http::Response<_> = string.into();\n                res.into()\n            }\n        }\n    };\n}\n\nimpl_into_string_responder!(&'_ String);\nimpl_into_string_responder!(Cow<'_, str>);\n\n#[cfg(test)]\npub(crate) mod tests {\n    use actix_http::body::to_bytes;\n    use actix_service::Service;\n\n    use super::*;\n    use crate::{\n        error,\n        http::header::{HeaderValue, CONTENT_TYPE},\n        test::{assert_body_eq, init_service, TestRequest},\n        web, App,\n    };\n\n    #[actix_rt::test]\n    async fn test_option_responder() {\n        let srv = init_service(\n            App::new()\n                .service(web::resource(\"/none\").to(|| async { Option::<&'static str>::None }))\n                .service(web::resource(\"/some\").to(|| async { Some(\"some\") })),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/none\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::with_uri(\"/some\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_body_eq!(resp, b\"some\");\n    }\n\n    #[actix_rt::test]\n    async fn test_responder() {\n        let req = TestRequest::default().to_http_request();\n\n        let res = \"test\".respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = b\"test\".respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = \"test\".to_string().respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = (&\"test\".to_string()).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let s = String::from(\"test\");\n        let res = Cow::Borrowed(s.as_str()).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = Cow::<'_, str>::Owned(s).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = Cow::Borrowed(\"test\").respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = Bytes::from_static(b\"test\").respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = BytesMut::from(b\"test\".as_ref()).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/octet-stream\")\n        );\n        assert_eq!(\n            to_bytes(res.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        // InternalError\n        let res = error::InternalError::new(\"err\", StatusCode::BAD_REQUEST).respond_to(&req);\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n    }\n\n    #[actix_rt::test]\n    async fn test_result_responder() {\n        let req = TestRequest::default().to_http_request();\n\n        // Result<I, E>\n        let resp = Ok::<_, Error>(\"test\".to_string()).respond_to(&req);\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"text/plain; charset=utf-8\")\n        );\n        assert_eq!(\n            to_bytes(resp.into_body()).await.unwrap(),\n            Bytes::from_static(b\"test\"),\n        );\n\n        let res = Err::<String, _>(error::InternalError::new(\"err\", StatusCode::BAD_REQUEST))\n            .respond_to(&req);\n\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/response/response.rs",
    "content": "use std::{\n    cell::{Ref, RefMut},\n    fmt,\n};\n\nuse actix_http::{\n    body::{BoxBody, EitherBody, MessageBody},\n    header::HeaderMap,\n    Extensions, Response, ResponseHead, StatusCode,\n};\n#[cfg(feature = \"cookies\")]\nuse {\n    actix_http::{\n        error::HttpError,\n        header::{self, HeaderValue},\n    },\n    cookie::Cookie,\n};\n\nuse crate::{error::Error, HttpRequest, HttpResponseBuilder, Responder};\n\n/// An outgoing response.\npub struct HttpResponse<B = BoxBody> {\n    res: Response<B>,\n    error: Option<Error>,\n}\n\nimpl HttpResponse<BoxBody> {\n    /// Constructs a response.\n    #[inline]\n    pub fn new(status: StatusCode) -> Self {\n        Self {\n            res: Response::new(status),\n            error: None,\n        }\n    }\n\n    /// Constructs a response builder with specific HTTP status.\n    #[inline]\n    pub fn build(status: StatusCode) -> HttpResponseBuilder {\n        HttpResponseBuilder::new(status)\n    }\n\n    /// Create an error response.\n    #[inline]\n    pub fn from_error(error: impl Into<Error>) -> Self {\n        let error = error.into();\n        let mut response = error.as_response_error().error_response();\n        response.error = Some(error);\n        response\n    }\n}\n\nimpl<B> HttpResponse<B> {\n    /// Constructs a response with body\n    #[inline]\n    pub fn with_body(status: StatusCode, body: B) -> Self {\n        Self {\n            res: Response::with_body(status, body),\n            error: None,\n        }\n    }\n\n    /// Returns a reference to response head.\n    #[inline]\n    pub fn head(&self) -> &ResponseHead {\n        self.res.head()\n    }\n\n    /// Returns a mutable reference to response head.\n    #[inline]\n    pub fn head_mut(&mut self) -> &mut ResponseHead {\n        self.res.head_mut()\n    }\n\n    /// The source `error` for this response\n    #[inline]\n    pub fn error(&self) -> Option<&Error> {\n        self.error.as_ref()\n    }\n\n    /// Get the response status code\n    #[inline]\n    pub fn status(&self) -> StatusCode {\n        self.res.status()\n    }\n\n    /// Set the `StatusCode` for this response\n    #[inline]\n    pub fn status_mut(&mut self) -> &mut StatusCode {\n        self.res.status_mut()\n    }\n\n    /// Get the headers from the response\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        self.res.headers()\n    }\n\n    /// Get a mutable reference to the headers\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        self.res.headers_mut()\n    }\n\n    /// Get an iterator for the cookies set by this response.\n    #[cfg(feature = \"cookies\")]\n    pub fn cookies(&self) -> CookieIter<'_> {\n        CookieIter {\n            iter: self.headers().get_all(header::SET_COOKIE),\n        }\n    }\n\n    /// Add a cookie to this response.\n    ///\n    /// # Errors\n    /// Returns an error if the cookie results in a malformed `Set-Cookie` header.\n    #[cfg(feature = \"cookies\")]\n    pub fn add_cookie(&mut self, cookie: &Cookie<'_>) -> Result<(), HttpError> {\n        HeaderValue::from_str(&cookie.to_string())\n            .map(|cookie| self.headers_mut().append(header::SET_COOKIE, cookie))\n            .map_err(Into::into)\n    }\n\n    /// Add a \"removal\" cookie to the response that matches attributes of given cookie.\n    ///\n    /// This will cause browsers/clients to remove stored cookies with this name.\n    ///\n    /// The `Set-Cookie` header added to the response will have:\n    /// - name matching given cookie;\n    /// - domain matching given cookie;\n    /// - path matching given cookie;\n    /// - an empty value;\n    /// - a max-age of `0`;\n    /// - an expiration date far in the past.\n    ///\n    /// If the cookie you're trying to remove has an explicit path or domain set, those attributes\n    /// will need to be included in the cookie passed in here.\n    ///\n    /// # Errors\n    /// Returns an error if the given name results in a malformed `Set-Cookie` header.\n    #[cfg(feature = \"cookies\")]\n    pub fn add_removal_cookie(&mut self, cookie: &Cookie<'_>) -> Result<(), HttpError> {\n        let mut removal_cookie = cookie.to_owned();\n        removal_cookie.make_removal();\n\n        HeaderValue::from_str(&removal_cookie.to_string())\n            .map(|cookie| self.headers_mut().append(header::SET_COOKIE, cookie))\n            .map_err(Into::into)\n    }\n\n    /// Remove all cookies with the given name from this response.\n    ///\n    /// Returns the number of cookies removed.\n    ///\n    /// This method can _not_ cause a browser/client to delete any of its stored cookies. Its only\n    /// purpose is to delete cookies that were added to this response using [`add_cookie`]\n    /// and [`add_removal_cookie`]. Use [`add_removal_cookie`] to send a \"removal\" cookie.\n    ///\n    /// [`add_cookie`]: Self::add_cookie\n    /// [`add_removal_cookie`]: Self::add_removal_cookie\n    #[cfg(feature = \"cookies\")]\n    pub fn del_cookie(&mut self, name: &str) -> usize {\n        let headers = self.headers_mut();\n\n        let vals: Vec<HeaderValue> = headers\n            .get_all(header::SET_COOKIE)\n            .map(|v| v.to_owned())\n            .collect();\n\n        headers.remove(header::SET_COOKIE);\n\n        let mut count: usize = 0;\n\n        for v in vals {\n            if let Ok(s) = v.to_str() {\n                if let Ok(c) = Cookie::parse_encoded(s) {\n                    if c.name() == name {\n                        count += 1;\n                        continue;\n                    }\n                }\n            }\n\n            // put set-cookie header head back if it does not validate\n            headers.append(header::SET_COOKIE, v);\n        }\n\n        count\n    }\n\n    /// Connection upgrade status\n    #[inline]\n    pub fn upgrade(&self) -> bool {\n        self.res.upgrade()\n    }\n\n    /// Keep-alive status for this connection\n    pub fn keep_alive(&self) -> bool {\n        self.res.keep_alive()\n    }\n\n    /// Returns reference to the response-local data/extensions container.\n    #[inline]\n    pub fn extensions(&self) -> Ref<'_, Extensions> {\n        self.res.extensions()\n    }\n\n    /// Returns reference to the response-local data/extensions container.\n    #[inline]\n    pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {\n        self.res.extensions_mut()\n    }\n\n    /// Returns a reference to this response's body.\n    #[inline]\n    pub fn body(&self) -> &B {\n        self.res.body()\n    }\n\n    /// Sets new body.\n    pub fn set_body<B2>(self, body: B2) -> HttpResponse<B2> {\n        HttpResponse {\n            res: self.res.set_body(body),\n            error: self.error,\n        }\n    }\n\n    /// Returns split head and body.\n    ///\n    /// # Implementation Notes\n    /// Due to internal performance optimizations, the first element of the returned tuple is an\n    /// `HttpResponse` as well but only contains the head of the response this was called on.\n    pub fn into_parts(self) -> (HttpResponse<()>, B) {\n        let (head, body) = self.res.into_parts();\n\n        (\n            HttpResponse {\n                res: head,\n                error: self.error,\n            },\n            body,\n        )\n    }\n\n    /// Drops body and returns new response.\n    pub fn drop_body(self) -> HttpResponse<()> {\n        HttpResponse {\n            res: self.res.drop_body(),\n            error: self.error,\n        }\n    }\n\n    /// Map the current body type to another using a closure, returning a new response.\n    ///\n    /// Closure receives the response head and the current body type.\n    pub fn map_body<F, B2>(self, f: F) -> HttpResponse<B2>\n    where\n        F: FnOnce(&mut ResponseHead, B) -> B2,\n    {\n        HttpResponse {\n            res: self.res.map_body(f),\n            error: self.error,\n        }\n    }\n\n    /// Map the current body type `B` to `EitherBody::Left(B)`.\n    ///\n    /// Useful for middleware which can generate their own responses.\n    #[inline]\n    pub fn map_into_left_body<R>(self) -> HttpResponse<EitherBody<B, R>> {\n        self.map_body(|_, body| EitherBody::left(body))\n    }\n\n    /// Map the current body type `B` to `EitherBody::Right(B)`.\n    ///\n    /// Useful for middleware which can generate their own responses.\n    #[inline]\n    pub fn map_into_right_body<L>(self) -> HttpResponse<EitherBody<L, B>> {\n        self.map_body(|_, body| EitherBody::right(body))\n    }\n\n    /// Map the current body to a type-erased `BoxBody`.\n    #[inline]\n    pub fn map_into_boxed_body(self) -> HttpResponse<BoxBody>\n    where\n        B: MessageBody + 'static,\n    {\n        self.map_body(|_, body| body.boxed())\n    }\n\n    /// Returns the response body, dropping all other parts.\n    pub fn into_body(self) -> B {\n        self.res.into_body()\n    }\n}\n\nimpl<B> fmt::Debug for HttpResponse<B>\nwhere\n    B: MessageBody,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"HttpResponse\")\n            .field(\"error\", &self.error)\n            .field(\"res\", &self.res)\n            .finish()\n    }\n}\n\nimpl<B> From<Response<B>> for HttpResponse<B> {\n    fn from(res: Response<B>) -> Self {\n        HttpResponse { res, error: None }\n    }\n}\n\nimpl From<Error> for HttpResponse {\n    fn from(err: Error) -> Self {\n        HttpResponse::from_error(err)\n    }\n}\n\nimpl<B> From<HttpResponse<B>> for Response<B> {\n    fn from(res: HttpResponse<B>) -> Self {\n        // this impl will always be called as part of dispatcher\n        res.res\n    }\n}\n\n// Rationale for cfg(test): this impl causes false positives on a clippy lint (async_yields_async)\n// when returning an HttpResponse from an async function/closure and it's not very useful outside of\n// tests anyway.\n#[cfg(test)]\nmod response_fut_impl {\n    use std::{\n        future::Future,\n        mem,\n        pin::Pin,\n        task::{Context, Poll},\n    };\n\n    use super::*;\n\n    // Future is only implemented for BoxBody payload type because it's the most useful for making\n    // simple handlers without async blocks. Making it generic over all MessageBody types requires a\n    // future impl on Response which would cause its body field to be, undesirably, Option<B>.\n    //\n    // This impl is not particularly efficient due to the Response construction and should probably\n    // not be invoked if performance is important. Prefer an async fn/block in such cases.\n    impl Future for HttpResponse<BoxBody> {\n        type Output = Result<Response<BoxBody>, Error>;\n\n        fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {\n            if let Some(err) = self.error.take() {\n                return Poll::Ready(Err(err));\n            }\n\n            Poll::Ready(Ok(mem::replace(\n                &mut self.res,\n                Response::new(StatusCode::default()),\n            )))\n        }\n    }\n}\n\nimpl<B> Responder for HttpResponse<B>\nwhere\n    B: MessageBody + 'static,\n{\n    type Body = B;\n\n    #[inline]\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        self\n    }\n}\n\n#[cfg(feature = \"cookies\")]\npub struct CookieIter<'a> {\n    iter: std::slice::Iter<'a, HeaderValue>,\n}\n\n#[cfg(feature = \"cookies\")]\nimpl<'a> Iterator for CookieIter<'a> {\n    type Item = Cookie<'a>;\n\n    #[inline]\n    fn next(&mut self) -> Option<Cookie<'a>> {\n        for v in self.iter.by_ref() {\n            if let Ok(c) = Cookie::parse_encoded(v.to_str().ok()?) {\n                return Some(c);\n            }\n        }\n        None\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::http::header::COOKIE;\n\n    assert_impl_all!(HttpResponse: Responder);\n    assert_impl_all!(HttpResponse<String>: Responder);\n    assert_impl_all!(HttpResponse<&'static str>: Responder);\n    assert_impl_all!(HttpResponse<crate::body::None>: Responder);\n\n    #[test]\n    fn test_debug() {\n        let resp = HttpResponse::Ok()\n            .append_header((COOKIE, HeaderValue::from_static(\"cookie1=value1; \")))\n            .append_header((COOKIE, HeaderValue::from_static(\"cookie2=value2; \")))\n            .finish();\n        let dbg = format!(\"{:?}\", resp);\n        assert!(dbg.contains(\"HttpResponse\"));\n    }\n}\n\n#[cfg(test)]\n#[cfg(feature = \"cookies\")]\nmod cookie_tests {\n    use super::*;\n\n    #[test]\n    fn removal_cookies() {\n        let mut res = HttpResponse::Ok().finish();\n        let cookie = Cookie::new(\"foo\", \"\");\n        res.add_removal_cookie(&cookie).unwrap();\n        let set_cookie_hdr = res.headers().get(header::SET_COOKIE).unwrap();\n        assert_eq!(\n            &set_cookie_hdr.as_bytes()[..25],\n            &b\"foo=; Max-Age=0; Expires=\"[..],\n            \"unexpected set-cookie value: {:?}\",\n            set_cookie_hdr.to_str()\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/rmap.rs",
    "content": "use std::{\n    borrow::{Borrow, Cow},\n    cell::RefCell,\n    collections::HashMap,\n    fmt::Write as _,\n    hash::{BuildHasher, Hash},\n    rc::{Rc, Weak},\n};\n\nuse actix_router::ResourceDef;\nuse foldhash::HashMap as FoldHashMap;\nuse url::Url;\n\nuse crate::{error::UrlGenerationError, request::HttpRequest};\n\nconst AVG_PATH_LEN: usize = 24;\n\n#[derive(Clone, Debug)]\npub struct ResourceMap {\n    pattern: ResourceDef,\n\n    /// Named resources within the tree or, for external resources, it points to isolated nodes\n    /// outside the tree.\n    named: FoldHashMap<String, Rc<ResourceMap>>,\n\n    parent: RefCell<Weak<ResourceMap>>,\n\n    /// Must be `None` for \"edge\" nodes.\n    nodes: Option<Vec<Rc<ResourceMap>>>,\n}\n\nimpl ResourceMap {\n    /// Creates a _container_ node in the `ResourceMap` tree.\n    pub fn new(root: ResourceDef) -> Self {\n        ResourceMap {\n            pattern: root,\n            named: FoldHashMap::default(),\n            parent: RefCell::new(Weak::new()),\n            nodes: Some(Vec::new()),\n        }\n    }\n\n    /// Format resource map as tree structure (unfinished).\n    #[allow(dead_code)]\n    pub(crate) fn tree(&self) -> String {\n        let mut buf = String::new();\n        self._tree(&mut buf, 0);\n        buf\n    }\n\n    pub(crate) fn _tree(&self, buf: &mut String, level: usize) {\n        if let Some(children) = &self.nodes {\n            for child in children {\n                writeln!(\n                    buf,\n                    \"{}{} {}\",\n                    \"--\".repeat(level),\n                    child.pattern.pattern().unwrap(),\n                    child\n                        .pattern\n                        .name()\n                        .map(|name| format!(\"({})\", name))\n                        .unwrap_or_else(|| \"\".to_owned())\n                )\n                .unwrap();\n\n                ResourceMap::_tree(child, buf, level + 1);\n            }\n        }\n    }\n\n    /// Adds a (possibly nested) resource.\n    ///\n    /// To add a non-prefix pattern, `nested` must be `None`.\n    /// To add external resource, supply a pattern without a leading `/`.\n    /// The root pattern of `nested`, if present, should match `pattern`.\n    pub fn add(&mut self, pattern: &mut ResourceDef, nested: Option<Rc<ResourceMap>>) {\n        pattern.set_id(self.nodes.as_ref().unwrap().len() as u16);\n\n        if let Some(new_node) = nested {\n            debug_assert_eq!(\n                &new_node.pattern, pattern,\n                \"`pattern` and `nested` mismatch\"\n            );\n            // parents absorb references to the named resources of children\n            self.named.extend(new_node.named.clone());\n            self.nodes.as_mut().unwrap().push(new_node);\n        } else {\n            let new_node = Rc::new(ResourceMap {\n                pattern: pattern.clone(),\n                named: FoldHashMap::default(),\n                parent: RefCell::new(Weak::new()),\n                nodes: None,\n            });\n\n            if let Some(name) = pattern.name() {\n                self.named.insert(name.to_owned(), Rc::clone(&new_node));\n            }\n\n            let is_external = match pattern.pattern() {\n                Some(p) => !p.is_empty() && !p.starts_with('/'),\n                None => false,\n            };\n\n            // don't add external resources to the tree\n            if !is_external {\n                self.nodes.as_mut().unwrap().push(new_node);\n            }\n        }\n    }\n\n    pub(crate) fn finish(self: &Rc<Self>) {\n        for node in self.nodes.iter().flatten() {\n            node.parent.replace(Rc::downgrade(self));\n            ResourceMap::finish(node);\n        }\n    }\n\n    /// Generate URL for named resource.\n    ///\n    /// Check [`HttpRequest::url_for`] for detailed information.\n    pub fn url_for<U, I>(\n        &self,\n        req: &HttpRequest,\n        name: &str,\n        elements: U,\n    ) -> Result<Url, UrlGenerationError>\n    where\n        U: IntoIterator<Item = I>,\n        I: AsRef<str>,\n    {\n        let mut elements = elements.into_iter();\n\n        let path = self\n            .named\n            .get(name)\n            .ok_or(UrlGenerationError::ResourceNotFound)?\n            .root_rmap_fn(String::with_capacity(AVG_PATH_LEN), |mut acc, node| {\n                node.pattern\n                    .resource_path_from_iter(&mut acc, &mut elements)\n                    .then_some(acc)\n            })\n            .ok_or(UrlGenerationError::NotEnoughElements)?;\n\n        self.url_from_path(req, path)\n    }\n\n    /// Generate URL for named resource using map of dynamic segment values.\n    ///\n    /// Check [`HttpRequest::url_for_map`] for detailed information.\n    pub fn url_for_map<K, V, S>(\n        &self,\n        req: &HttpRequest,\n        name: &str,\n        elements: &HashMap<K, V, S>,\n    ) -> Result<Url, UrlGenerationError>\n    where\n        K: Borrow<str> + Eq + Hash,\n        V: AsRef<str>,\n        S: BuildHasher,\n    {\n        let path = self\n            .named\n            .get(name)\n            .ok_or(UrlGenerationError::ResourceNotFound)?\n            .root_rmap_fn(String::with_capacity(AVG_PATH_LEN), |mut acc, node| {\n                node.pattern\n                    .resource_path_from_map(&mut acc, elements)\n                    .then_some(acc)\n            })\n            .ok_or(UrlGenerationError::NotEnoughElements)?;\n\n        self.url_from_path(req, path)\n    }\n\n    /// Generate URL for named resource using an iterator of key-value pairs.\n    ///\n    /// Check [`HttpRequest::url_for_iter`] for detailed information.\n    pub fn url_for_iter<K, V, I>(\n        &self,\n        req: &HttpRequest,\n        name: &str,\n        elements: I,\n    ) -> Result<Url, UrlGenerationError>\n    where\n        I: IntoIterator<Item = (K, V)>,\n        K: Borrow<str> + Eq + Hash,\n        V: AsRef<str>,\n    {\n        let elements = elements.into_iter().collect::<FoldHashMap<K, V>>();\n        self.url_for_map(req, name, &elements)\n    }\n\n    fn url_from_path(&self, req: &HttpRequest, path: String) -> Result<Url, UrlGenerationError> {\n        let (base, path): (Cow<'_, _>, _) = if path.starts_with('/') {\n            // build full URL from connection info parts and resource path\n            let conn = req.connection_info();\n            let base = format!(\"{}://{}\", conn.scheme(), conn.host());\n            (Cow::Owned(base), path.as_str())\n        } else {\n            // external resource; third slash would be the root slash in the path\n            let third_slash_index = path\n                .char_indices()\n                .filter_map(|(i, c)| (c == '/').then_some(i))\n                .nth(2)\n                .unwrap_or(path.len());\n\n            (\n                Cow::Borrowed(&path[..third_slash_index]),\n                &path[third_slash_index..],\n            )\n        };\n\n        let mut url = Url::parse(&base)?;\n        url.set_path(path);\n        Ok(url)\n    }\n\n    /// Returns true if there is a resource that would match `path`.\n    pub fn has_resource(&self, path: &str) -> bool {\n        self.find_matching_node(path).is_some()\n    }\n\n    /// Returns the name of the route that matches the given path or None if no full match\n    /// is possible or the matching resource is not named.\n    pub fn match_name(&self, path: &str) -> Option<&str> {\n        self.find_matching_node(path)?.pattern.name()\n    }\n\n    /// Returns the full resource pattern matched against a path or None if no full match\n    /// is possible.\n    pub fn match_pattern(&self, path: &str) -> Option<String> {\n        self.find_matching_node(path)?.root_rmap_fn(\n            String::with_capacity(AVG_PATH_LEN),\n            |mut acc, node| {\n                let pattern = node.pattern.pattern()?;\n                acc.push_str(pattern);\n                Some(acc)\n            },\n        )\n    }\n\n    pub(crate) fn is_resource_path_match(&self, resource_path: &[u16]) -> bool {\n        self.find_node_by_resource_path(resource_path)\n            .is_some_and(|node| node.nodes.is_none())\n    }\n\n    pub(crate) fn match_name_by_resource_path(&self, resource_path: &[u16]) -> Option<&str> {\n        self.find_node_by_resource_path(resource_path)?\n            .pattern\n            .name()\n    }\n\n    pub(crate) fn match_pattern_by_resource_path(&self, resource_path: &[u16]) -> Option<String> {\n        self.find_node_by_resource_path(resource_path)?\n            .root_rmap_fn(String::with_capacity(AVG_PATH_LEN), |mut acc, node| {\n                let pattern = node.pattern.pattern()?;\n                acc.push_str(pattern);\n                Some(acc)\n            })\n    }\n\n    fn find_matching_node(&self, path: &str) -> Option<&ResourceMap> {\n        self._find_matching_node(path).flatten()\n    }\n\n    fn find_node_by_resource_path(&self, resource_path: &[u16]) -> Option<&ResourceMap> {\n        let mut node = self;\n\n        for id in resource_path {\n            node = node.nodes.as_ref()?.get(*id as usize)?;\n        }\n\n        Some(node)\n    }\n\n    /// Returns `None` if root pattern doesn't match;\n    /// `Some(None)` if root pattern matches but there is no matching child pattern.\n    /// Don't search sideways when `Some(none)` is returned.\n    fn _find_matching_node(&self, path: &str) -> Option<Option<&ResourceMap>> {\n        let matched_len = self.pattern.find_match(path)?;\n        let path = &path[matched_len..];\n\n        Some(match &self.nodes {\n            // find first sub-node to match remaining path\n            Some(nodes) => nodes\n                .iter()\n                .filter_map(|node| node._find_matching_node(path))\n                .next()\n                .flatten(),\n\n            // only terminate at edge nodes\n            None => Some(self),\n        })\n    }\n\n    /// Find `self`'s highest ancestor and then run `F`, providing `B`, in that rmap context.\n    fn root_rmap_fn<F, B>(&self, init: B, mut f: F) -> Option<B>\n    where\n        F: FnMut(B, &ResourceMap) -> Option<B>,\n    {\n        self._root_rmap_fn(init, &mut f)\n    }\n\n    /// Run `F`, providing `B`, if `self` is top-level resource map, else recurse to parent map.\n    fn _root_rmap_fn<F, B>(&self, init: B, f: &mut F) -> Option<B>\n    where\n        F: FnMut(B, &ResourceMap) -> Option<B>,\n    {\n        let data = match self.parent.borrow().upgrade() {\n            Some(ref parent) => parent._root_rmap_fn(init, f)?,\n            None => init,\n        };\n\n        f(data, self)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn extract_matched_pattern() {\n        let mut root = ResourceMap::new(ResourceDef::root_prefix(\"\"));\n\n        let mut user_map = ResourceMap::new(ResourceDef::root_prefix(\"/user/{id}\"));\n        user_map.add(&mut ResourceDef::new(\"/\"), None);\n        user_map.add(&mut ResourceDef::new(\"/profile\"), None);\n        user_map.add(&mut ResourceDef::new(\"/article/{id}\"), None);\n        user_map.add(&mut ResourceDef::new(\"/post/{post_id}\"), None);\n        user_map.add(\n            &mut ResourceDef::new(\"/post/{post_id}/comment/{comment_id}\"),\n            None,\n        );\n\n        root.add(&mut ResourceDef::new(\"/info\"), None);\n        root.add(&mut ResourceDef::new(\"/v{version:[[:digit:]]{1}}\"), None);\n        root.add(\n            &mut ResourceDef::root_prefix(\"/user/{id}\"),\n            Some(Rc::new(user_map)),\n        );\n        root.add(&mut ResourceDef::new(\"/info\"), None);\n\n        let root = Rc::new(root);\n        ResourceMap::finish(&root);\n\n        // sanity check resource map setup\n\n        assert!(root.has_resource(\"/info\"));\n        assert!(!root.has_resource(\"/bar\"));\n\n        assert!(root.has_resource(\"/v1\"));\n        assert!(root.has_resource(\"/v2\"));\n        assert!(!root.has_resource(\"/v33\"));\n\n        assert!(!root.has_resource(\"/user/22\"));\n        assert!(root.has_resource(\"/user/22/\"));\n        assert!(root.has_resource(\"/user/22/profile\"));\n\n        // extract patterns from paths\n\n        assert!(root.match_pattern(\"/bar\").is_none());\n        assert!(root.match_pattern(\"/v44\").is_none());\n\n        assert_eq!(root.match_pattern(\"/info\"), Some(\"/info\".to_owned()));\n        assert_eq!(\n            root.match_pattern(\"/v1\"),\n            Some(\"/v{version:[[:digit:]]{1}}\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/v2\"),\n            Some(\"/v{version:[[:digit:]]{1}}\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/user/22/profile\"),\n            Some(\"/user/{id}/profile\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/user/602CFB82-7709-4B17-ADCF-4C347B6F2203/profile\"),\n            Some(\"/user/{id}/profile\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/user/22/article/44\"),\n            Some(\"/user/{id}/article/{id}\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/user/22/post/my-post\"),\n            Some(\"/user/{id}/post/{post_id}\".to_owned())\n        );\n        assert_eq!(\n            root.match_pattern(\"/user/22/post/other-post/comment/42\"),\n            Some(\"/user/{id}/post/{post_id}/comment/{comment_id}\".to_owned())\n        );\n    }\n\n    #[test]\n    fn extract_matched_name() {\n        let mut root = ResourceMap::new(ResourceDef::root_prefix(\"\"));\n\n        let mut rdef = ResourceDef::new(\"/info\");\n        rdef.set_name(\"root_info\");\n        root.add(&mut rdef, None);\n\n        let mut user_map = ResourceMap::new(ResourceDef::root_prefix(\"/user/{id}\"));\n        let mut rdef = ResourceDef::new(\"/\");\n        user_map.add(&mut rdef, None);\n\n        let mut rdef = ResourceDef::new(\"/post/{post_id}\");\n        rdef.set_name(\"user_post\");\n        user_map.add(&mut rdef, None);\n\n        root.add(\n            &mut ResourceDef::root_prefix(\"/user/{id}\"),\n            Some(Rc::new(user_map)),\n        );\n\n        let root = Rc::new(root);\n        ResourceMap::finish(&root);\n\n        // sanity check resource map setup\n\n        assert!(root.has_resource(\"/info\"));\n        assert!(!root.has_resource(\"/bar\"));\n\n        assert!(!root.has_resource(\"/user/22\"));\n        assert!(root.has_resource(\"/user/22/\"));\n        assert!(root.has_resource(\"/user/22/post/55\"));\n\n        // extract patterns from paths\n\n        assert!(root.match_name(\"/bar\").is_none());\n        assert!(root.match_name(\"/v44\").is_none());\n\n        assert_eq!(root.match_name(\"/info\"), Some(\"root_info\"));\n        assert_eq!(root.match_name(\"/user/22\"), None);\n        assert_eq!(root.match_name(\"/user/22/\"), None);\n        assert_eq!(root.match_name(\"/user/22/post/55\"), Some(\"user_post\"));\n    }\n\n    #[test]\n    fn bug_fix_issue_1582_debug_print_exits() {\n        // ref: https://github.com/actix/actix-web/issues/1582\n        let mut root = ResourceMap::new(ResourceDef::root_prefix(\"\"));\n\n        let mut user_map = ResourceMap::new(ResourceDef::root_prefix(\"/user/{id}\"));\n        user_map.add(&mut ResourceDef::new(\"/\"), None);\n        user_map.add(&mut ResourceDef::new(\"/profile\"), None);\n        user_map.add(&mut ResourceDef::new(\"/article/{id}\"), None);\n        user_map.add(&mut ResourceDef::new(\"/post/{post_id}\"), None);\n        user_map.add(\n            &mut ResourceDef::new(\"/post/{post_id}/comment/{comment_id}\"),\n            None,\n        );\n\n        root.add(\n            &mut ResourceDef::root_prefix(\"/user/{id}\"),\n            Some(Rc::new(user_map)),\n        );\n\n        let root = Rc::new(root);\n        ResourceMap::finish(&root);\n\n        // check root has no parent\n        assert!(root.parent.borrow().upgrade().is_none());\n        // check child has parent reference\n        assert!(root.nodes.as_ref().unwrap()[0]\n            .parent\n            .borrow()\n            .upgrade()\n            .is_some());\n        // check child's parent root id matches root's root id\n        assert!(Rc::ptr_eq(\n            &root.nodes.as_ref().unwrap()[0]\n                .parent\n                .borrow()\n                .upgrade()\n                .unwrap(),\n            &root\n        ));\n\n        let output = format!(\"{:?}\", root);\n        assert!(output.starts_with(\"ResourceMap {\"));\n        assert!(output.ends_with(\" }\"));\n    }\n\n    #[test]\n    fn short_circuit() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut user_root = ResourceDef::prefix(\"/user\");\n        let mut user_map = ResourceMap::new(user_root.clone());\n        user_map.add(&mut ResourceDef::new(\"/u1\"), None);\n        user_map.add(&mut ResourceDef::new(\"/u2\"), None);\n\n        root.add(&mut ResourceDef::new(\"/user/u3\"), None);\n        root.add(&mut user_root, Some(Rc::new(user_map)));\n        root.add(&mut ResourceDef::new(\"/user/u4\"), None);\n\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        assert!(rmap.has_resource(\"/user/u1\"));\n        assert!(rmap.has_resource(\"/user/u2\"));\n        assert!(rmap.has_resource(\"/user/u3\"));\n        assert!(!rmap.has_resource(\"/user/u4\"));\n    }\n\n    #[test]\n    fn url_for() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut user_scope_rdef = ResourceDef::prefix(\"/user\");\n        let mut user_scope_map = ResourceMap::new(user_scope_rdef.clone());\n\n        let mut user_rdef = ResourceDef::new(\"/{user_id}\");\n        let mut user_map = ResourceMap::new(user_rdef.clone());\n\n        let mut post_rdef = ResourceDef::new(\"/post/{sub_id}\");\n        post_rdef.set_name(\"post\");\n\n        user_map.add(&mut post_rdef, None);\n        user_scope_map.add(&mut user_rdef, Some(Rc::new(user_map)));\n        root.add(&mut user_scope_rdef, Some(Rc::new(user_scope_map)));\n\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        let mut req = crate::test::TestRequest::default();\n        req.set_server_hostname(\"localhost:8888\");\n        let req = req.to_http_request();\n\n        let url = rmap\n            .url_for(&req, \"post\", [\"u123\", \"foobar\"])\n            .unwrap()\n            .to_string();\n        assert_eq!(url, \"http://localhost:8888/user/u123/post/foobar\");\n\n        assert!(rmap.url_for(&req, \"missing\", [\"u123\"]).is_err());\n    }\n\n    #[test]\n    fn url_for_parser() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut rdef_1 = ResourceDef::new(\"/{var}\");\n        rdef_1.set_name(\"internal\");\n\n        let mut rdef_2 = ResourceDef::new(\"http://host.dom/{var}\");\n        rdef_2.set_name(\"external.1\");\n\n        let mut rdef_3 = ResourceDef::new(\"{var}\");\n        rdef_3.set_name(\"external.2\");\n\n        root.add(&mut rdef_1, None);\n        root.add(&mut rdef_2, None);\n        root.add(&mut rdef_3, None);\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        let mut req = crate::test::TestRequest::default();\n        req.set_server_hostname(\"localhost:8888\");\n        let req = req.to_http_request();\n\n        const INPUT: &[&str] = &[\"a/../quick brown%20fox/%nan?query#frag\"];\n        const OUTPUT: &str = \"/quick%20brown%20fox/%nan%3Fquery%23frag\";\n\n        let url = rmap.url_for(&req, \"internal\", INPUT).unwrap();\n        assert_eq!(url.path(), OUTPUT);\n\n        let url = rmap.url_for(&req, \"external.1\", INPUT).unwrap();\n        assert_eq!(url.path(), OUTPUT);\n\n        assert!(rmap.url_for(&req, \"external.2\", INPUT).is_err());\n        assert!(rmap.url_for(&req, \"external.2\", [\"\"]).is_err());\n    }\n\n    #[test]\n    fn external_resource_with_no_name() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut rdef = ResourceDef::new(\"https://duck.com/{query}\");\n        root.add(&mut rdef, None);\n\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        assert!(!rmap.has_resource(\"https://duck.com/abc\"));\n    }\n\n    #[test]\n    fn external_resource_with_name() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut rdef = ResourceDef::new(\"https://duck.com/{query}\");\n        rdef.set_name(\"duck\");\n        root.add(&mut rdef, None);\n\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        assert!(!rmap.has_resource(\"https://duck.com/abc\"));\n\n        let mut req = crate::test::TestRequest::default();\n        req.set_server_hostname(\"localhost:8888\");\n        let req = req.to_http_request();\n\n        assert_eq!(\n            rmap.url_for(&req, \"duck\", [\"abcd\"]).unwrap().to_string(),\n            \"https://duck.com/abcd\"\n        );\n    }\n\n    #[test]\n    fn url_for_override_within_map() {\n        let mut root = ResourceMap::new(ResourceDef::prefix(\"\"));\n\n        let mut foo_rdef = ResourceDef::prefix(\"/foo\");\n        let mut foo_map = ResourceMap::new(foo_rdef.clone());\n        let mut nested_rdef = ResourceDef::new(\"/nested\");\n        nested_rdef.set_name(\"nested\");\n        foo_map.add(&mut nested_rdef, None);\n        root.add(&mut foo_rdef, Some(Rc::new(foo_map)));\n\n        let mut foo_rdef = ResourceDef::prefix(\"/bar\");\n        let mut foo_map = ResourceMap::new(foo_rdef.clone());\n        let mut nested_rdef = ResourceDef::new(\"/nested\");\n        nested_rdef.set_name(\"nested\");\n        foo_map.add(&mut nested_rdef, None);\n        root.add(&mut foo_rdef, Some(Rc::new(foo_map)));\n\n        let rmap = Rc::new(root);\n        ResourceMap::finish(&rmap);\n\n        let req = crate::test::TestRequest::default().to_http_request();\n\n        let url = rmap.url_for(&req, \"nested\", [\"\"; 0]).unwrap().to_string();\n        assert_eq!(url, \"http://localhost:8080/bar/nested\");\n\n        assert!(rmap.url_for(&req, \"missing\", [\"u123\"]).is_err());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/route.rs",
    "content": "use std::{mem, rc::Rc};\n\nuse actix_http::{body::MessageBody, Method};\nuse actix_service::{\n    apply,\n    boxed::{self, BoxService},\n    fn_service, Service, ServiceFactory, ServiceFactoryExt, Transform,\n};\nuse futures_core::future::LocalBoxFuture;\n\nuse crate::{\n    guard::{self, Guard},\n    handler::{handler_service, Handler},\n    middleware::Compat,\n    service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse},\n    Error, FromRequest, HttpResponse, Responder,\n};\n\n/// A request handler with [guards](guard).\n///\n/// Route uses a builder-like pattern for configuration. If handler is not set, a `404 Not Found`\n/// handler is used.\npub struct Route {\n    service: BoxedHttpServiceFactory,\n    guards: Rc<Vec<Box<dyn Guard>>>,\n    wrapped: bool,\n}\n\nimpl Route {\n    /// Create new route which matches any request.\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Route {\n        Route {\n            service: boxed::factory(fn_service(|req: ServiceRequest| async {\n                Ok(req.into_response(HttpResponse::NotFound()))\n            })),\n            guards: Rc::new(Vec::new()),\n            wrapped: false,\n        }\n    }\n\n    /// Registers a route middleware.\n    ///\n    /// `mw` is a middleware component (type), that can modify the requests and responses handled by\n    /// this `Route`.\n    ///\n    /// This middleware wraps the currently configured route service. Call this method after\n    /// [`Route::to`] or [`Route::service`] so the middleware is applied to the final handler.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::{web, HttpResponse, middleware};\n    /// web::get()\n    ///     .to(|| async { HttpResponse::Ok() })\n    ///     .wrap(middleware::Logger::default());\n    /// ```\n    ///\n    /// See [`App::wrap`](crate::App::wrap) for more details.\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap<M, B>(self, mw: M) -> Route\n    where\n        M: Transform<\n                BoxService<ServiceRequest, ServiceResponse, Error>,\n                ServiceRequest,\n                Response = ServiceResponse<B>,\n                Error = Error,\n                InitError = (),\n            > + 'static,\n        B: MessageBody + 'static,\n    {\n        Route {\n            service: boxed::factory(apply(Compat::new(mw), self.service)),\n            guards: self.guards,\n            wrapped: true,\n        }\n    }\n\n    pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {\n        mem::take(Rc::get_mut(&mut self.guards).unwrap())\n    }\n\n    #[cold]\n    #[inline(never)]\n    #[track_caller]\n    fn panic_after_wrap(replaced: &str, example: &str) -> ! {\n        panic!(\n            \"Route middleware was already registered with `.wrap()`. \\\n             Calling `.{replaced}()` now would replace the wrapped service and silently drop middleware. \\\n             Call `.{replaced}()` before `.wrap()` (for example: `{example}`).\"\n        );\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for Route {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = RouteService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        let fut = self.service.new_service(());\n        let guards = Rc::clone(&self.guards);\n\n        Box::pin(async move {\n            let service = fut.await?;\n            Ok(RouteService { service, guards })\n        })\n    }\n}\n\npub struct RouteService {\n    service: BoxService<ServiceRequest, ServiceResponse, Error>,\n    guards: Rc<Vec<Box<dyn Guard>>>,\n}\n\nimpl RouteService {\n    // TODO(breaking): remove pass by ref mut\n    #[allow(clippy::needless_pass_by_ref_mut)]\n    pub fn check(&self, req: &mut ServiceRequest) -> bool {\n        let guard_ctx = req.guard_ctx();\n\n        for guard in self.guards.iter() {\n            if !guard.check(&guard_ctx) {\n                return false;\n            }\n        }\n        true\n    }\n}\n\nimpl Service<ServiceRequest> for RouteService {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        self.service.call(req)\n    }\n}\n\nimpl Route {\n    /// Add method guard to the route.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::*;\n    /// # fn main() {\n    /// App::new().service(web::resource(\"/path\").route(\n    ///     web::get()\n    ///         .method(http::Method::CONNECT)\n    ///         .guard(guard::Header(\"content-type\", \"text/plain\"))\n    ///         .to(|req: HttpRequest| HttpResponse::Ok()))\n    /// );\n    /// # }\n    /// ```\n    pub fn method(mut self, method: Method) -> Self {\n        Rc::get_mut(&mut self.guards)\n            .unwrap()\n            .push(Box::new(guard::Method(method)));\n        self\n    }\n\n    /// Add guard to the route.\n    ///\n    /// # Examples\n    /// ```\n    /// # use actix_web::*;\n    /// # fn main() {\n    /// App::new().service(web::resource(\"/path\").route(\n    ///     web::route()\n    ///         .guard(guard::Get())\n    ///         .guard(guard::Header(\"content-type\", \"text/plain\"))\n    ///         .to(|req: HttpRequest| HttpResponse::Ok()))\n    /// );\n    /// # }\n    /// ```\n    pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {\n        Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f));\n        self\n    }\n\n    #[cfg(feature = \"experimental-introspection\")]\n    pub(crate) fn guards(&self) -> &Vec<Box<dyn Guard>> {\n        &self.guards\n    }\n\n    /// Set handler function, use request extractors for parameters.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{web, http, App};\n    /// use serde::Deserialize;\n    ///\n    /// #[derive(Deserialize)]\n    /// struct Info {\n    ///     username: String,\n    /// }\n    ///\n    /// /// extract path info using serde\n    /// async fn index(info: web::Path<Info>) -> String {\n    ///     format!(\"Welcome {}!\", info.username)\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/{username}/index.html\") // <- define path parameters\n    ///         .route(web::get().to(index))        // <- register handler\n    /// );\n    /// ```\n    ///\n    /// It is possible to use multiple extractors for one handler function.\n    /// ```\n    /// # use std::collections::HashMap;\n    /// # use serde::Deserialize;\n    /// use actix_web::{web, App};\n    ///\n    /// #[derive(Deserialize)]\n    /// struct Info {\n    ///     username: String,\n    /// }\n    ///\n    /// /// extract path info using serde\n    /// async fn index(\n    ///     path: web::Path<Info>,\n    ///     query: web::Query<HashMap<String, String>>,\n    ///     body: web::Json<Info>\n    /// ) -> String {\n    ///     format!(\"Welcome {}!\", path.username)\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::resource(\"/{username}/index.html\") // <- define path parameters\n    ///         .route(web::get().to(index))\n    /// );\n    /// ```\n    ///\n    /// # Panics\n    /// Panics if called after [`Route::wrap`], since this would replace the wrapped service and\n    /// silently discard middleware.\n    #[track_caller]\n    pub fn to<F, Args>(mut self, handler: F) -> Self\n    where\n        F: Handler<Args>,\n        Args: FromRequest + 'static,\n        F::Output: Responder + 'static,\n    {\n        if self.wrapped {\n            Self::panic_after_wrap(\"to\", \"web::get().to(handler).wrap(mw)\");\n        }\n\n        self.service = handler_service(handler);\n        self\n    }\n\n    /// Set raw service to be constructed and called as the request handler.\n    ///\n    /// # Examples\n    /// ```\n    /// # use std::convert::Infallible;\n    /// # use futures_util::future::LocalBoxFuture;\n    /// # use actix_web::{*, dev::*, http::header};\n    /// struct HelloWorld;\n    ///\n    /// impl Service<ServiceRequest> for HelloWorld {\n    ///     type Response = ServiceResponse;\n    ///     type Error = Infallible;\n    ///     type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n    ///\n    ///     dev::always_ready!();\n    ///\n    ///     fn call(&self, req: ServiceRequest) -> Self::Future {\n    ///         let (req, _) = req.into_parts();\n    ///\n    ///         let res = HttpResponse::Ok()\n    ///             .insert_header(header::ContentType::plaintext())\n    ///             .body(\"Hello world!\");\n    ///\n    ///         Box::pin(async move { Ok(ServiceResponse::new(req, res)) })\n    ///     }\n    /// }\n    ///\n    /// App::new().route(\n    ///     \"/\",\n    ///     web::get().service(fn_factory(|| async { Ok(HelloWorld) })),\n    /// );\n    /// ```\n    ///\n    /// # Panics\n    /// Panics if called after [`Route::wrap`], since this would replace the wrapped service and\n    /// silently discard middleware.\n    #[track_caller]\n    pub fn service<S, E>(mut self, service_factory: S) -> Self\n    where\n        S: ServiceFactory<\n                ServiceRequest,\n                Response = ServiceResponse,\n                Error = E,\n                InitError = (),\n                Config = (),\n            > + 'static,\n        E: Into<Error> + 'static,\n    {\n        if self.wrapped {\n            Self::panic_after_wrap(\"service\", \"web::get().service(factory).wrap(mw)\");\n        }\n\n        self.service = boxed::factory(service_factory.map_err(Into::into));\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{convert::Infallible, time::Duration};\n\n    use actix_rt::time::sleep;\n    use bytes::Bytes;\n    use futures_core::future::LocalBoxFuture;\n    use serde::Serialize;\n\n    use crate::{\n        dev::{always_ready, fn_factory, fn_service, Service},\n        error,\n        http::{header, Method, StatusCode},\n        middleware::{DefaultHeaders, Logger},\n        service::{ServiceRequest, ServiceResponse},\n        test::{call_service, init_service, read_body, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    #[derive(Serialize, PartialEq, Debug)]\n    struct MyObject {\n        name: String,\n    }\n\n    #[actix_rt::test]\n    async fn test_route() {\n        let srv =\n            init_service(\n                App::new()\n                    .service(\n                        web::resource(\"/test\")\n                            .route(web::get().to(HttpResponse::Ok))\n                            .route(web::put().to(|| async {\n                                Err::<HttpResponse, _>(error::ErrorBadRequest(\"err\"))\n                            }))\n                            .route(web::post().to(|| async {\n                                sleep(Duration::from_millis(100)).await;\n                                Ok::<_, Infallible>(HttpResponse::Created())\n                            }))\n                            .route(web::delete().to(|| async {\n                                sleep(Duration::from_millis(100)).await;\n                                Err::<HttpResponse, _>(error::ErrorBadRequest(\"err\"))\n                            })),\n                    )\n                    .service(web::resource(\"/json\").route(web::get().to(|| async {\n                        sleep(Duration::from_millis(25)).await;\n                        web::Json(MyObject {\n                            name: \"test\".to_string(),\n                        })\n                    }))),\n            )\n            .await;\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::GET)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::POST)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::CREATED);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::PUT)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::DELETE)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/test\")\n            .method(Method::HEAD)\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n\n        let req = TestRequest::with_uri(\"/json\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"{\\\"name\\\":\\\"test\\\"}\"));\n    }\n\n    #[actix_rt::test]\n    async fn route_middleware() {\n        let srv = init_service(\n            App::new()\n                .route(\"/\", web::get().to(HttpResponse::Ok).wrap(Logger::default()))\n                .service(\n                    web::resource(\"/test\")\n                        .route(web::get().to(HttpResponse::Ok))\n                        .route(\n                            web::post()\n                                .to(HttpResponse::Created)\n                                .wrap(DefaultHeaders::new().add((\"x-test\", \"x-posted\"))),\n                        )\n                        .route(\n                            web::delete()\n                                .to(HttpResponse::Accepted)\n                                // logger changes body type, proving Compat is not needed\n                                .wrap(Logger::default()),\n                        ),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/test\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        assert!(!res.headers().contains_key(\"x-test\"));\n\n        let req = TestRequest::post().uri(\"/test\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::CREATED);\n        assert_eq!(res.headers().get(\"x-test\").unwrap(), \"x-posted\");\n\n        let req = TestRequest::delete().uri(\"/test\").to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::ACCEPTED);\n    }\n\n    #[actix_rt::test]\n    async fn test_service_handler() {\n        struct HelloWorld;\n\n        impl Service<ServiceRequest> for HelloWorld {\n            type Response = ServiceResponse;\n            type Error = crate::Error;\n            type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n            always_ready!();\n\n            fn call(&self, req: ServiceRequest) -> Self::Future {\n                let (req, _) = req.into_parts();\n\n                let res = HttpResponse::Ok()\n                    .insert_header(header::ContentType::plaintext())\n                    .body(\"Hello world!\");\n\n                Box::pin(async move { Ok(ServiceResponse::new(req, res)) })\n            }\n        }\n\n        let srv = init_service(\n            App::new()\n                .route(\n                    \"/hello\",\n                    web::get().service(fn_factory(|| async { Ok(HelloWorld) })),\n                )\n                .route(\n                    \"/bye\",\n                    web::get().service(fn_factory(|| async {\n                        Ok::<_, ()>(fn_service(|req: ServiceRequest| async {\n                            let (req, _) = req.into_parts();\n\n                            let res = HttpResponse::Ok()\n                                .insert_header(header::ContentType::plaintext())\n                                .body(\"Goodbye, and thanks for all the fish!\");\n\n                            Ok::<_, Infallible>(ServiceResponse::new(req, res))\n                        }))\n                    })),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::get().uri(\"/hello\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"Hello world!\"));\n\n        let req = TestRequest::get().uri(\"/bye\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(\n            body,\n            Bytes::from_static(b\"Goodbye, and thanks for all the fish!\")\n        );\n    }\n\n    #[test]\n    #[should_panic(expected = \"Route middleware was already registered with `.wrap()`\")]\n    fn wrap_before_to_panics() {\n        web::get()\n            .wrap(DefaultHeaders::new().add((\"x-test\", \"x-value\")))\n            .to(HttpResponse::Ok);\n    }\n\n    #[test]\n    #[should_panic(expected = \"Route middleware was already registered with `.wrap()`\")]\n    fn wrap_before_service_panics() {\n        web::get()\n            .wrap(DefaultHeaders::new().add((\"x-test\", \"x-value\")))\n            .service(fn_factory(|| async {\n                Ok::<_, ()>(fn_service(|req: ServiceRequest| async {\n                    let (req, _) = req.into_parts();\n                    Ok::<_, Infallible>(ServiceResponse::new(req, HttpResponse::Ok().finish()))\n                }))\n            }));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/rt.rs",
    "content": "//! A selection of re-exports from [`tokio`] and [`actix-rt`].\n//!\n//! Actix Web runs on [Tokio], providing full[^compat] compatibility with its huge ecosystem of\n//! crates. Each of the server's workers uses a single-threaded runtime. Read more about the\n//! architecture in [`actix-rt`]'s docs.\n//!\n//! # Running Actix Web Without Macros\n//!\n//! ```no_run\n//! use actix_web::{middleware, rt, web, App, HttpRequest, HttpServer};\n//!\n//! async fn index(req: HttpRequest) -> &'static str {\n//!     println!(\"REQ: {:?}\", req);\n//!     \"Hello world!\\r\\n\"\n//! }\n//!\n//! fn main() -> std::io::Result<()> {\n//!     rt::System::new().block_on(\n//!         HttpServer::new(|| {\n//!             App::new().service(web::resource(\"/\").route(web::get().to(index)))\n//!         })\n//!         .bind((\"127.0.0.1\", 8080))?\n//!         .run()\n//!     )\n//! }\n//! ```\n//!\n//! # Running Actix Web Using `#[tokio::main]`\n//!\n//! If you need to run something that uses Tokio's work stealing functionality alongside Actix Web,\n//! you can run Actix Web under `#[tokio::main]`. The [`Server`](crate::dev::Server) object returned\n//! from [`HttpServer::run`](crate::HttpServer::run) can also be [`spawn`]ed, if preferred.\n//!\n//! Note that `actix` actor support (and therefore WebSocket support through `actix-web-actors`)\n//! still require `#[actix_web::main]` since they require a [`System`] to be set up.\n//!\n//! Also note that calls to this module's [`spawn()`] re-export require an `#[actix_web::main]`\n//! runtime (or a manually configured `LocalSet`) since it makes calls into to the current thread's\n//! `LocalSet`, which `#[tokio::main]` does not set up.\n//!\n//! ```no_run\n//! use actix_web::{get, middleware, rt, web, App, HttpRequest, HttpServer};\n//!\n//! #[get(\"/\")]\n//! async fn index(req: HttpRequest) -> &'static str {\n//!     println!(\"REQ: {:?}\", req);\n//!     \"Hello world!\\r\\n\"\n//! }\n//!\n//! #[tokio::main]\n//! async fn main() -> std::io::Result<()> {\n//!     HttpServer::new(|| {\n//!         App::new().service(index)\n//!     })\n//!     .bind((\"127.0.0.1\", 8080))?\n//!     .run()\n//!     .await\n//! }\n//! ```\n//!\n//! [^compat]: Crates that use Tokio's [`block_in_place`] will not work with Actix Web. Fortunately,\n//!   the vast majority of Tokio-based crates do not use it.\n//!\n//! [`actix-rt`]: https://docs.rs/actix-rt\n//! [`tokio`]: https://docs.rs/tokio\n//! [Tokio]: https://docs.rs/tokio\n//! [`spawn`]: https://docs.rs/tokio/1/tokio/fn.spawn.html\n//! [`block_in_place`]: https://docs.rs/tokio/1/tokio/task/fn.block_in_place.html\n\n// In particular:\n// - Omit the `Arbiter` types because they have limited value here.\n// - Re-export but hide the runtime macros because they won't work directly but are required for\n//   `#[actix_web::main]` and `#[actix_web::test]` to work.\n\n#[cfg(feature = \"macros\")]\n#[doc(hidden)]\npub use actix_macros::{main, test};\npub use actix_rt::{net, pin, signal, spawn, task, time, Runtime, System, SystemRunner};\n"
  },
  {
    "path": "actix-web/src/scope.rs",
    "content": "use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc};\n\nuse actix_http::{body::MessageBody, Extensions};\nuse actix_router::{ResourceDef, Router};\nuse actix_service::{\n    apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt,\n    Transform,\n};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::future::join_all;\n\nuse crate::{\n    config::ServiceConfig,\n    data::Data,\n    dev::AppService,\n    guard::Guard,\n    rmap::ResourceMap,\n    service::{\n        AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory,\n        ServiceFactoryWrapper, ServiceRequest, ServiceResponse,\n    },\n    Error, Resource, Route,\n};\n\ntype Guards = Vec<Box<dyn Guard>>;\n\n/// A collection of [`Route`]s, [`Resource`]s, or other services that share a common path prefix.\n///\n/// The `Scope`'s path can contain [dynamic segments]. The dynamic segments can be extracted from\n/// requests using the [`Path`](crate::web::Path) extractor or\n/// with [`HttpRequest::match_info()`](crate::HttpRequest::match_info).\n///\n/// # Avoid Trailing Slashes\n/// Avoid using trailing slashes in the scope prefix (e.g., `web::scope(\"/scope/\")`). It will almost\n/// certainly not have the expected behavior. See the [documentation on resource definitions][pat]\n/// to understand why this is the case and how to correctly construct scope/prefix definitions.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App, HttpResponse};\n///\n/// let app = App::new().service(\n///     web::scope(\"/{project_id}\")\n///         .service(web::resource(\"/path1\").to(|| async { \"OK\" }))\n///         .service(web::resource(\"/path2\").route(web::get().to(|| HttpResponse::Ok())))\n///         .service(web::resource(\"/path3\").route(web::head().to(HttpResponse::MethodNotAllowed)))\n/// );\n/// ```\n///\n/// In the above example three routes get registered:\n/// - /{project_id}/path1 - responds to all HTTP methods\n/// - /{project_id}/path2 - responds to `GET` requests\n/// - /{project_id}/path3 - responds to `HEAD` requests\n///\n/// [pat]: crate::dev::ResourceDef#prefix-resources\n/// [dynamic segments]: crate::dev::ResourceDef#dynamic-segments\npub struct Scope<T = ScopeEndpoint> {\n    endpoint: T,\n    rdef: String,\n    app_data: Option<Extensions>,\n    services: Vec<Box<dyn AppServiceFactory>>,\n    guards: Vec<Box<dyn Guard>>,\n    default: Option<Rc<BoxedHttpServiceFactory>>,\n    external: Vec<ResourceDef>,\n    factory_ref: Rc<RefCell<Option<ScopeFactory>>>,\n}\n\nimpl Scope {\n    /// Create a new scope\n    pub fn new(path: &str) -> Scope {\n        let factory_ref = Rc::new(RefCell::new(None));\n\n        Scope {\n            endpoint: ScopeEndpoint::new(Rc::clone(&factory_ref)),\n            rdef: path.to_string(),\n            app_data: None,\n            guards: Vec::new(),\n            services: Vec::new(),\n            default: None,\n            external: Vec::new(),\n            factory_ref,\n        }\n    }\n}\n\nimpl<T> Scope<T>\nwhere\n    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,\n{\n    /// Add match guard to a scope.\n    ///\n    /// ```\n    /// use actix_web::{web, guard, App, HttpRequest, HttpResponse};\n    ///\n    /// async fn index(data: web::Path<(String, String)>) -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::scope(\"/app\")\n    ///         .guard(guard::Header(\"content-type\", \"text/plain\"))\n    ///         .route(\"/test1\", web::get().to(index))\n    ///         .route(\"/test2\", web::post().to(|r: HttpRequest| {\n    ///             HttpResponse::MethodNotAllowed()\n    ///         }))\n    /// );\n    /// ```\n    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {\n        self.guards.push(Box::new(guard));\n        self\n    }\n\n    /// Add scope data.\n    ///\n    /// Data of different types from parent contexts will still be accessible. Any `Data<T>` types\n    /// set here can be extracted in handlers using the `Data<T>` extractor.\n    ///\n    /// # Examples\n    /// ```\n    /// use std::cell::Cell;\n    /// use actix_web::{web, App, HttpRequest, HttpResponse, Responder};\n    ///\n    /// struct MyData {\n    ///     count: std::cell::Cell<usize>,\n    /// }\n    ///\n    /// async fn handler(req: HttpRequest, counter: web::Data<MyData>) -> impl Responder {\n    ///     // note this cannot use the Data<T> extractor because it was not added with it\n    ///     let incr = *req.app_data::<usize>().unwrap();\n    ///     assert_eq!(incr, 3);\n    ///\n    ///     // update counter using other value from app data\n    ///     counter.count.set(counter.count.get() + incr);\n    ///\n    ///     HttpResponse::Ok().body(counter.count.get().to_string())\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::scope(\"/app\")\n    ///         .app_data(3usize)\n    ///         .app_data(web::Data::new(MyData { count: Default::default() }))\n    ///         .route(\"/\", web::get().to(handler))\n    /// );\n    /// ```\n    #[doc(alias = \"manage\")]\n    pub fn app_data<U: 'static>(mut self, data: U) -> Self {\n        self.app_data\n            .get_or_insert_with(Extensions::new)\n            .insert(data);\n\n        self\n    }\n\n    /// Add scope data after wrapping in `Data<T>`.\n    ///\n    /// Deprecated in favor of [`app_data`](Self::app_data).\n    #[deprecated(since = \"4.0.0\", note = \"Use `.app_data(Data::new(val))` instead.\")]\n    pub fn data<U: 'static>(self, data: U) -> Self {\n        self.app_data(Data::new(data))\n    }\n\n    /// Run external configuration as part of the scope building process.\n    ///\n    /// This function is useful for moving parts of configuration to a different module or library.\n    /// For example, some of the resource's configuration could be moved to different module.\n    ///\n    /// ```\n    /// use actix_web::{web, middleware, App, HttpResponse};\n    ///\n    /// // this function could be located in different module\n    /// fn config(cfg: &mut web::ServiceConfig) {\n    ///     cfg.service(web::resource(\"/test\")\n    ///         .route(web::get().to(|| HttpResponse::Ok()))\n    ///         .route(web::head().to(|| HttpResponse::MethodNotAllowed()))\n    ///     );\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .wrap(middleware::Logger::default())\n    ///     .service(\n    ///         web::scope(\"/api\")\n    ///             .configure(config)\n    ///     )\n    ///     .route(\"/index.html\", web::get().to(|| HttpResponse::Ok()));\n    /// ```\n    pub fn configure<F>(mut self, cfg_fn: F) -> Self\n    where\n        F: FnOnce(&mut ServiceConfig),\n    {\n        let mut cfg = ServiceConfig::new();\n        cfg_fn(&mut cfg);\n\n        self.services.extend(cfg.services);\n        self.external.extend(cfg.external);\n\n        // TODO: add Extensions::is_empty check and conditionally insert data\n        self.app_data\n            .get_or_insert_with(Extensions::new)\n            .extend(cfg.app_data);\n\n        if let Some(default) = cfg.default {\n            self.default = Some(default);\n        }\n\n        self\n    }\n\n    /// Register HTTP service.\n    ///\n    /// This is similar to `App's` service registration.\n    ///\n    /// Actix Web provides several services implementations:\n    ///\n    /// * *Resource* is an entry in resource table which corresponds to requested URL.\n    /// * *Scope* is a set of resources with common root path.\n    ///\n    /// ```\n    /// use actix_web::{web, App, HttpRequest};\n    ///\n    /// struct AppState;\n    ///\n    /// async fn index(req: HttpRequest) -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::scope(\"/app\").service(\n    ///         web::scope(\"/v1\")\n    ///             .service(web::resource(\"/test1\").to(index)))\n    /// );\n    /// ```\n    pub fn service<F>(mut self, factory: F) -> Self\n    where\n        F: HttpServiceFactory + 'static,\n    {\n        self.services\n            .push(Box::new(ServiceFactoryWrapper::new(factory)));\n        self\n    }\n\n    /// Configure route for a specific path.\n    ///\n    /// This is a simplified version of the `Scope::service()` method.\n    /// This method can be called multiple times, in that case\n    /// multiple resources with one route would be registered for same resource path.\n    ///\n    /// ```\n    /// use actix_web::{web, App, HttpResponse};\n    ///\n    /// async fn index(data: web::Path<(String, String)>) -> &'static str {\n    ///     \"Welcome!\"\n    /// }\n    ///\n    /// let app = App::new().service(\n    ///     web::scope(\"/app\")\n    ///         .route(\"/test1\", web::get().to(index))\n    ///         .route(\"/test2\", web::post().to(|| HttpResponse::MethodNotAllowed()))\n    /// );\n    /// ```\n    pub fn route(self, path: &str, mut route: Route) -> Self {\n        self.service(\n            Resource::new(path)\n                .add_guards(route.take_guards())\n                .route(route),\n        )\n    }\n\n    /// Default service to be used if no matching resource could be found.\n    ///\n    /// If a default service is not registered, it will fall back to the default service of\n    /// the parent [`App`](crate::App) (see [`App::default_service`](crate::App::default_service)).\n    pub fn default_service<F, U>(mut self, f: F) -> Self\n    where\n        F: IntoServiceFactory<U, ServiceRequest>,\n        U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>\n            + 'static,\n        U::InitError: fmt::Debug,\n    {\n        // create and configure default resource\n        self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err(\n            |err| {\n                log::error!(\"Can not construct default service: {err:?}\");\n            },\n        ))));\n\n        self\n    }\n\n    /// Registers a scope-wide middleware.\n    ///\n    /// `mw` is a middleware component (type), that can modify the request and response across all\n    /// sub-resources managed by this `Scope`.\n    ///\n    /// See [`App::wrap`](crate::App::wrap) for more details.\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap<M, B>(\n        self,\n        mw: M,\n    ) -> Scope<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        M: Transform<\n                T::Service,\n                ServiceRequest,\n                Response = ServiceResponse<B>,\n                Error = Error,\n                InitError = (),\n            > + 'static,\n        B: MessageBody,\n    {\n        Scope {\n            endpoint: apply(mw, self.endpoint),\n            rdef: self.rdef,\n            app_data: self.app_data,\n            guards: self.guards,\n            services: self.services,\n            default: self.default,\n            external: self.external,\n            factory_ref: self.factory_ref,\n        }\n    }\n\n    /// Registers a scope-wide function middleware.\n    ///\n    /// `mw` is a closure that runs during inbound and/or outbound processing in the request\n    /// life-cycle (request -> response), modifying request/response as necessary, across all\n    /// requests handled by the `Scope`.\n    ///\n    /// See [`App::wrap_fn`](crate::App::wrap_fn) for examples and more details.\n    #[doc(alias = \"middleware\")]\n    #[doc(alias = \"use\")] // nodejs terminology\n    pub fn wrap_fn<F, R, B>(\n        self,\n        mw: F,\n    ) -> Scope<\n        impl ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        >,\n    >\n    where\n        F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,\n        R: Future<Output = Result<ServiceResponse<B>, Error>>,\n        B: MessageBody,\n    {\n        Scope {\n            endpoint: apply_fn_factory(self.endpoint, mw),\n            rdef: self.rdef,\n            app_data: self.app_data,\n            guards: self.guards,\n            services: self.services,\n            default: self.default,\n            external: self.external,\n            factory_ref: self.factory_ref,\n        }\n    }\n}\n\nimpl<T, B> HttpServiceFactory for Scope<T>\nwhere\n    T: ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse<B>,\n            Error = Error,\n            InitError = (),\n        > + 'static,\n    B: MessageBody + 'static,\n{\n    fn register(mut self, config: &mut AppService) {\n        // update default resource if needed\n        let default = self.default.unwrap_or_else(|| config.default_service());\n\n        // register nested services\n        let mut cfg = config.clone_config();\n\n        // Update the prefix for the nested scope\n        #[cfg(feature = \"experimental-introspection\")]\n        {\n            let scope_id = config.prepare_scope_id();\n            cfg.scope_id_stack.push(scope_id);\n            cfg.update_prefix(&self.rdef);\n        }\n\n        self.services\n            .into_iter()\n            .for_each(|mut srv| srv.register(&mut cfg));\n\n        let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef));\n\n        #[cfg(feature = \"experimental-introspection\")]\n        let origin_scope = cfg.current_prefix.clone();\n\n        // external resources\n        for mut rdef in mem::take(&mut self.external) {\n            #[cfg(feature = \"experimental-introspection\")]\n            {\n                cfg.introspector\n                    .borrow_mut()\n                    .register_external(&rdef, &origin_scope);\n            }\n            rmap.add(&mut rdef, None);\n        }\n\n        // complete scope pipeline creation\n        *self.factory_ref.borrow_mut() = Some(ScopeFactory {\n            default,\n            services: cfg\n                .into_services()\n                .1\n                .into_iter()\n                .map(|(mut rdef, srv, guards, nested)| {\n                    rmap.add(&mut rdef, nested);\n                    (rdef, srv, RefCell::new(guards))\n                })\n                .collect::<Vec<_>>()\n                .into_boxed_slice()\n                .into(),\n        });\n\n        // get guards\n        let guards = if self.guards.is_empty() {\n            None\n        } else {\n            Some(self.guards)\n        };\n\n        let scope_data = self.app_data.map(Rc::new);\n\n        // wraps endpoint service (including middleware) call and injects app data for this scope\n        let endpoint = apply_fn_factory(self.endpoint, move |mut req: ServiceRequest, srv| {\n            if let Some(ref data) = scope_data {\n                req.add_data_container(Rc::clone(data));\n            }\n\n            let fut = srv.call(req);\n\n            async { Ok(fut.await?.map_into_boxed_body()) }\n        });\n\n        // register final service\n        config.register_service(\n            ResourceDef::root_prefix(&self.rdef),\n            guards,\n            endpoint,\n            Some(Rc::new(rmap)),\n        )\n    }\n}\n\npub struct ScopeFactory {\n    #[allow(clippy::type_complexity)]\n    services: Rc<\n        [(\n            ResourceDef,\n            BoxedHttpServiceFactory,\n            RefCell<Option<Guards>>,\n        )],\n    >,\n    default: Rc<BoxedHttpServiceFactory>,\n}\n\nimpl ServiceFactory<ServiceRequest> for ScopeFactory {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = ScopeService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        // construct default service factory future\n        let default_fut = self.default.new_service(());\n\n        // construct all services factory future with it's resource def and guards.\n        let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {\n            let path = path.clone();\n            let guards = guards.borrow_mut().take().unwrap_or_default();\n            let factory_fut = factory.new_service(());\n            async move {\n                factory_fut\n                    .await\n                    .map(move |service| (path, guards, service))\n            }\n        }));\n\n        Box::pin(async move {\n            let default = default_fut.await?;\n\n            // build router from the factory future result.\n            let router = factory_fut\n                .await\n                .into_iter()\n                .collect::<Result<Vec<_>, _>>()?\n                .drain(..)\n                .fold(Router::build(), |mut router, (path, guards, service)| {\n                    router.push(path, service, guards);\n                    router\n                })\n                .finish();\n\n            Ok(ScopeService { router, default })\n        })\n    }\n}\n\npub struct ScopeService {\n    router: Router<BoxedHttpService, Vec<Box<dyn Guard>>>,\n    default: BoxedHttpService,\n}\n\nimpl Service<ServiceRequest> for ScopeService {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::always_ready!();\n\n    fn call(&self, mut req: ServiceRequest) -> Self::Future {\n        let res = self.router.recognize_fn(&mut req, |req, guards| {\n            let guard_ctx = req.guard_ctx();\n            guards.iter().all(|guard| guard.check(&guard_ctx))\n        });\n\n        if let Some((srv, info)) = res {\n            req.push_resource_id(info.0);\n\n            let matched = req\n                .resource_map()\n                .is_resource_path_match(req.resource_id_path());\n\n            req.mark_resource_path(matched);\n\n            srv.call(req)\n        } else {\n            self.default.call(req)\n        }\n    }\n}\n\n#[doc(hidden)]\npub struct ScopeEndpoint {\n    factory: Rc<RefCell<Option<ScopeFactory>>>,\n}\n\nimpl ScopeEndpoint {\n    fn new(factory: Rc<RefCell<Option<ScopeFactory>>>) -> Self {\n        ScopeEndpoint { factory }\n    }\n}\n\nimpl ServiceFactory<ServiceRequest> for ScopeEndpoint {\n    type Response = ServiceResponse;\n    type Error = Error;\n    type Config = ();\n    type Service = ScopeService;\n    type InitError = ();\n    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;\n\n    fn new_service(&self, _: ()) -> Self::Future {\n        self.factory.borrow_mut().as_mut().unwrap().new_service(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_utils::future::ok;\n    use bytes::Bytes;\n\n    use super::*;\n    use crate::{\n        guard,\n        http::{\n            header::{self, HeaderValue},\n            Method, StatusCode,\n        },\n        middleware::DefaultHeaders,\n        test::{assert_body_eq, call_service, init_service, read_body, TestRequest},\n        web, App, HttpMessage, HttpRequest, HttpResponse,\n    };\n\n    #[test]\n    fn can_be_returned_from_fn() {\n        fn my_scope_1() -> Scope {\n            web::scope(\"/test\")\n                .service(web::resource(\"\").route(web::get().to(|| async { \"hello\" })))\n        }\n\n        fn my_scope_2() -> Scope<\n            impl ServiceFactory<\n                ServiceRequest,\n                Config = (),\n                Response = ServiceResponse<impl MessageBody>,\n                Error = Error,\n                InitError = (),\n            >,\n        > {\n            web::scope(\"/test-compat\")\n                .wrap_fn(|req, srv| {\n                    let fut = srv.call(req);\n                    async { Ok(fut.await?.map_into_right_body::<()>()) }\n                })\n                .service(web::resource(\"\").route(web::get().to(|| async { \"hello\" })))\n        }\n\n        fn my_scope_3() -> impl HttpServiceFactory {\n            my_scope_2()\n        }\n\n        App::new()\n            .service(my_scope_1())\n            .service(my_scope_2())\n            .service(my_scope_3());\n    }\n\n    #[actix_rt::test]\n    async fn test_scope() {\n        let srv = init_service(\n            App::new()\n                .service(web::scope(\"/app\").service(web::resource(\"/path1\").to(HttpResponse::Ok))),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_root() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/app\")\n                    .service(web::resource(\"\").to(HttpResponse::Ok))\n                    .service(web::resource(\"/\").to(HttpResponse::Created)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::CREATED);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_root2() {\n        let srv = init_service(\n            App::new().service(web::scope(\"/app/\").service(web::resource(\"\").to(HttpResponse::Ok))),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::with_uri(\"/app/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_root3() {\n        let srv = init_service(\n            App::new()\n                .service(web::scope(\"/app/\").service(web::resource(\"/\").to(HttpResponse::Ok))),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::with_uri(\"/app/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_route() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .route(\"/path1\", web::get().to(HttpResponse::Ok))\n                    .route(\"/path1\", web::delete().to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::DELETE)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::POST)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_route_without_leading_slash() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\").service(\n                    web::resource(\"path1\")\n                        .route(web::get().to(HttpResponse::Ok))\n                        .route(web::delete().to(HttpResponse::Ok)),\n                ),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::DELETE)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::POST)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_guard() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/app\")\n                    .guard(guard::Get())\n                    .service(web::resource(\"/path1\").to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::POST)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::with_uri(\"/app/path1\")\n            .method(Method::GET)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_variable_segment() {\n        let srv = init_service(App::new().service(web::scope(\"/ab-{project}\").service(\n            web::resource(\"/path1\").to(|r: HttpRequest| {\n                HttpResponse::Ok().body(format!(\"project: {}\", &r.match_info()[\"project\"]))\n            }),\n        )))\n        .await;\n\n        let req = TestRequest::with_uri(\"/ab-project1/path1\").to_request();\n        let res = srv.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_body_eq!(res, b\"project: project1\");\n\n        let req = TestRequest::with_uri(\"/aa-project1/path1\").to_request();\n        let res = srv.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_nested_scope() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").service(\n            web::scope(\"/t1\").service(web::resource(\"/path1\").to(HttpResponse::Created)),\n        )))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/t1/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::CREATED);\n    }\n\n    #[actix_rt::test]\n    async fn test_nested_scope_no_slash() {\n        let srv =\n            init_service(App::new().service(web::scope(\"/app\").service(\n                web::scope(\"t1\").service(web::resource(\"/path1\").to(HttpResponse::Created)),\n            )))\n            .await;\n\n        let req = TestRequest::with_uri(\"/app/t1/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::CREATED);\n    }\n\n    #[actix_rt::test]\n    async fn test_nested_scope_root() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/app\").service(\n                    web::scope(\"/t1\")\n                        .service(web::resource(\"\").to(HttpResponse::Ok))\n                        .service(web::resource(\"/\").to(HttpResponse::Created)),\n                ),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/t1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/t1/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::CREATED);\n    }\n\n    #[actix_rt::test]\n    async fn test_nested_scope_filter() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/app\").service(\n                    web::scope(\"/t1\")\n                        .guard(guard::Get())\n                        .service(web::resource(\"/path1\").to(HttpResponse::Ok)),\n                ),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/t1/path1\")\n            .method(Method::POST)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let req = TestRequest::with_uri(\"/app/t1/path1\")\n            .method(Method::GET)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_nested_scope_with_variable_segment() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").service(\n            web::scope(\"/{project_id}\").service(web::resource(\"/path1\").to(|r: HttpRequest| {\n                HttpResponse::Created().body(format!(\"project: {}\", &r.match_info()[\"project_id\"]))\n            })),\n        )))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/project_1/path1\").to_request();\n        let res = srv.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::CREATED);\n        assert_body_eq!(res, b\"project: project_1\");\n    }\n\n    #[actix_rt::test]\n    async fn test_nested2_scope_with_variable_segment() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").service(\n            web::scope(\"/{project}\").service(web::scope(\"/{id}\").service(\n                web::resource(\"/path1\").to(|r: HttpRequest| {\n                    HttpResponse::Created().body(format!(\n                        \"project: {} - {}\",\n                        &r.match_info()[\"project\"],\n                        &r.match_info()[\"id\"],\n                    ))\n                }),\n            )),\n        )))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test/1/path1\").to_request();\n        let res = srv.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::CREATED);\n        assert_body_eq!(res, b\"project: test - 1\");\n\n        let req = TestRequest::with_uri(\"/app/test/1/path2\").to_request();\n        let res = srv.call(req).await.unwrap();\n        assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_default_resource() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/app\")\n                    .service(web::resource(\"/path1\").to(HttpResponse::Ok))\n                    .default_service(|r: ServiceRequest| {\n                        ok(r.into_response(HttpResponse::BadRequest()))\n                    }),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/path2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_default_resource_propagation() {\n        let srv = init_service(\n            App::new()\n                .service(web::scope(\"/app1\").default_service(web::to(HttpResponse::BadRequest)))\n                .service(web::scope(\"/app2\"))\n                .default_service(|r: ServiceRequest| {\n                    ok(r.into_response(HttpResponse::MethodNotAllowed()))\n                }),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/non-exist\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n\n        let req = TestRequest::with_uri(\"/app1/non-exist\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/app2/non-exist\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .wrap(\n                        DefaultHeaders::new()\n                            .add((header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"))),\n                    )\n                    .service(web::resource(\"/test\").route(web::get().to(HttpResponse::Ok))),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_body_type() {\n        // Compile test that Scope accepts any body type; test for `EitherBody`\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .wrap_fn(|req, srv| {\n                        let fut = srv.call(req);\n                        async { Ok(fut.await?.map_into_right_body::<()>()) }\n                    })\n                    .service(web::resource(\"/test\").route(web::get().to(|| async { \"hello\" }))),\n            ),\n        )\n        .await;\n\n        // test if `MessageBody::try_into_bytes()` is preserved across scope layer\n        use actix_http::body::MessageBody as _;\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        let body = resp.into_body();\n        assert_eq!(body.try_into_bytes().unwrap(), b\"hello\".as_ref());\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_fn() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .wrap_fn(|req, srv| {\n                        let fut = srv.call(req);\n                        async move {\n                            let mut res = fut.await?;\n                            res.headers_mut()\n                                .insert(header::CONTENT_TYPE, HeaderValue::from_static(\"0001\"));\n                            Ok(res)\n                        }\n                    })\n                    .route(\"/test\", web::get().to(HttpResponse::Ok)),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        assert_eq!(\n            resp.headers().get(header::CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"0001\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_middleware_app_data() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"app\")\n                    .app_data(1usize)\n                    .wrap_fn(|req, srv| {\n                        assert_eq!(req.app_data::<usize>(), Some(&1usize));\n                        req.extensions_mut().insert(1usize);\n                        srv.call(req)\n                    })\n                    .route(\"/test\", web::get().to(HttpResponse::Ok))\n                    .default_service(|req: ServiceRequest| async move {\n                        let (req, _) = req.into_parts();\n\n                        assert_eq!(req.extensions().get::<usize>(), Some(&1));\n\n                        Ok(ServiceResponse::new(\n                            req,\n                            HttpResponse::BadRequest().finish(),\n                        ))\n                    }),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/app/default\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n    }\n\n    // allow deprecated {App, Scope}::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_override_data() {\n        let srv = init_service(App::new().data(1usize).service(\n            web::scope(\"app\").data(10usize).route(\n                \"/t\",\n                web::get().to(|data: web::Data<usize>| {\n                    assert_eq!(**data, 10);\n                    HttpResponse::Ok()\n                }),\n            ),\n        ))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/t\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    // allow deprecated `{App, Scope}::data`\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_override_data_default_service() {\n        let srv =\n            init_service(App::new().data(1usize).service(\n                web::scope(\"app\").data(10usize).default_service(web::to(\n                    |data: web::Data<usize>| {\n                        assert_eq!(**data, 10);\n                        HttpResponse::Ok()\n                    },\n                )),\n            ))\n            .await;\n\n        let req = TestRequest::with_uri(\"/app/t\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_override_app_data() {\n        let srv = init_service(App::new().app_data(web::Data::new(1usize)).service(\n            web::scope(\"app\").app_data(web::Data::new(10usize)).route(\n                \"/t\",\n                web::get().to(|data: web::Data<usize>| {\n                    assert_eq!(**data, 10);\n                    HttpResponse::Ok()\n                }),\n            ),\n        ))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/t\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_config() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").configure(|s| {\n            s.route(\"/path1\", web::get().to(HttpResponse::Ok));\n        })))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/path1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_scope_config_2() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").configure(|s| {\n            s.service(web::scope(\"/v1\").configure(|s| {\n                s.route(\"/\", web::get().to(HttpResponse::Ok));\n            }));\n        })))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/v1/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_url_for_external() {\n        let srv = init_service(App::new().service(web::scope(\"/app\").configure(|s| {\n            s.service(web::scope(\"/v1\").configure(|s| {\n                s.external_resource(\"youtube\", \"https://youtube.com/watch/{video_id}\");\n                s.route(\n                    \"/\",\n                    web::get().to(|req: HttpRequest| {\n                        HttpResponse::Ok()\n                            .body(req.url_for(\"youtube\", [\"xxxxxx\"]).unwrap().to_string())\n                    }),\n                );\n            }));\n        })))\n        .await;\n\n        let req = TestRequest::with_uri(\"/app/v1/\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(body, &b\"https://youtube.com/watch/xxxxxx\"[..]);\n    }\n\n    #[actix_rt::test]\n    async fn test_url_for_nested() {\n        let srv = init_service(App::new().service(web::scope(\"/a\").service(\n            web::scope(\"/b\").service(web::resource(\"/c/{stuff}\").name(\"c\").route(web::get().to(\n                |req: HttpRequest| {\n                    HttpResponse::Ok().body(format!(\"{}\", req.url_for(\"c\", [\"12345\"]).unwrap()))\n                },\n            ))),\n        )))\n        .await;\n\n        let req = TestRequest::with_uri(\"/a/b/c/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n        let body = read_body(resp).await;\n        assert_eq!(\n            body,\n            Bytes::from_static(b\"http://localhost:8080/a/b/c/12345\")\n        );\n    }\n\n    #[actix_rt::test]\n    async fn dynamic_scopes() {\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/{a}/\").service(\n                    web::scope(\"/{b}/\")\n                        .route(\"\", web::get().to(|_: HttpRequest| HttpResponse::Created()))\n                        .route(\n                            \"/\",\n                            web::get().to(|_: HttpRequest| HttpResponse::Accepted()),\n                        )\n                        .route(\"/{c}\", web::get().to(|_: HttpRequest| HttpResponse::Ok())),\n                ),\n            ),\n        )\n        .await;\n\n        // note the unintuitive behavior with trailing slashes on scopes with dynamic segments\n        let req = TestRequest::with_uri(\"/a//b//c\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/a//b/\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::CREATED);\n\n        let req = TestRequest::with_uri(\"/a//b//\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::ACCEPTED);\n\n        let req = TestRequest::with_uri(\"/a//b//c/d\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n\n        let srv = init_service(\n            App::new().service(\n                web::scope(\"/{a}\").service(\n                    web::scope(\"/{b}\")\n                        .route(\"\", web::get().to(|_: HttpRequest| HttpResponse::Created()))\n                        .route(\n                            \"/\",\n                            web::get().to(|_: HttpRequest| HttpResponse::Accepted()),\n                        )\n                        .route(\"/{c}\", web::get().to(|_: HttpRequest| HttpResponse::Ok())),\n                ),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/a/b/c\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/a/b\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::CREATED);\n\n        let req = TestRequest::with_uri(\"/a/b/\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::ACCEPTED);\n\n        let req = TestRequest::with_uri(\"/a/b/c/d\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::NOT_FOUND);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/server.rs",
    "content": "use std::{\n    any::Any,\n    cmp, fmt,\n    future::Future,\n    io,\n    marker::PhantomData,\n    net,\n    sync::{Arc, Mutex},\n    time::Duration,\n};\n\n#[cfg(feature = \"__tls\")]\nuse actix_http::TlsAcceptorConfig;\nuse actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response};\nuse actix_server::{Server, ServerBuilder};\nuse actix_service::{\n    map_config, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,\n};\n#[cfg(feature = \"openssl\")]\nuse actix_tls::accept::openssl::reexports::{AlpnError, SslAcceptor, SslAcceptorBuilder};\n\nuse crate::{config::AppConfig, Error};\n\nstruct Socket {\n    scheme: &'static str,\n    addr: net::SocketAddr,\n}\n\nstruct Config {\n    host: Option<String>,\n    keep_alive: KeepAlive,\n    tcp_nodelay: Option<bool>,\n    client_request_timeout: Duration,\n    client_disconnect_timeout: Duration,\n    h1_allow_half_closed: bool,\n    h2_initial_window_size: Option<u32>,\n    h2_initial_connection_window_size: Option<u32>,\n    #[allow(dead_code)] // only dead when no TLS features are enabled\n    tls_handshake_timeout: Option<Duration>,\n}\n\n/// An HTTP Server.\n///\n/// Create new HTTP server with application factory.\n///\n/// # Automatic HTTP Version Selection\n///\n/// There are two ways to select the HTTP version of an incoming connection:\n///\n/// - One is to rely on the ALPN information that is provided when using a TLS (HTTPS); both\n///   versions are supported automatically when using either of the `.bind_rustls()` or\n///   `.bind_openssl()` methods.\n/// - The other is to read the first few bytes of the TCP stream. This is the only viable approach\n///   for supporting H2C, which allows the HTTP/2 protocol to work over plaintext connections. Use\n///   the `.bind_auto_h2c()` method to enable this behavior.\n///\n/// # Examples\n///\n/// ```no_run\n/// use actix_web::{web, App, HttpResponse, HttpServer};\n///\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     HttpServer::new(|| {\n///         App::new()\n///             .service(web::resource(\"/\").to(|| async { \"hello world\" }))\n///     })\n///     .bind((\"127.0.0.1\", 8080))?\n///     .run()\n///     .await\n/// }\n/// ```\n#[must_use]\npub struct HttpServer<F, I, S, B>\nwhere\n    F: Fn() -> I + Send + Clone + 'static,\n    I: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig>,\n    S::Error: Into<Error>,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>>,\n    B: MessageBody,\n{\n    pub(super) factory: F,\n    config: Arc<Mutex<Config>>,\n    backlog: u32,\n    sockets: Vec<Socket>,\n    builder: ServerBuilder,\n    #[allow(clippy::type_complexity)]\n    on_connect_fn: Option<Arc<dyn Fn(&dyn Any, &mut Extensions) + Send + Sync>>,\n    _phantom: PhantomData<(S, B)>,\n}\n\nimpl<F, I, S, B> HttpServer<F, I, S, B>\nwhere\n    F: Fn() -> I + Send + Clone + 'static,\n    I: IntoServiceFactory<S, Request>,\n\n    S: ServiceFactory<Request, Config = AppConfig> + 'static,\n    S::Error: Into<Error> + 'static,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>> + 'static,\n    <S::Service as Service<Request>>::Future: 'static,\n    S::Service: 'static,\n\n    B: MessageBody + 'static,\n{\n    /// Create new HTTP server with application factory\n    ///\n    /// # Worker Count\n    ///\n    /// The `factory` will be instantiated multiple times in most configurations. See\n    /// [`bind()`](Self::bind()) docs for more on how worker count and bind address resolution\n    /// causes multiple server factory instantiations.\n    pub fn new(factory: F) -> Self {\n        HttpServer {\n            factory,\n            config: Arc::new(Mutex::new(Config {\n                host: None,\n                keep_alive: KeepAlive::default(),\n                tcp_nodelay: None,\n                client_request_timeout: Duration::from_secs(5),\n                client_disconnect_timeout: Duration::from_secs(1),\n                h1_allow_half_closed: true,\n                h2_initial_window_size: None,\n                h2_initial_connection_window_size: None,\n                tls_handshake_timeout: None,\n            })),\n            backlog: 1024,\n            sockets: Vec::new(),\n            builder: ServerBuilder::default(),\n            on_connect_fn: None,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Sets number of workers to start (per bind address).\n    ///\n    /// The default worker count is the determined by [`std::thread::available_parallelism()`]. See\n    /// its documentation to determine what behavior you should expect when server is run.\n    ///\n    /// Note that the server factory passed to [`new`](Self::new()) will be instantiated **at least\n    /// once per worker**. See [`bind()`](Self::bind()) docs for more on how worker count and bind\n    /// address resolution causes multiple server factory instantiations.\n    ///\n    /// `num` must be greater than 0.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `num` is 0.\n    pub fn workers(mut self, num: usize) -> Self {\n        self.builder = self.builder.workers(num);\n        self\n    }\n\n    /// Sets server keep-alive preference.\n    ///\n    /// By default keep-alive is set to 5 seconds.\n    pub fn keep_alive<T: Into<KeepAlive>>(self, val: T) -> Self {\n        self.config.lock().unwrap().keep_alive = val.into();\n        self\n    }\n\n    /// Sets `TCP_NODELAY` value on accepted TCP connections.\n    ///\n    /// By default, accepted TCP connections keep the OS default.\n    /// This method overrides that behavior for all accepted TCP connections.\n    pub fn tcp_nodelay(self, enabled: bool) -> Self {\n        self.config.lock().unwrap().tcp_nodelay = Some(enabled);\n        self\n    }\n\n    /// Sets the maximum number of pending connections.\n    ///\n    /// This refers to the number of clients that can be waiting to be served. Exceeding this number\n    /// results in the client getting an error when attempting to connect. It should only affect\n    /// servers under significant load.\n    ///\n    /// Generally set in the 64–2048 range. Default value is 2048.\n    ///\n    /// This method will have no effect if called after a `bind()`.\n    pub fn backlog(mut self, backlog: u32) -> Self {\n        self.backlog = backlog;\n        self.builder = self.builder.backlog(backlog);\n        self\n    }\n\n    /// Sets the per-worker maximum number of concurrent connections.\n    ///\n    /// All socket listeners will stop accepting connections when this limit is reached for\n    /// each worker.\n    ///\n    /// By default max connections is set to a 25k.\n    pub fn max_connections(mut self, num: usize) -> Self {\n        self.builder = self.builder.max_concurrent_connections(num);\n        self\n    }\n\n    /// Sets the per-worker maximum concurrent TLS connection limit.\n    ///\n    /// All listeners will stop accepting connections when this limit is reached. It can be used to\n    /// limit the global TLS CPU usage.\n    ///\n    /// By default max connections is set to a 256.\n    #[allow(unused_variables)]\n    pub fn max_connection_rate(self, num: usize) -> Self {\n        #[cfg(feature = \"__tls\")]\n        actix_tls::accept::max_concurrent_tls_connect(num);\n        self\n    }\n\n    /// Sets max number of threads for each worker's blocking task thread pool.\n    ///\n    /// One thread pool is set up **per worker**; not shared across workers.\n    ///\n    /// By default, set to 512 divided by [available parallelism](std::thread::available_parallelism()).\n    pub fn worker_max_blocking_threads(mut self, num: usize) -> Self {\n        self.builder = self.builder.worker_max_blocking_threads(num);\n        self\n    }\n\n    /// Sets server client timeout for first request.\n    ///\n    /// Defines a timeout for reading client request head. If a client does not transmit the entire\n    /// set headers within this time, the request is terminated with a 408 (Request Timeout) error.\n    ///\n    /// To disable timeout set value to 0.\n    ///\n    /// By default client timeout is set to 5000 milliseconds.\n    pub fn client_request_timeout(self, dur: Duration) -> Self {\n        self.config.lock().unwrap().client_request_timeout = dur;\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Renamed to `client_request_timeout`.\")]\n    pub fn client_timeout(self, dur: Duration) -> Self {\n        self.client_request_timeout(dur)\n    }\n\n    /// Sets server connection shutdown timeout.\n    ///\n    /// Defines a timeout for connection shutdown. If a shutdown procedure does not complete within\n    /// this time, the request is dropped.\n    ///\n    /// To disable timeout set value to 0.\n    ///\n    /// By default client timeout is set to 5000 milliseconds.\n    pub fn client_disconnect_timeout(self, dur: Duration) -> Self {\n        self.config.lock().unwrap().client_disconnect_timeout = dur;\n        self\n    }\n\n    /// Sets TLS handshake timeout.\n    ///\n    /// Defines a timeout for TLS handshake. If the TLS handshake does not complete within this\n    /// time, the connection is closed.\n    ///\n    /// By default, the handshake timeout is 3 seconds.\n    #[cfg(feature = \"__tls\")]\n    pub fn tls_handshake_timeout(self, dur: Duration) -> Self {\n        self.config\n            .lock()\n            .unwrap()\n            .tls_handshake_timeout\n            .replace(dur);\n\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Renamed to `client_disconnect_timeout`.\")]\n    pub fn client_shutdown(self, dur: u64) -> Self {\n        self.client_disconnect_timeout(Duration::from_millis(dur))\n    }\n\n    /// Sets whether HTTP/1 connections should support half-closures.\n    ///\n    /// Clients can choose to shutdown their writer-side of the connection after completing their\n    /// request and while waiting for the server response. Setting this to `false` will cause the\n    /// server to abort the connection handling as soon as it detects an EOF from the client.\n    ///\n    /// The default behavior is to allow, i.e. `true`\n    pub fn h1_allow_half_closed(self, allow: bool) -> Self {\n        self.config.lock().unwrap().h1_allow_half_closed = allow;\n        self\n    }\n\n    /// Sets initial stream-level flow control window size for HTTP/2 connections.\n    ///\n    /// Higher values can improve upload performance on high-latency links at the cost of higher\n    /// worst-case memory usage per connection.\n    ///\n    /// The default value is 1MiB.\n    #[cfg(feature = \"http2\")]\n    pub fn h2_initial_window_size(self, size: u32) -> Self {\n        self.config.lock().unwrap().h2_initial_window_size = Some(size);\n        self\n    }\n\n    /// Sets initial connection-level flow control window size for HTTP/2 connections.\n    ///\n    /// Higher values can improve upload performance on high-latency links at the cost of higher\n    /// worst-case memory usage per connection.\n    ///\n    /// The default value is 2MiB.\n    #[cfg(feature = \"http2\")]\n    pub fn h2_initial_connection_window_size(self, size: u32) -> Self {\n        self.config\n            .lock()\n            .unwrap()\n            .h2_initial_connection_window_size = Some(size);\n        self\n    }\n\n    /// Sets function that will be called once before each connection is handled.\n    ///\n    /// It will receive a `&std::any::Any`, which contains underlying connection type and an\n    /// [Extensions] container so that connection data can be accessed in middleware and handlers.\n    ///\n    /// # Connection Types\n    /// - `actix_tls::accept::openssl::TlsStream<actix_web::rt::net::TcpStream>` when using OpenSSL.\n    /// - `actix_tls::accept::rustls_0_20::TlsStream<actix_web::rt::net::TcpStream>` when using\n    ///   Rustls v0.20.\n    /// - `actix_tls::accept::rustls_0_21::TlsStream<actix_web::rt::net::TcpStream>` when using\n    ///   Rustls v0.21.\n    /// - `actix_tls::accept::rustls_0_22::TlsStream<actix_web::rt::net::TcpStream>` when using\n    ///   Rustls v0.22.\n    /// - `actix_tls::accept::rustls_0_23::TlsStream<actix_web::rt::net::TcpStream>` when using\n    ///   Rustls v0.23.\n    /// - `actix_web::rt::net::TcpStream` when no encryption is used.\n    ///\n    /// See the `on_connect` example for additional details.\n    pub fn on_connect<CB>(mut self, f: CB) -> HttpServer<F, I, S, B>\n    where\n        CB: Fn(&dyn Any, &mut Extensions) + Send + Sync + 'static,\n    {\n        self.on_connect_fn = Some(Arc::new(f));\n        self\n    }\n\n    /// Sets server host name.\n    ///\n    /// Host name is used by application router as a hostname for url generation. Check\n    /// [`ConnectionInfo`](crate::dev::ConnectionInfo::host()) docs for more info.\n    ///\n    /// By default, hostname is set to \"localhost\".\n    pub fn server_hostname<T: AsRef<str>>(self, val: T) -> Self {\n        self.config.lock().unwrap().host = Some(val.as_ref().to_owned());\n        self\n    }\n\n    /// Flags the `System` to exit after server shutdown.\n    ///\n    /// Does nothing when running under `#[tokio::main]` runtime.\n    pub fn system_exit(mut self) -> Self {\n        self.builder = self.builder.system_exit();\n        self\n    }\n\n    /// Disables signal handling.\n    pub fn disable_signals(mut self) -> Self {\n        self.builder = self.builder.disable_signals();\n        self\n    }\n\n    /// Specify shutdown signal from a future.\n    ///\n    /// Using this method will prevent OS signal handlers being set up.\n    ///\n    /// Typically, a `CancellationToken` will be used, but any future _can_ be.\n    ///\n    /// # Examples\n    ///\n    /// ```no_run\n    /// use actix_web::{App, HttpServer};\n    /// use tokio_util::sync::CancellationToken;\n    ///\n    /// # #[actix_web::main]\n    /// # async fn main() -> std::io::Result<()> {\n    /// let stop_signal = CancellationToken::new();\n    ///\n    /// HttpServer::new(move || App::new())\n    ///     .shutdown_signal(stop_signal.cancelled_owned())\n    ///     .bind((\"127.0.0.1\", 8080))?\n    ///     .run()\n    ///     .await\n    /// # }\n    /// ```\n    pub fn shutdown_signal<Fut>(mut self, shutdown_signal: Fut) -> Self\n    where\n        Fut: Future<Output = ()> + Send + 'static,\n    {\n        self.builder = self.builder.shutdown_signal(shutdown_signal);\n        self\n    }\n\n    /// Sets timeout for graceful worker shutdown of workers.\n    ///\n    /// After receiving a stop signal, workers have this much time to finish serving requests.\n    /// Workers still alive after the timeout are force dropped.\n    ///\n    /// By default shutdown timeout sets to 30 seconds.\n    pub fn shutdown_timeout(mut self, sec: u64) -> Self {\n        self.builder = self.builder.shutdown_timeout(sec);\n        self\n    }\n\n    /// Returns addresses of bound sockets.\n    pub fn addrs(&self) -> Vec<net::SocketAddr> {\n        self.sockets.iter().map(|s| s.addr).collect()\n    }\n\n    /// Returns addresses of bound sockets and the scheme for it.\n    ///\n    /// This is useful when the server is bound from different sources with some sockets listening\n    /// on HTTP and some listening on HTTPS and the user should be presented with an enumeration of\n    /// which socket requires which protocol.\n    pub fn addrs_with_scheme(&self) -> Vec<(net::SocketAddr, &str)> {\n        self.sockets.iter().map(|s| (s.addr, s.scheme)).collect()\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s).\n    ///\n    /// # Hostname Resolution\n    ///\n    /// When `addrs` includes a hostname, it is possible for this method to bind to both the IPv4\n    /// and IPv6 addresses that result from a DNS lookup. You can test this by passing\n    /// `localhost:8080` and noting that the server binds to `127.0.0.1:8080` _and_ `[::1]:8080`. To\n    /// bind additional addresses, call this method multiple times.\n    ///\n    /// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.\n    ///\n    /// # Worker Count\n    ///\n    /// The `factory` will be instantiated multiple times in most scenarios. The number of\n    /// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by\n    /// `addrs`.\n    ///\n    /// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`\n    /// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`\n    /// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing\n    /// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).\n    ///\n    /// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple\n    /// the number of instantiations in a similar way.\n    ///\n    /// # Typical Usage\n    ///\n    /// In general, use `127.0.0.1:<port>` when testing locally and `0.0.0.0:<port>` when deploying\n    /// (with or without a reverse proxy or load balancer) so that the server is accessible.\n    ///\n    /// # Errors\n    ///\n    /// Returns an `io::Error` if:\n    /// - `addrs` cannot be resolved into one or more socket addresses;\n    /// - all the resolved socket addresses are already bound.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use actix_web::{App, HttpServer};\n    /// # fn inner() -> std::io::Result<()> {\n    /// HttpServer::new(|| App::new())\n    ///     .bind((\"127.0.0.1\", 8080))?\n    ///     .bind(\"[::1]:9000\")?\n    /// # ; Ok(()) }\n    /// ```\n    pub fn bind<A: net::ToSocketAddrs>(mut self, addrs: A) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n\n        for lst in sockets {\n            self = self.listen(lst)?;\n        }\n\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for plaintext HTTP/1.x\n    /// or HTTP/2 connections.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    #[cfg(feature = \"http2\")]\n    pub fn bind_auto_h2c<A: net::ToSocketAddrs>(mut self, addrs: A) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n\n        for lst in sockets {\n            self = self.listen_auto_h2c(lst)?;\n        }\n\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for TLS connections\n    /// using Rustls v0.20.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_20\")]\n    pub fn bind_rustls<A: net::ToSocketAddrs>(\n        mut self,\n        addrs: A,\n        config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n        for lst in sockets {\n            self = self.listen_rustls_0_20_inner(lst, config.clone())?;\n        }\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for TLS connections\n    /// using Rustls v0.21.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_21\")]\n    pub fn bind_rustls_021<A: net::ToSocketAddrs>(\n        mut self,\n        addrs: A,\n        config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n        for lst in sockets {\n            self = self.listen_rustls_0_21_inner(lst, config.clone())?;\n        }\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for TLS connections\n    /// using Rustls v0.22.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_22\")]\n    pub fn bind_rustls_0_22<A: net::ToSocketAddrs>(\n        mut self,\n        addrs: A,\n        config: actix_tls::accept::rustls_0_22::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n        for lst in sockets {\n            self = self.listen_rustls_0_22_inner(lst, config.clone())?;\n        }\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for TLS connections\n    /// using Rustls v0.23.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_23\")]\n    pub fn bind_rustls_0_23<A: net::ToSocketAddrs>(\n        mut self,\n        addrs: A,\n        config: actix_tls::accept::rustls_0_23::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n        for lst in sockets {\n            self = self.listen_rustls_0_23_inner(lst, config.clone())?;\n        }\n        Ok(self)\n    }\n\n    /// Resolves socket address(es) and binds server to created listener(s) for TLS connections\n    /// using OpenSSL.\n    ///\n    /// See [`bind()`](Self::bind()) for more details on `addrs` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"openssl\")]\n    pub fn bind_openssl<A>(mut self, addrs: A, builder: SslAcceptorBuilder) -> io::Result<Self>\n    where\n        A: net::ToSocketAddrs,\n    {\n        let sockets = bind_addrs(addrs, self.backlog)?;\n        let acceptor = openssl_acceptor(builder)?;\n\n        for lst in sockets {\n            self = self.listen_openssl_inner(lst, acceptor.clone())?;\n        }\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming connection requests.\n    ///\n    /// No changes are made to `lst`'s configuration. Ensure it is configured properly before\n    /// passing ownership to `listen()`.\n    pub fn listen(mut self, lst: net::TcpListener) -> io::Result<Self> {\n        let cfg = Arc::clone(&self.config);\n        let factory = self.factory.clone();\n        let addr = lst.local_addr().unwrap();\n\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"http\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let cfg = cfg.lock().unwrap();\n                    let host = cfg.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(cfg.keep_alive)\n                        .client_request_timeout(cfg.client_request_timeout)\n                        .client_disconnect_timeout(cfg.client_disconnect_timeout)\n                        .h1_allow_half_closed(cfg.h1_allow_half_closed)\n                        .local_addr(addr);\n\n                    if let Some(enabled) = cfg.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = cfg.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = cfg.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc =\n                            svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext))\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(false, host.clone(), addr)\n                    }))\n                    .tcp()\n                })?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming plaintext HTTP/1.x or HTTP/2 connections.\n    #[cfg(feature = \"http2\")]\n    pub fn listen_auto_h2c(mut self, lst: net::TcpListener) -> io::Result<Self> {\n        let cfg = Arc::clone(&self.config);\n        let factory = self.factory.clone();\n        let addr = lst.local_addr().unwrap();\n\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"http\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let cfg = cfg.lock().unwrap();\n                    let host = cfg.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(cfg.keep_alive)\n                        .client_request_timeout(cfg.client_request_timeout)\n                        .client_disconnect_timeout(cfg.client_disconnect_timeout)\n                        .h1_allow_half_closed(cfg.h1_allow_half_closed)\n                        .local_addr(addr);\n\n                    if let Some(enabled) = cfg.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = cfg.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = cfg.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc =\n                            svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext))\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(false, host.clone(), addr)\n                    }))\n                    .tcp_auto_h2c()\n                })?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming TLS connection requests using Rustls\n    /// v0.20.\n    ///\n    /// See [`listen()`](Self::listen) for more details on the `lst` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_20\")]\n    pub fn listen_rustls(\n        self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        self.listen_rustls_0_20_inner(lst, config)\n    }\n\n    /// Binds to existing listener for accepting incoming TLS connection requests using Rustls\n    /// v0.21.\n    ///\n    /// See [`listen()`](Self::listen()) for more details on the `lst` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_21\")]\n    pub fn listen_rustls_0_21(\n        self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        self.listen_rustls_0_21_inner(lst, config)\n    }\n\n    #[cfg(feature = \"rustls-0_20\")]\n    fn listen_rustls_0_20_inner(\n        mut self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let factory = self.factory.clone();\n        let cfg = Arc::clone(&self.config);\n        let addr = lst.local_addr().unwrap();\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"https\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let c = cfg.lock().unwrap();\n                    let host = c.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .client_disconnect_timeout(c.client_disconnect_timeout);\n\n                    if let Some(enabled) = c.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = c.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = c.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc = svc\n                            .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    let acceptor_config = match c.tls_handshake_timeout {\n                        Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),\n                        None => TlsAcceptorConfig::default(),\n                    };\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(true, host.clone(), addr)\n                    }))\n                    .rustls_with_config(config.clone(), acceptor_config)\n                })?;\n\n        Ok(self)\n    }\n\n    #[cfg(feature = \"rustls-0_21\")]\n    fn listen_rustls_0_21_inner(\n        mut self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let factory = self.factory.clone();\n        let cfg = Arc::clone(&self.config);\n        let addr = lst.local_addr().unwrap();\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"https\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let c = cfg.lock().unwrap();\n                    let host = c.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .client_disconnect_timeout(c.client_disconnect_timeout);\n\n                    if let Some(enabled) = c.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = c.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = c.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc = svc\n                            .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    let acceptor_config = match c.tls_handshake_timeout {\n                        Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),\n                        None => TlsAcceptorConfig::default(),\n                    };\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(true, host.clone(), addr)\n                    }))\n                    .rustls_021_with_config(config.clone(), acceptor_config)\n                })?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming TLS connection requests using Rustls\n    /// v0.22.\n    ///\n    /// See [`listen()`](Self::listen()) for more details on the `lst` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_22\")]\n    pub fn listen_rustls_0_22(\n        self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_22::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        self.listen_rustls_0_22_inner(lst, config)\n    }\n\n    #[cfg(feature = \"rustls-0_22\")]\n    fn listen_rustls_0_22_inner(\n        mut self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_22::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let factory = self.factory.clone();\n        let cfg = Arc::clone(&self.config);\n        let addr = lst.local_addr().unwrap();\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"https\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let c = cfg.lock().unwrap();\n                    let host = c.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .client_disconnect_timeout(c.client_disconnect_timeout);\n\n                    if let Some(enabled) = c.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = c.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = c.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc = svc\n                            .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    let acceptor_config = match c.tls_handshake_timeout {\n                        Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),\n                        None => TlsAcceptorConfig::default(),\n                    };\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(true, host.clone(), addr)\n                    }))\n                    .rustls_0_22_with_config(config.clone(), acceptor_config)\n                })?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming TLS connection requests using Rustls\n    /// v0.23.\n    ///\n    /// See [`listen()`](Self::listen()) for more details on the `lst` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"rustls-0_23\")]\n    pub fn listen_rustls_0_23(\n        self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_23::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        self.listen_rustls_0_23_inner(lst, config)\n    }\n\n    #[cfg(feature = \"rustls-0_23\")]\n    fn listen_rustls_0_23_inner(\n        mut self,\n        lst: net::TcpListener,\n        config: actix_tls::accept::rustls_0_23::reexports::ServerConfig,\n    ) -> io::Result<Self> {\n        let factory = self.factory.clone();\n        let cfg = Arc::clone(&self.config);\n        let addr = lst.local_addr().unwrap();\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"https\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let c = cfg.lock().unwrap();\n                    let host = c.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .client_disconnect_timeout(c.client_disconnect_timeout);\n\n                    if let Some(enabled) = c.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = c.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = c.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc = svc\n                            .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    let acceptor_config = match c.tls_handshake_timeout {\n                        Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),\n                        None => TlsAcceptorConfig::default(),\n                    };\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(true, host.clone(), addr)\n                    }))\n                    .rustls_0_23_with_config(config.clone(), acceptor_config)\n                })?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing listener for accepting incoming TLS connection requests using OpenSSL.\n    ///\n    /// See [`listen()`](Self::listen) for more details on the `lst` argument.\n    ///\n    /// ALPN protocols \"h2\" and \"http/1.1\" are added to any configured ones.\n    #[cfg(feature = \"openssl\")]\n    pub fn listen_openssl(\n        self,\n        lst: net::TcpListener,\n        builder: SslAcceptorBuilder,\n    ) -> io::Result<Self> {\n        self.listen_openssl_inner(lst, openssl_acceptor(builder)?)\n    }\n\n    #[cfg(feature = \"openssl\")]\n    fn listen_openssl_inner(\n        mut self,\n        lst: net::TcpListener,\n        acceptor: SslAcceptor,\n    ) -> io::Result<Self> {\n        let factory = self.factory.clone();\n        let cfg = Arc::clone(&self.config);\n        let addr = lst.local_addr().unwrap();\n\n        self.sockets.push(Socket {\n            addr,\n            scheme: \"https\",\n        });\n\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder =\n            self.builder\n                .listen(format!(\"actix-web-service-{}\", addr), lst, move || {\n                    let c = cfg.lock().unwrap();\n                    let host = c.host.clone().unwrap_or_else(|| format!(\"{}\", addr));\n\n                    let mut svc = HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .client_disconnect_timeout(c.client_disconnect_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .local_addr(addr);\n\n                    if let Some(enabled) = c.tcp_nodelay {\n                        svc = svc.tcp_nodelay(enabled);\n                    }\n\n                    if let Some(val) = c.h2_initial_window_size {\n                        svc = svc.h2_initial_window_size(val);\n                    }\n\n                    if let Some(val) = c.h2_initial_connection_window_size {\n                        svc = svc.h2_initial_connection_window_size(val);\n                    }\n\n                    if let Some(handler) = on_connect_fn.clone() {\n                        svc = svc\n                            .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                    };\n\n                    let fac = factory()\n                        .into_factory()\n                        .map_err(|err| err.into().error_response());\n\n                    // false positive lint (?)\n                    #[allow(clippy::significant_drop_in_scrutinee)]\n                    let acceptor_config = match c.tls_handshake_timeout {\n                        Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur),\n                        None => TlsAcceptorConfig::default(),\n                    };\n\n                    svc.finish(map_config(fac, move |_| {\n                        AppConfig::new(true, host.clone(), addr)\n                    }))\n                    .openssl_with_config(acceptor.clone(), acceptor_config)\n                })?;\n\n        Ok(self)\n    }\n\n    /// Opens Unix Domain Socket (UDS) from `uds` path and binds server to created listener.\n    #[cfg(unix)]\n    pub fn bind_uds<A>(mut self, uds_path: A) -> io::Result<Self>\n    where\n        A: AsRef<std::path::Path>,\n    {\n        use actix_http::Protocol;\n        use actix_rt::net::UnixStream;\n        use actix_service::{fn_service, ServiceFactoryExt as _};\n\n        let cfg = Arc::clone(&self.config);\n        let factory = self.factory.clone();\n        let socket_addr =\n            net::SocketAddr::new(net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), 8080);\n\n        self.sockets.push(Socket {\n            scheme: \"http\",\n            addr: socket_addr,\n        });\n\n        self.builder = self.builder.bind_uds(\n            format!(\"actix-web-service-{:?}\", uds_path.as_ref()),\n            uds_path,\n            move || {\n                let c = cfg.lock().unwrap();\n                let config = AppConfig::new(\n                    false,\n                    c.host.clone().unwrap_or_else(|| format!(\"{}\", socket_addr)),\n                    socket_addr,\n                );\n\n                let fac = factory()\n                    .into_factory()\n                    .map_err(|err| err.into().error_response());\n\n                fn_service(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }).and_then(\n                    HttpService::build()\n                        .keep_alive(c.keep_alive)\n                        .client_request_timeout(c.client_request_timeout)\n                        .client_disconnect_timeout(c.client_disconnect_timeout)\n                        .h1_allow_half_closed(c.h1_allow_half_closed)\n                        .finish(map_config(fac, move |_| config.clone())),\n                )\n            },\n        )?;\n\n        Ok(self)\n    }\n\n    /// Binds to existing Unix Domain Socket (UDS) listener.\n    #[cfg(unix)]\n    pub fn listen_uds(mut self, lst: std::os::unix::net::UnixListener) -> io::Result<Self> {\n        use actix_http::Protocol;\n        use actix_rt::net::UnixStream;\n        use actix_service::{fn_service, ServiceFactoryExt as _};\n\n        let cfg = Arc::clone(&self.config);\n        let factory = self.factory.clone();\n        let socket_addr =\n            net::SocketAddr::new(net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), 8080);\n\n        self.sockets.push(Socket {\n            scheme: \"http\",\n            addr: socket_addr,\n        });\n\n        let addr = lst.local_addr()?;\n        let name = format!(\"actix-web-service-{:?}\", addr);\n        let on_connect_fn = self.on_connect_fn.clone();\n\n        self.builder = self.builder.listen_uds(name, lst, move || {\n            let c = cfg.lock().unwrap();\n            let config = AppConfig::new(\n                false,\n                c.host.clone().unwrap_or_else(|| format!(\"{}\", socket_addr)),\n                socket_addr,\n            );\n\n            fn_service(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }).and_then({\n                let mut svc = HttpService::build()\n                    .keep_alive(c.keep_alive)\n                    .client_request_timeout(c.client_request_timeout)\n                    .h1_allow_half_closed(c.h1_allow_half_closed)\n                    .client_disconnect_timeout(c.client_disconnect_timeout);\n\n                if let Some(handler) = on_connect_fn.clone() {\n                    svc = svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));\n                }\n\n                let fac = factory()\n                    .into_factory()\n                    .map_err(|err| err.into().error_response());\n\n                svc.finish(map_config(fac, move |_| config.clone()))\n            })\n        })?;\n        Ok(self)\n    }\n}\n\nimpl<F, I, S, B> HttpServer<F, I, S, B>\nwhere\n    F: Fn() -> I + Send + Clone + 'static,\n    I: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig>,\n    S::Error: Into<Error>,\n    S::InitError: fmt::Debug,\n    S::Response: Into<Response<B>>,\n    S::Service: 'static,\n    B: MessageBody,\n{\n    /// Start listening for incoming connections.\n    ///\n    /// # Workers\n    /// This method starts a number of HTTP workers in separate threads. The number of workers in a\n    /// set is defined by [`workers()`](Self::workers) or, by default, the number of the machine's\n    /// physical cores. One worker set is created for each socket address to be bound. For example,\n    /// if workers is set to 4, and there are 2 addresses to bind, then 8 worker threads will be\n    /// spawned.\n    ///\n    /// # Panics\n    /// This methods panics if no socket addresses were successfully bound or if no Tokio runtime\n    /// is set up.\n    pub fn run(self) -> Server {\n        self.builder.run()\n    }\n}\n\n/// Bind TCP listeners to socket addresses resolved from `addrs` with options.\nfn bind_addrs(addrs: impl net::ToSocketAddrs, backlog: u32) -> io::Result<Vec<net::TcpListener>> {\n    let mut err = None;\n    let mut success = false;\n    let mut sockets = Vec::new();\n\n    for addr in addrs.to_socket_addrs()? {\n        match create_tcp_listener(addr, backlog) {\n            Ok(lst) => {\n                success = true;\n                sockets.push(lst);\n            }\n            Err(error) => err = Some(error),\n        }\n    }\n\n    if success {\n        Ok(sockets)\n    } else if let Some(err) = err.take() {\n        Err(err)\n    } else {\n        Err(io::Error::other(\"Could not bind to address\"))\n    }\n}\n\n/// Creates a TCP listener from socket address and options.\nfn create_tcp_listener(addr: net::SocketAddr, backlog: u32) -> io::Result<net::TcpListener> {\n    use socket2::{Domain, Protocol, Socket, Type};\n    let domain = Domain::for_address(addr);\n    let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;\n    #[cfg(not(windows))]\n    {\n        socket.set_reuse_address(true)?;\n    }\n    socket.bind(&addr.into())?;\n    // clamp backlog to max u32 that fits in i32 range\n    let backlog = cmp::min(backlog, i32::MAX as u32) as i32;\n    socket.listen(backlog)?;\n    Ok(net::TcpListener::from(socket))\n}\n\n/// Configures OpenSSL acceptor `builder` with ALPN protocols.\n#[cfg(feature = \"openssl\")]\nfn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> {\n    builder.set_alpn_select_callback(|_, protocols| {\n        const H2: &[u8] = b\"\\x02h2\";\n        const H11: &[u8] = b\"\\x08http/1.1\";\n\n        if protocols.windows(3).any(|window| window == H2) {\n            Ok(b\"h2\")\n        } else if protocols.windows(9).any(|window| window == H11) {\n            Ok(b\"http/1.1\")\n        } else {\n            Err(AlpnError::NOACK)\n        }\n    });\n\n    builder.set_alpn_protos(b\"\\x08http/1.1\\x02h2\")?;\n\n    Ok(builder.build())\n}\n"
  },
  {
    "path": "actix-web/src/service.rs",
    "content": "use std::{\n    cell::{Ref, RefMut},\n    fmt, net,\n    rc::Rc,\n};\n\nuse actix_http::{\n    body::{BoxBody, EitherBody, MessageBody},\n    header::HeaderMap,\n    BoxedPayloadStream, Extensions, HttpMessage, Method, Payload, RequestHead, Response,\n    ResponseHead, StatusCode, Uri, Version,\n};\nuse actix_router::{IntoPatterns, Path, Patterns, Resource, ResourceDef, Url};\nuse actix_service::{\n    boxed::{BoxService, BoxServiceFactory},\n    IntoServiceFactory, ServiceFactory,\n};\n#[cfg(feature = \"cookies\")]\nuse cookie::{Cookie, ParseError as CookieParseError};\n\nuse crate::{\n    config::{AppConfig, AppService},\n    dev::ensure_leading_slash,\n    guard::{Guard, GuardContext},\n    info::ConnectionInfo,\n    rmap::ResourceMap,\n    Error, FromRequest, HttpRequest, HttpResponse,\n};\n\npub(crate) type BoxedHttpService = BoxService<ServiceRequest, ServiceResponse<BoxBody>, Error>;\npub(crate) type BoxedHttpServiceFactory =\n    BoxServiceFactory<(), ServiceRequest, ServiceResponse<BoxBody>, Error, ()>;\n\npub trait HttpServiceFactory {\n    fn register(self, config: &mut AppService);\n}\n\nimpl<T: HttpServiceFactory> HttpServiceFactory for Vec<T> {\n    fn register(self, config: &mut AppService) {\n        self.into_iter()\n            .for_each(|factory| factory.register(config));\n    }\n}\n\npub(crate) trait AppServiceFactory {\n    fn register(&mut self, config: &mut AppService);\n}\n\npub(crate) struct ServiceFactoryWrapper<T> {\n    factory: Option<T>,\n}\n\nimpl<T> ServiceFactoryWrapper<T> {\n    pub fn new(factory: T) -> Self {\n        Self {\n            factory: Some(factory),\n        }\n    }\n}\n\nimpl<T> AppServiceFactory for ServiceFactoryWrapper<T>\nwhere\n    T: HttpServiceFactory,\n{\n    fn register(&mut self, config: &mut AppService) {\n        if let Some(item) = self.factory.take() {\n            item.register(config)\n        }\n    }\n}\n\n/// A service level request wrapper.\n///\n/// Allows mutable access to request's internal structures.\npub struct ServiceRequest {\n    req: HttpRequest,\n    payload: Payload,\n}\n\nimpl ServiceRequest {\n    /// Construct `ServiceRequest` from parts.\n    pub(crate) fn new(req: HttpRequest, payload: Payload) -> Self {\n        Self { req, payload }\n    }\n\n    /// Deconstruct `ServiceRequest` into inner parts.\n    #[inline]\n    pub fn into_parts(self) -> (HttpRequest, Payload) {\n        (self.req, self.payload)\n    }\n\n    /// Returns mutable accessors to inner parts.\n    #[inline]\n    pub fn parts_mut(&mut self) -> (&mut HttpRequest, &mut Payload) {\n        (&mut self.req, &mut self.payload)\n    }\n\n    /// Returns immutable accessors to inner parts.\n    #[inline]\n    pub fn parts(&self) -> (&HttpRequest, &Payload) {\n        (&self.req, &self.payload)\n    }\n\n    /// Returns immutable accessor to inner [`HttpRequest`].\n    #[inline]\n    pub fn request(&self) -> &HttpRequest {\n        &self.req\n    }\n\n    /// Derives a type from this request using an [extractor](crate::FromRequest).\n    ///\n    /// Returns the `T` extractor's `Future` type which can be `await`ed. This is particularly handy\n    /// when you want to use an extractor in a middleware implementation.\n    ///\n    /// # Examples\n    /// ```\n    /// use actix_web::{\n    ///     dev::{ServiceRequest, ServiceResponse},\n    ///     web::Path, Error\n    /// };\n    ///\n    /// async fn my_helper(mut srv_req: ServiceRequest) -> Result<ServiceResponse, Error> {\n    ///     let path = srv_req.extract::<Path<(String, u32)>>().await?;\n    ///     // [...]\n    /// #   todo!()\n    /// }\n    /// ```\n    pub fn extract<T>(&mut self) -> <T as FromRequest>::Future\n    where\n        T: FromRequest,\n    {\n        T::from_request(&self.req, &mut self.payload)\n    }\n\n    /// Construct request from parts.\n    pub fn from_parts(req: HttpRequest, payload: Payload) -> Self {\n        #[cfg(debug_assertions)]\n        if Rc::strong_count(&req.inner) > 1 {\n            log::warn!(\"Cloning an `HttpRequest` might cause panics.\");\n        }\n\n        Self { req, payload }\n    }\n\n    /// Construct `ServiceRequest` with no payload from given `HttpRequest`.\n    #[inline]\n    pub fn from_request(req: HttpRequest) -> Self {\n        ServiceRequest {\n            req,\n            payload: Payload::None,\n        }\n    }\n\n    /// Create `ServiceResponse` from this request and given response.\n    #[inline]\n    pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> {\n        let res = HttpResponse::from(res.into());\n        ServiceResponse::new(self.req, res)\n    }\n\n    /// Create `ServiceResponse` from this request and given error.\n    #[inline]\n    pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse {\n        let res = HttpResponse::from_error(err.into());\n        ServiceResponse::new(self.req, res)\n    }\n\n    /// Returns a reference to the request head.\n    #[inline]\n    pub fn head(&self) -> &RequestHead {\n        self.req.head()\n    }\n\n    /// Returns a mutable reference to the request head.\n    #[inline]\n    pub fn head_mut(&mut self) -> &mut RequestHead {\n        self.req.head_mut()\n    }\n\n    /// Returns the request URI.\n    #[inline]\n    pub fn uri(&self) -> &Uri {\n        &self.head().uri\n    }\n\n    /// Returns the request method.\n    #[inline]\n    pub fn method(&self) -> &Method {\n        &self.head().method\n    }\n\n    /// Returns the request version.\n    #[inline]\n    pub fn version(&self) -> Version {\n        self.head().version\n    }\n\n    /// Returns a reference to request headers.\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    /// Returns a mutable reference to request headers.\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.head_mut().headers\n    }\n\n    /// Returns request path.\n    #[inline]\n    pub fn path(&self) -> &str {\n        self.head().uri.path()\n    }\n\n    /// Counterpart to [`HttpRequest::query_string`].\n    #[inline]\n    pub fn query_string(&self) -> &str {\n        self.req.query_string()\n    }\n\n    /// Returns peer's socket address.\n    ///\n    /// See [`HttpRequest::peer_addr`] for more details.\n    ///\n    /// [`HttpRequest::peer_addr`]: crate::HttpRequest::peer_addr\n    #[inline]\n    pub fn peer_addr(&self) -> Option<net::SocketAddr> {\n        self.head().peer_addr\n    }\n\n    /// Returns a reference to connection info.\n    #[inline]\n    pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {\n        self.req.connection_info()\n    }\n\n    /// Counterpart to [`HttpRequest::match_info`].\n    #[inline]\n    pub fn match_info(&self) -> &Path<Url> {\n        self.req.match_info()\n    }\n\n    /// Returns a mutable reference to the path match information.\n    #[inline]\n    pub fn match_info_mut(&mut self) -> &mut Path<Url> {\n        self.req.match_info_mut()\n    }\n\n    /// Counterpart to [`HttpRequest::match_name`].\n    #[inline]\n    pub fn match_name(&self) -> Option<&str> {\n        self.req.match_name()\n    }\n\n    /// Counterpart to [`HttpRequest::match_pattern`].\n    #[inline]\n    pub fn match_pattern(&self) -> Option<String> {\n        self.req.match_pattern()\n    }\n\n    /// Returns a reference to the application's resource map.\n    /// Counterpart to [`HttpRequest::resource_map`].\n    #[inline]\n    pub fn resource_map(&self) -> &ResourceMap {\n        self.req.resource_map()\n    }\n\n    /// Counterpart to [`HttpRequest::app_config`].\n    #[inline]\n    pub fn app_config(&self) -> &AppConfig {\n        self.req.app_config()\n    }\n\n    /// Counterpart to [`HttpRequest::app_data`].\n    #[inline]\n    pub fn app_data<T: 'static>(&self) -> Option<&T> {\n        for container in self.req.inner.app_data.iter().rev() {\n            if let Some(data) = container.get::<T>() {\n                return Some(data);\n            }\n        }\n\n        None\n    }\n\n    /// Counterpart to [`HttpRequest::conn_data`].\n    #[inline]\n    pub fn conn_data<T: 'static>(&self) -> Option<&T> {\n        self.req.conn_data()\n    }\n\n    /// Return request cookies.\n    #[cfg(feature = \"cookies\")]\n    #[inline]\n    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {\n        self.req.cookies()\n    }\n\n    /// Return request cookie.\n    #[cfg(feature = \"cookies\")]\n    #[inline]\n    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {\n        self.req.cookie(name)\n    }\n\n    /// Set request payload.\n    #[inline]\n    pub fn set_payload(&mut self, payload: Payload) {\n        self.payload = payload;\n    }\n\n    /// Add data container to request's resolution set.\n    ///\n    /// In middleware, prefer [`extensions_mut`](ServiceRequest::extensions_mut) for request-local\n    /// data since it is assumed that the same app data is presented for every request.\n    pub fn add_data_container(&mut self, extensions: Rc<Extensions>) {\n        Rc::get_mut(&mut (self.req).inner)\n            .unwrap()\n            .app_data\n            .push(extensions);\n    }\n\n    #[inline]\n    pub(crate) fn push_resource_id(&mut self, id: u16) {\n        self.req.push_resource_id(id);\n    }\n\n    #[inline]\n    pub(crate) fn mark_resource_path(&mut self, is_matched: bool) {\n        self.req.mark_resource_path(is_matched);\n    }\n\n    #[inline]\n    pub(crate) fn resource_id_path(&self) -> &[u16] {\n        self.req.resource_path()\n    }\n\n    /// Creates a context object for use with a routing [guard](crate::guard).\n    #[inline]\n    pub fn guard_ctx(&self) -> GuardContext<'_> {\n        GuardContext { req: self }\n    }\n}\n\nimpl Resource for ServiceRequest {\n    type Path = Url;\n\n    #[inline]\n    fn resource_path(&mut self) -> &mut Path<Self::Path> {\n        self.match_info_mut()\n    }\n}\n\nimpl HttpMessage for ServiceRequest {\n    type Stream = BoxedPayloadStream;\n\n    #[inline]\n    fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    #[inline]\n    fn extensions(&self) -> Ref<'_, Extensions> {\n        self.req.extensions()\n    }\n\n    #[inline]\n    fn extensions_mut(&self) -> RefMut<'_, Extensions> {\n        self.req.extensions_mut()\n    }\n\n    #[inline]\n    fn take_payload(&mut self) -> Payload<Self::Stream> {\n        self.payload.take()\n    }\n}\n\nimpl fmt::Debug for ServiceRequest {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(\n            f,\n            \"\\nServiceRequest {:?} {}:{}\",\n            self.head().version,\n            self.head().method,\n            self.path()\n        )?;\n        if !self.query_string().is_empty() {\n            writeln!(f, \"  query: ?{:?}\", self.query_string())?;\n        }\n        if !self.match_info().is_empty() {\n            writeln!(f, \"  params: {:?}\", self.match_info())?;\n        }\n        writeln!(f, \"  headers:\")?;\n        for (key, val) in self.headers().iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n        Ok(())\n    }\n}\n\n/// A service level response wrapper.\npub struct ServiceResponse<B = BoxBody> {\n    request: HttpRequest,\n    response: HttpResponse<B>,\n}\n\nimpl ServiceResponse<BoxBody> {\n    /// Create service response from the error\n    pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self {\n        let response = HttpResponse::from_error(err);\n        ServiceResponse { request, response }\n    }\n}\n\nimpl<B> ServiceResponse<B> {\n    /// Create service response instance\n    pub fn new(request: HttpRequest, response: HttpResponse<B>) -> Self {\n        ServiceResponse { request, response }\n    }\n\n    /// Create service response for error\n    #[inline]\n    pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse {\n        ServiceResponse::from_err(err, self.request)\n    }\n\n    /// Create service response\n    #[inline]\n    pub fn into_response<B1>(self, response: HttpResponse<B1>) -> ServiceResponse<B1> {\n        ServiceResponse::new(self.request, response)\n    }\n\n    /// Returns reference to original request.\n    #[inline]\n    pub fn request(&self) -> &HttpRequest {\n        &self.request\n    }\n\n    /// Returns reference to response.\n    #[inline]\n    pub fn response(&self) -> &HttpResponse<B> {\n        &self.response\n    }\n\n    /// Returns mutable reference to response.\n    #[inline]\n    pub fn response_mut(&mut self) -> &mut HttpResponse<B> {\n        &mut self.response\n    }\n\n    /// Returns response status code.\n    #[inline]\n    pub fn status(&self) -> StatusCode {\n        self.response.status()\n    }\n\n    /// Returns response's headers.\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        self.response.headers()\n    }\n\n    /// Returns mutable response's headers.\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        self.response.headers_mut()\n    }\n\n    /// Destructures `ServiceResponse` into request and response components.\n    #[inline]\n    pub fn into_parts(self) -> (HttpRequest, HttpResponse<B>) {\n        (self.request, self.response)\n    }\n\n    /// Map the current body type to another using a closure. Returns a new response.\n    ///\n    /// Closure receives the response head and the current body type.\n    #[inline]\n    pub fn map_body<F, B2>(self, f: F) -> ServiceResponse<B2>\n    where\n        F: FnOnce(&mut ResponseHead, B) -> B2,\n    {\n        let response = self.response.map_body(f);\n\n        ServiceResponse {\n            response,\n            request: self.request,\n        }\n    }\n\n    #[inline]\n    pub fn map_into_left_body<R>(self) -> ServiceResponse<EitherBody<B, R>> {\n        self.map_body(|_, body| EitherBody::left(body))\n    }\n\n    #[inline]\n    pub fn map_into_right_body<L>(self) -> ServiceResponse<EitherBody<L, B>> {\n        self.map_body(|_, body| EitherBody::right(body))\n    }\n\n    #[inline]\n    pub fn map_into_boxed_body(self) -> ServiceResponse<BoxBody>\n    where\n        B: MessageBody + 'static,\n    {\n        self.map_body(|_, body| body.boxed())\n    }\n\n    /// Consumes the response and returns its body.\n    #[inline]\n    pub fn into_body(self) -> B {\n        self.response.into_body()\n    }\n}\n\nimpl<B> From<ServiceResponse<B>> for HttpResponse<B> {\n    fn from(res: ServiceResponse<B>) -> HttpResponse<B> {\n        res.response\n    }\n}\n\nimpl<B> From<ServiceResponse<B>> for Response<B> {\n    fn from(res: ServiceResponse<B>) -> Response<B> {\n        res.response.into()\n    }\n}\n\nimpl<B> fmt::Debug for ServiceResponse<B>\nwhere\n    B: MessageBody,\n    B::Error: Into<Error>,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let res = writeln!(\n            f,\n            \"\\nServiceResponse {:?} {}{}\",\n            self.response.head().version,\n            self.response.head().status,\n            self.response.head().reason.unwrap_or(\"\"),\n        );\n        let _ = writeln!(f, \"  headers:\");\n        for (key, val) in self.response.head().headers.iter() {\n            let _ = writeln!(f, \"    {:?}: {:?}\", key, val);\n        }\n        let _ = writeln!(f, \"  body: {:?}\", self.response.body().size());\n        res\n    }\n}\n\npub struct WebService {\n    rdef: Patterns,\n    name: Option<String>,\n    guards: Vec<Box<dyn Guard>>,\n}\n\nimpl WebService {\n    /// Create new `WebService` instance.\n    pub fn new<T: IntoPatterns>(path: T) -> Self {\n        WebService {\n            rdef: path.patterns(),\n            name: None,\n            guards: Vec::new(),\n        }\n    }\n\n    /// Set service name.\n    ///\n    /// Name is used for URL generation.\n    pub fn name(mut self, name: &str) -> Self {\n        self.name = Some(name.to_string());\n        self\n    }\n\n    /// Add match guard to a web service.\n    ///\n    /// ```\n    /// use actix_web::{web, guard, dev, App, Error, HttpResponse};\n    ///\n    /// async fn index(req: dev::ServiceRequest) -> Result<dev::ServiceResponse, Error> {\n    ///     Ok(req.into_response(HttpResponse::Ok().finish()))\n    /// }\n    ///\n    /// let app = App::new()\n    ///     .service(\n    ///         web::service(\"/app\")\n    ///             .guard(guard::Header(\"content-type\", \"text/plain\"))\n    ///             .finish(index)\n    ///     );\n    /// ```\n    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {\n        self.guards.push(Box::new(guard));\n        self\n    }\n\n    /// Set a service factory implementation and generate web service.\n    pub fn finish<T, F>(self, service: F) -> impl HttpServiceFactory\n    where\n        F: IntoServiceFactory<T, ServiceRequest>,\n        T: ServiceFactory<\n                ServiceRequest,\n                Config = (),\n                Response = ServiceResponse,\n                Error = Error,\n                InitError = (),\n            > + 'static,\n    {\n        WebServiceImpl {\n            srv: service.into_factory(),\n            rdef: self.rdef,\n            name: self.name,\n            guards: self.guards,\n        }\n    }\n}\n\nstruct WebServiceImpl<T> {\n    srv: T,\n    rdef: Patterns,\n    name: Option<String>,\n    guards: Vec<Box<dyn Guard>>,\n}\n\nimpl<T> HttpServiceFactory for WebServiceImpl<T>\nwhere\n    T: ServiceFactory<\n            ServiceRequest,\n            Config = (),\n            Response = ServiceResponse,\n            Error = Error,\n            InitError = (),\n        > + 'static,\n{\n    fn register(mut self, config: &mut AppService) {\n        let guards = if self.guards.is_empty() {\n            None\n        } else {\n            Some(std::mem::take(&mut self.guards))\n        };\n\n        let mut rdef = if config.is_root() || !self.rdef.is_empty() {\n            ResourceDef::new(ensure_leading_slash(self.rdef))\n        } else {\n            ResourceDef::new(self.rdef)\n        };\n\n        if let Some(ref name) = self.name {\n            rdef.set_name(name);\n        }\n\n        config.register_service(rdef, guards, self.srv, None)\n    }\n}\n\n/// Macro to help register different types of services at the same time.\n///\n/// The max number of services that can be grouped together is 12 and all must implement the\n/// [`HttpServiceFactory`] trait.\n///\n/// # Examples\n/// ```\n/// use actix_web::{services, web, App};\n///\n/// let services = services![\n///     web::resource(\"/test2\").to(|| async { \"test2\" }),\n///     web::scope(\"/test3\").route(\"/\", web::get().to(|| async { \"test3\" }))\n/// ];\n///\n/// let app = App::new().service(services);\n///\n/// // services macro just convert multiple services to a tuple.\n/// // below would also work without importing the macro.\n/// let app = App::new().service((\n///     web::resource(\"/test2\").to(|| async { \"test2\" }),\n///     web::scope(\"/test3\").route(\"/\", web::get().to(|| async { \"test3\" }))\n/// ));\n/// ```\n#[macro_export]\nmacro_rules! services {\n    () => {()};\n    ($($x:expr),+ $(,)?) => {\n        ($($x,)+)\n    }\n}\n\n/// HttpServiceFactory trait impl for tuples\nmacro_rules! service_tuple ({ $($T:ident)+ } => {\n    impl<$($T: HttpServiceFactory),+> HttpServiceFactory for ($($T,)+) {\n        #[allow(non_snake_case)]\n        fn register(self, config: &mut AppService) {\n            let ($($T,)*) = self;\n            $($T.register(config);)+\n        }\n    }\n});\n\nservice_tuple! { A }\nservice_tuple! { A B }\nservice_tuple! { A B C }\nservice_tuple! { A B C D }\nservice_tuple! { A B C D E }\nservice_tuple! { A B C D E F }\nservice_tuple! { A B C D E F G }\nservice_tuple! { A B C D E F G H }\nservice_tuple! { A B C D E F G H I }\nservice_tuple! { A B C D E F G H I J }\nservice_tuple! { A B C D E F G H I J K }\nservice_tuple! { A B C D E F G H I J K L }\n\n#[cfg(test)]\nmod tests {\n    use actix_service::Service;\n    use actix_utils::future::ok;\n\n    use super::*;\n    use crate::{\n        guard, http,\n        test::{self, init_service, TestRequest},\n        web, App,\n    };\n\n    #[actix_rt::test]\n    async fn test_service() {\n        let srv =\n            init_service(\n                App::new().service(web::service(\"/test\").name(\"test\").finish(\n                    |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),\n                )),\n            )\n            .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let srv =\n            init_service(\n                App::new().service(web::service(\"/test\").guard(guard::Get()).finish(\n                    |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),\n                )),\n            )\n            .await;\n        let req = TestRequest::with_uri(\"/test\")\n            .method(http::Method::PUT)\n            .to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::NOT_FOUND);\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_service_data() {\n        let srv = init_service(\n            App::new()\n                .data(42u32)\n                .service(\n                    web::service(\"/test\")\n                        .name(\"test\")\n                        .finish(|req: ServiceRequest| {\n                            assert_eq!(req.app_data::<web::Data<u32>>().unwrap().as_ref(), &42);\n                            ok(req.into_response(HttpResponse::Ok().finish()))\n                        }),\n                ),\n        )\n        .await;\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n    }\n\n    #[test]\n    fn test_fmt_debug() {\n        let req = TestRequest::get()\n            .uri(\"/index.html?test=1\")\n            .insert_header((\"x-test\", \"111\"))\n            .to_srv_request();\n        let s = format!(\"{:?}\", req);\n        assert!(s.contains(\"ServiceRequest\"));\n        assert!(s.contains(\"test=1\"));\n        assert!(s.contains(\"x-test\"));\n\n        let res = HttpResponse::Ok().insert_header((\"x-test\", \"111\")).finish();\n        let res = TestRequest::post()\n            .uri(\"/index.html?test=1\")\n            .to_srv_response(res);\n\n        let s = format!(\"{:?}\", res);\n        assert!(s.contains(\"ServiceResponse\"));\n        assert!(s.contains(\"x-test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_services_macro() {\n        let scoped = services![\n            web::service(\"/scoped_test1\").name(\"scoped_test1\").finish(\n                |req: ServiceRequest| async { Ok(req.into_response(HttpResponse::Ok().finish())) }\n            ),\n            web::resource(\"/scoped_test2\").to(|| async { \"test2\" }),\n        ];\n\n        let services = services![\n            web::service(\"/test1\")\n                .name(\"test\")\n                .finish(|req: ServiceRequest| async {\n                    Ok(req.into_response(HttpResponse::Ok().finish()))\n                }),\n            web::resource(\"/test2\").to(|| async { \"test2\" }),\n            web::scope(\"/test3\").service(scoped)\n        ];\n\n        let srv = init_service(App::new().service(services)).await;\n\n        let req = TestRequest::with_uri(\"/test1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test3/scoped_test1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test3/scoped_test2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_services_vec() {\n        let services = vec![\n            web::resource(\"/test1\").to(|| async { \"test1\" }),\n            web::resource(\"/test2\").to(|| async { \"test2\" }),\n        ];\n\n        let scoped = vec![\n            web::resource(\"/scoped_test1\").to(|| async { \"test1\" }),\n            web::resource(\"/scoped_test2\").to(|| async { \"test2\" }),\n        ];\n\n        let srv = init_service(\n            App::new()\n                .service(services)\n                .service(web::scope(\"/test3\").service(scoped)),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/test1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test3/scoped_test1\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/test3/scoped_test2\").to_request();\n        let resp = srv.call(req).await.unwrap();\n        assert_eq!(resp.status(), http::StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    #[should_panic(expected = \"called `Option::unwrap()` on a `None` value\")]\n    async fn cloning_request_panics() {\n        async fn index(_name: web::Path<(String,)>) -> &'static str {\n            \"\"\n        }\n\n        let app = test::init_service(\n            App::new()\n                .wrap_fn(|req, svc| {\n                    let (req, pl) = req.into_parts();\n                    let _req2 = req.clone();\n                    let req = ServiceRequest::from_parts(req, pl);\n                    svc.call(req)\n                })\n                .route(\"/\", web::get().to(|| async { \"\" }))\n                .service(web::resource(\"/resource1/{name}/index.html\").route(web::get().to(index))),\n        )\n        .await;\n\n        let req = test::TestRequest::default().to_request();\n        let _res = test::call_service(&app, req).await;\n    }\n\n    #[test]\n    fn define_services_macro_with_multiple_arguments() {\n        let result = services!(1, 2, 3);\n        assert_eq!(result, (1, 2, 3));\n    }\n\n    #[test]\n    fn define_services_macro_with_single_argument() {\n        let result = services!(1);\n        assert_eq!(result, (1,));\n    }\n\n    #[test]\n    fn define_services_macro_with_no_arguments() {\n        let result = services!();\n        let () = result;\n    }\n\n    #[test]\n    fn define_services_macro_with_trailing_comma() {\n        let result = services!(1, 2, 3,);\n        assert_eq!(result, (1, 2, 3));\n    }\n\n    #[test]\n    fn define_services_macro_with_comments_in_arguments() {\n        let result = services!(\n            1, // First comment\n            2, // Second comment\n            3  // Third comment\n        );\n\n        // Assert that comments are ignored and it correctly returns a tuple.\n        assert_eq!(result, (1, 2, 3));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/test/mod.rs",
    "content": "//! Various helpers for Actix applications to use during testing.\n//!\n//! # Initializing A Test Service\n//! - [`init_service`]\n//!\n//! # Off-The-Shelf Test Services\n//! - [`ok_service`]\n//! - [`status_service`]\n//!\n//! # Calling Test Service\n//! - [`TestRequest`]\n//! - [`call_service`]\n//! - [`try_call_service`]\n//! - [`call_and_read_body`]\n//! - [`call_and_read_body_json`]\n//! - [`try_call_and_read_body_json`]\n//!\n//! # Reading Response Payloads\n//! - [`read_body`]\n//! - [`try_read_body`]\n//! - [`read_body_json`]\n//! - [`try_read_body_json`]\n\n// TODO: more docs on generally how testing works with these parts\n\npub use actix_http::test::TestBuffer;\n\nmod test_request;\nmod test_services;\nmod test_utils;\n\n#[allow(deprecated)]\npub use self::test_services::{default_service, ok_service, simple_service, status_service};\n#[cfg(test)]\npub(crate) use self::test_utils::try_init_service;\n#[allow(deprecated)]\npub use self::test_utils::{read_response, read_response_json};\npub use self::{\n    test_request::TestRequest,\n    test_utils::{\n        call_and_read_body, call_and_read_body_json, call_service, init_service, read_body,\n        read_body_json, try_call_and_read_body_json, try_call_service, try_read_body,\n        try_read_body_json,\n    },\n};\n\n/// Reduces boilerplate code when testing expected response payloads.\n///\n/// Must be used inside an async test. Works for both `ServiceRequest` and `HttpRequest`.\n///\n/// # Examples\n///\n/// ```\n/// use actix_web::{http::StatusCode, HttpResponse};\n///\n/// let res = HttpResponse::with_body(StatusCode::OK, \"http response\");\n/// assert_body_eq!(res, b\"http response\");\n/// ```\n#[cfg(test)]\nmacro_rules! assert_body_eq {\n    ($res:ident, $expected:expr) => {\n        assert_eq!(\n            ::actix_http::body::to_bytes($res.into_body())\n                .await\n                .expect(\"error reading test response body\"),\n            ::bytes::Bytes::from_static($expected),\n        )\n    };\n}\n\n#[cfg(test)]\npub(crate) use assert_body_eq;\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{http::StatusCode, service::ServiceResponse, HttpResponse};\n\n    #[actix_rt::test]\n    async fn assert_body_works_for_service_and_regular_response() {\n        let res = HttpResponse::with_body(StatusCode::OK, \"http response\");\n        assert_body_eq!(res, b\"http response\");\n\n        let req = TestRequest::default().to_http_request();\n        let res = HttpResponse::with_body(StatusCode::OK, \"service response\");\n        let res = ServiceResponse::new(req, res);\n        assert_body_eq!(res, b\"service response\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/test/test_request.rs",
    "content": "use std::{borrow::Cow, net::SocketAddr, rc::Rc};\n\nuse actix_http::{test::TestRequest as HttpTestRequest, Request};\nuse serde::Serialize;\n\n#[cfg(feature = \"cookies\")]\nuse crate::cookie::{Cookie, CookieJar};\nuse crate::{\n    app_service::AppInitServiceState,\n    config::AppConfig,\n    data::Data,\n    dev::{Extensions, Path, Payload, ResourceDef, Service, Url},\n    http::{\n        header::{ContentType, TryIntoHeaderPair},\n        Method, Uri, Version,\n    },\n    rmap::ResourceMap,\n    service::{ServiceRequest, ServiceResponse},\n    test,\n    web::Bytes,\n    HttpRequest, HttpResponse,\n};\n\n/// Test `Request` builder.\n///\n/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.\n/// You can generate various types of request via TestRequest's methods:\n/// - [`TestRequest::to_request`] creates an [`actix_http::Request`](Request).\n/// - [`TestRequest::to_srv_request`] creates a [`ServiceRequest`], which is used for testing middlewares and chain adapters.\n/// - [`TestRequest::to_srv_response`] creates a [`ServiceResponse`].\n/// - [`TestRequest::to_http_request`] creates an [`HttpRequest`], which is used for testing handlers.\n///\n/// ```\n/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};\n/// use actix_web::http::{header, StatusCode};\n///\n/// async fn handler(req: HttpRequest) -> HttpResponse {\n///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {\n///         HttpResponse::Ok().into()\n///     } else {\n///         HttpResponse::BadRequest().into()\n///     }\n/// }\n///\n/// #[actix_web::test]\n/// # // force rustdoc to display the correct thing and also compile check the test\n/// # async fn _test() {}\n/// async fn test_index() {\n///     let req = test::TestRequest::default()\n///         .insert_header(header::ContentType::plaintext())\n///         .to_http_request();\n///\n///     let resp = handler(req).await;\n///     assert_eq!(resp.status(), StatusCode::OK);\n///\n///     let req = test::TestRequest::default().to_http_request();\n///     let resp = handler(req).await;\n///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n/// }\n/// ```\npub struct TestRequest {\n    req: HttpTestRequest,\n    rmap: ResourceMap,\n    config: AppConfig,\n    path: Path<Url>,\n    peer_addr: Option<SocketAddr>,\n    app_data: Extensions,\n    #[cfg(feature = \"cookies\")]\n    cookies: CookieJar,\n}\n\nimpl Default for TestRequest {\n    fn default() -> TestRequest {\n        TestRequest {\n            req: HttpTestRequest::default(),\n            rmap: ResourceMap::new(ResourceDef::new(\"\")),\n            config: AppConfig::default(),\n            path: Path::new(Url::new(Uri::default())),\n            peer_addr: None,\n            app_data: Extensions::new(),\n            #[cfg(feature = \"cookies\")]\n            cookies: CookieJar::new(),\n        }\n    }\n}\n\n#[allow(clippy::wrong_self_convention)]\nimpl TestRequest {\n    /// Constructs test request and sets request URI.\n    pub fn with_uri(uri: &str) -> TestRequest {\n        TestRequest::default().uri(uri)\n    }\n\n    /// Constructs test request with GET method.\n    pub fn get() -> TestRequest {\n        TestRequest::default().method(Method::GET)\n    }\n\n    /// Constructs test request with POST method.\n    pub fn post() -> TestRequest {\n        TestRequest::default().method(Method::POST)\n    }\n\n    /// Constructs test request with PUT method.\n    pub fn put() -> TestRequest {\n        TestRequest::default().method(Method::PUT)\n    }\n\n    /// Constructs test request with PATCH method.\n    pub fn patch() -> TestRequest {\n        TestRequest::default().method(Method::PATCH)\n    }\n\n    /// Constructs test request with DELETE method.\n    pub fn delete() -> TestRequest {\n        TestRequest::default().method(Method::DELETE)\n    }\n\n    /// Sets HTTP version of this request.\n    pub fn version(mut self, ver: Version) -> Self {\n        self.req.version(ver);\n        self\n    }\n\n    /// Sets method of this request.\n    pub fn method(mut self, meth: Method) -> Self {\n        self.req.method(meth);\n        self\n    }\n\n    /// Sets URI of this request.\n    pub fn uri(mut self, path: &str) -> Self {\n        self.req.uri(path);\n        self\n    }\n\n    /// Inserts a header, replacing any that were set with an equivalent field name.\n    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        self.req.insert_header(header);\n        self\n    }\n\n    /// Appends a header, keeping any that were set with an equivalent field name.\n    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        self.req.append_header(header);\n        self\n    }\n\n    /// Sets cookie for this request.\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {\n        self.cookies.add(cookie.into_owned());\n        self\n    }\n\n    /// Sets request path pattern parameter.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use actix_web::test::TestRequest;\n    ///\n    /// let req = TestRequest::default().param(\"foo\", \"bar\");\n    /// let req = TestRequest::default().param(\"foo\".to_owned(), \"bar\".to_owned());\n    /// ```\n    pub fn param(\n        mut self,\n        name: impl Into<Cow<'static, str>>,\n        value: impl Into<Cow<'static, str>>,\n    ) -> Self {\n        self.path.add_static(name, value);\n        self\n    }\n\n    /// Sets peer address.\n    pub fn peer_addr(mut self, addr: SocketAddr) -> Self {\n        self.peer_addr = Some(addr);\n        self\n    }\n\n    /// Sets request payload.\n    pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {\n        self.req.set_payload(data);\n        self\n    }\n\n    /// Serializes `data` to a URL encoded form and set it as the request payload.\n    ///\n    /// The `Content-Type` header is set to `application/x-www-form-urlencoded`.\n    pub fn set_form(mut self, data: impl Serialize) -> Self {\n        let bytes = serde_urlencoded::to_string(&data)\n            .expect(\"Failed to serialize test data as a urlencoded form\");\n        self.req.set_payload(bytes);\n        self.req.insert_header(ContentType::form_url_encoded());\n        self\n    }\n\n    /// Serializes `data` to JSON and set it as the request payload.\n    ///\n    /// The `Content-Type` header is set to `application/json`.\n    pub fn set_json(mut self, data: impl Serialize) -> Self {\n        let bytes = serde_json::to_string(&data).expect(\"Failed to serialize test data to json\");\n        self.req.set_payload(bytes);\n        self.req.insert_header(ContentType::json());\n        self\n    }\n\n    /// Inserts application data.\n    ///\n    /// This is equivalent of `App::app_data()` method for testing purpose.\n    pub fn app_data<T: 'static>(mut self, data: T) -> Self {\n        self.app_data.insert(data);\n        self\n    }\n\n    /// Inserts application data.\n    ///\n    /// This is equivalent of `App::data()` method for testing purpose.\n    #[doc(hidden)]\n    pub fn data<T: 'static>(mut self, data: T) -> Self {\n        self.app_data.insert(Data::new(data));\n        self\n    }\n\n    /// Sets resource map.\n    #[cfg(test)]\n    pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {\n        self.rmap = rmap;\n        self\n    }\n\n    /// Finalizes test request.\n    ///\n    /// This request builder will be useless after calling `finish()`.\n    fn finish(&mut self) -> Request {\n        // mut used when cookie feature is enabled\n        #[allow(unused_mut)]\n        let mut req = self.req.finish();\n\n        #[cfg(feature = \"cookies\")]\n        {\n            use actix_http::header::{HeaderValue, COOKIE};\n\n            let cookie: String = self\n                .cookies\n                .delta()\n                // ensure only name=value is written to cookie header\n                .map(|c| c.stripped().encoded().to_string())\n                .collect::<Vec<_>>()\n                .join(\"; \");\n\n            if !cookie.is_empty() {\n                req.headers_mut()\n                    .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap());\n            }\n        }\n\n        req\n    }\n\n    /// Finalizes request creation and returns `Request` instance.\n    pub fn to_request(mut self) -> Request {\n        let mut req = self.finish();\n        req.head_mut().peer_addr = self.peer_addr;\n        req\n    }\n\n    /// Finalizes request creation and returns `ServiceRequest` instance.\n    pub fn to_srv_request(mut self) -> ServiceRequest {\n        let (mut head, payload) = self.finish().into_parts();\n        head.peer_addr = self.peer_addr;\n        self.path.get_mut().update(&head.uri);\n\n        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());\n\n        ServiceRequest::new(\n            HttpRequest::new(\n                self.path,\n                head,\n                app_state,\n                Rc::new(self.app_data),\n                None,\n                Default::default(),\n            ),\n            payload,\n        )\n    }\n\n    /// Finalizes request creation and returns `ServiceResponse` instance.\n    pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {\n        self.to_srv_request().into_response(res)\n    }\n\n    /// Finalizes request creation and returns `HttpRequest` instance.\n    pub fn to_http_request(mut self) -> HttpRequest {\n        let (mut head, _) = self.finish().into_parts();\n        head.peer_addr = self.peer_addr;\n        self.path.get_mut().update(&head.uri);\n\n        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());\n\n        HttpRequest::new(\n            self.path,\n            head,\n            app_state,\n            Rc::new(self.app_data),\n            None,\n            Default::default(),\n        )\n    }\n\n    /// Finalizes request creation and returns `HttpRequest` and `Payload` pair.\n    pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {\n        let (mut head, payload) = self.finish().into_parts();\n        head.peer_addr = self.peer_addr;\n        self.path.get_mut().update(&head.uri);\n\n        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());\n\n        let req = HttpRequest::new(\n            self.path,\n            head,\n            app_state,\n            Rc::new(self.app_data),\n            None,\n            Default::default(),\n        );\n\n        (req, payload)\n    }\n\n    /// Finalizes request creation, calls service, and waits for response future completion.\n    pub async fn send_request<S, B, E>(self, app: &S) -> S::Response\n    where\n        S: Service<Request, Response = ServiceResponse<B>, Error = E>,\n        E: std::fmt::Debug,\n    {\n        let req = self.to_request();\n        test::call_service(app, req).await\n    }\n\n    #[cfg(test)]\n    pub fn set_server_hostname(&mut self, host: &str) {\n        self.config.set_host(host)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::SystemTime;\n\n    use super::*;\n    use crate::{http::header, test::init_service, web, App, Error, Responder};\n\n    #[actix_rt::test]\n    async fn test_basics() {\n        let req = TestRequest::default()\n            .version(Version::HTTP_2)\n            .insert_header(header::ContentType::json())\n            .insert_header(header::Date(SystemTime::now().into()))\n            .param(\"test\", \"123\")\n            .data(10u32)\n            .app_data(20u64)\n            .peer_addr(\"127.0.0.1:8081\".parse().unwrap())\n            .to_http_request();\n        assert!(req.headers().contains_key(header::CONTENT_TYPE));\n        assert!(req.headers().contains_key(header::DATE));\n        assert_eq!(\n            req.head().peer_addr,\n            Some(\"127.0.0.1:8081\".parse().unwrap())\n        );\n        assert_eq!(&req.match_info()[\"test\"], \"123\");\n        assert_eq!(req.version(), Version::HTTP_2);\n        let data = req.app_data::<Data<u32>>().unwrap();\n        assert!(req.app_data::<Data<u64>>().is_none());\n        assert_eq!(*data.get_ref(), 10);\n\n        assert!(req.app_data::<u32>().is_none());\n        let data = req.app_data::<u64>().unwrap();\n        assert_eq!(*data, 20);\n    }\n\n    #[actix_rt::test]\n    async fn test_send_request() {\n        let app = init_service(\n            App::new().service(\n                web::resource(\"/index.html\")\n                    .route(web::get().to(|| HttpResponse::Ok().body(\"welcome!\"))),\n            ),\n        )\n        .await;\n\n        let resp = TestRequest::get()\n            .uri(\"/index.html\")\n            .send_request(&app)\n            .await;\n\n        let result = test::read_body(resp).await;\n        assert_eq!(result, Bytes::from_static(b\"welcome!\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_async_with_block() {\n        async fn async_with_block() -> Result<HttpResponse, Error> {\n            let res = web::block(move || Some(4usize).ok_or(\"wrong\")).await;\n\n            match res {\n                Ok(value) => Ok(HttpResponse::Ok()\n                    .content_type(\"text/plain\")\n                    .body(format!(\"Async with block value: {:?}\", value))),\n                Err(_) => panic!(\"Unexpected\"),\n            }\n        }\n\n        let app =\n            init_service(App::new().service(web::resource(\"/index.html\").to(async_with_block)))\n                .await;\n\n        let req = TestRequest::post().uri(\"/index.html\").to_request();\n        let res = app.call(req).await.unwrap();\n        assert!(res.status().is_success());\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_server_data() {\n        async fn handler(data: web::Data<usize>) -> impl Responder {\n            assert_eq!(**data, 10);\n            HttpResponse::Ok()\n        }\n\n        let app = init_service(\n            App::new()\n                .data(10usize)\n                .service(web::resource(\"/index.html\").to(handler)),\n        )\n        .await;\n\n        let req = TestRequest::post().uri(\"/index.html\").to_request();\n        let res = app.call(req).await.unwrap();\n        assert!(res.status().is_success());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/test/test_services.rs",
    "content": "use actix_utils::future::ok;\n\nuse crate::{\n    body::BoxBody,\n    dev::{fn_service, Service, ServiceRequest, ServiceResponse},\n    http::StatusCode,\n    Error, HttpResponseBuilder,\n};\n\n/// Creates service that always responds with `200 OK` and no body.\npub fn ok_service(\n) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {\n    status_service(StatusCode::OK)\n}\n\n/// Creates service that always responds with given status code and no body.\npub fn status_service(\n    status_code: StatusCode,\n) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {\n    fn_service(move |req: ServiceRequest| {\n        ok(req.into_response(HttpResponseBuilder::new(status_code).finish()))\n    })\n}\n\n#[doc(hidden)]\n#[deprecated(since = \"4.0.0\", note = \"Renamed to `status_service`.\")]\npub fn simple_service(\n    status_code: StatusCode,\n) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {\n    status_service(status_code)\n}\n\n#[doc(hidden)]\n#[deprecated(since = \"4.0.0\", note = \"Renamed to `status_service`.\")]\npub fn default_service(\n    status_code: StatusCode,\n) -> impl Service<ServiceRequest, Response = ServiceResponse<BoxBody>, Error = Error> {\n    status_service(status_code)\n}\n"
  },
  {
    "path": "actix-web/src/test/test_utils.rs",
    "content": "use std::error::Error as StdError;\n\nuse actix_http::Request;\nuse actix_service::IntoServiceFactory;\nuse serde::de::DeserializeOwned;\n\nuse crate::{\n    body::{self, MessageBody},\n    config::AppConfig,\n    dev::{Service, ServiceFactory},\n    service::ServiceResponse,\n    web::Bytes,\n    Error,\n};\n\n/// Initialize service from application builder instance.\n///\n/// # Examples\n/// ```\n/// use actix_service::Service;\n/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};\n///\n/// #[actix_web::test]\n/// async fn test_init_service() {\n///     let app = test::init_service(\n///         App::new()\n///             .service(web::resource(\"/test\").to(|| async { \"OK\" }))\n///     ).await;\n///\n///     // Create request object\n///     let req = test::TestRequest::with_uri(\"/test\").to_request();\n///\n///     // Execute application\n///     let res = app.call(req).await.unwrap();\n///     assert_eq!(res.status(), StatusCode::OK);\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if service initialization returns an error.\npub async fn init_service<R, S, B, E>(\n    app: R,\n) -> impl Service<Request, Response = ServiceResponse<B>, Error = E>\nwhere\n    R: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,\n    S::InitError: std::fmt::Debug,\n{\n    try_init_service(app)\n        .await\n        .expect(\"service initialization failed\")\n}\n\n/// Fallible version of [`init_service`] that allows testing initialization errors.\npub(crate) async fn try_init_service<R, S, B, E>(\n    app: R,\n) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>\nwhere\n    R: IntoServiceFactory<S, Request>,\n    S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,\n    S::InitError: std::fmt::Debug,\n{\n    let srv = app.into_factory();\n    srv.new_service(AppConfig::default()).await\n}\n\n/// Calls service and waits for response future completion.\n///\n/// # Examples\n/// ```\n/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};\n///\n/// #[actix_web::test]\n/// async fn test_response() {\n///     let app = test::init_service(\n///         App::new()\n///             .service(web::resource(\"/test\").to(|| async {\n///                 HttpResponse::Ok()\n///             }))\n///     ).await;\n///\n///     // Create request object\n///     let req = test::TestRequest::with_uri(\"/test\").to_request();\n///\n///     // Call application\n///     let res = test::call_service(&app, req).await;\n///     assert_eq!(res.status(), StatusCode::OK);\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if service call returns error. To handle errors use `app.call(req)`.\npub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response\nwhere\n    S: Service<R, Response = ServiceResponse<B>, Error = E>,\n    E: std::fmt::Debug,\n{\n    app.call(req)\n        .await\n        .expect(\"test service call returned error\")\n}\n\n/// Fallible version of [`call_service`] that allows testing response completion errors.\npub async fn try_call_service<S, R, B, E>(app: &S, req: R) -> Result<S::Response, E>\nwhere\n    S: Service<R, Response = ServiceResponse<B>, Error = E>,\n    E: std::fmt::Debug,\n{\n    app.call(req).await\n}\n\n/// Helper function that returns a response body of a TestRequest\n///\n/// # Examples\n/// ```\n/// use actix_web::{test, web, App, HttpResponse, http::header};\n/// use bytes::Bytes;\n///\n/// #[actix_web::test]\n/// async fn test_index() {\n///     let app = test::init_service(\n///         App::new().service(\n///             web::resource(\"/index.html\")\n///                 .route(web::post().to(|| async {\n///                     HttpResponse::Ok().body(\"welcome!\")\n///                 })))\n///     ).await;\n///\n///     let req = test::TestRequest::post()\n///         .uri(\"/index.html\")\n///         .header(header::CONTENT_TYPE, \"application/json\")\n///         .to_request();\n///\n///     let result = test::call_and_read_body(&app, req).await;\n///     assert_eq!(result, Bytes::from_static(b\"welcome!\"));\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if:\n/// - service call returns error;\n/// - body yields an error while it is being read.\npub async fn call_and_read_body<S, B>(app: &S, req: Request) -> Bytes\nwhere\n    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n{\n    let res = call_service(app, req).await;\n    read_body(res).await\n}\n\n#[doc(hidden)]\n#[deprecated(since = \"4.0.0\", note = \"Renamed to `call_and_read_body`.\")]\npub async fn read_response<S, B>(app: &S, req: Request) -> Bytes\nwhere\n    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n{\n    let res = call_service(app, req).await;\n    read_body(res).await\n}\n\n/// Helper function that returns a response body of a ServiceResponse.\n///\n/// # Examples\n/// ```\n/// use actix_web::{test, web, App, HttpResponse, http::header};\n/// use bytes::Bytes;\n///\n/// #[actix_web::test]\n/// async fn test_index() {\n///     let app = test::init_service(\n///         App::new().service(\n///             web::resource(\"/index.html\")\n///                 .route(web::post().to(|| async {\n///                     HttpResponse::Ok().body(\"welcome!\")\n///                 })))\n///     ).await;\n///\n///     let req = test::TestRequest::post()\n///         .uri(\"/index.html\")\n///         .header(header::CONTENT_TYPE, \"application/json\")\n///         .to_request();\n///\n///     let res = test::call_service(&app, req).await;\n///     let result = test::read_body(res).await;\n///     assert_eq!(result, Bytes::from_static(b\"welcome!\"));\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if body yields an error while it is being read.\npub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes\nwhere\n    B: MessageBody,\n{\n    try_read_body(res)\n        .await\n        .map_err(Into::<Box<dyn StdError>>::into)\n        .expect(\"error reading test response body\")\n}\n\n/// Fallible version of [`read_body`] that allows testing MessageBody reading errors.\npub async fn try_read_body<B>(res: ServiceResponse<B>) -> Result<Bytes, <B as MessageBody>::Error>\nwhere\n    B: MessageBody,\n{\n    let body = res.into_body();\n    body::to_bytes(body).await\n}\n\n/// Helper function that returns a deserialized response body of a ServiceResponse.\n///\n/// # Examples\n/// ```\n/// use actix_web::{App, test, web, HttpResponse, http::header};\n/// use serde::{Serialize, Deserialize};\n///\n/// #[derive(Serialize, Deserialize)]\n/// pub struct Person {\n///     id: String,\n///     name: String,\n/// }\n///\n/// #[actix_web::test]\n/// async fn test_post_person() {\n///     let app = test::init_service(\n///         App::new().service(\n///             web::resource(\"/people\")\n///                 .route(web::post().to(|person: web::Json<Person>| async {\n///                     HttpResponse::Ok()\n///                         .json(person)})\n///                     ))\n///     ).await;\n///\n///     let payload = r#\"{\"id\":\"12345\",\"name\":\"User name\"}\"#.as_bytes();\n///\n///     let res = test::TestRequest::post()\n///         .uri(\"/people\")\n///         .header(header::CONTENT_TYPE, \"application/json\")\n///         .set_payload(payload)\n///         .send_request(&mut app)\n///         .await;\n///\n///     assert!(res.status().is_success());\n///\n///     let result: Person = test::read_body_json(res).await;\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if:\n/// - body yields an error while it is being read;\n/// - received body is not a valid JSON representation of `T`.\npub async fn read_body_json<T, B>(res: ServiceResponse<B>) -> T\nwhere\n    B: MessageBody,\n    T: DeserializeOwned,\n{\n    try_read_body_json(res).await.unwrap_or_else(|err| {\n        panic!(\n            \"could not deserialize body into a {}\\nerr: {}\",\n            std::any::type_name::<T>(),\n            err,\n        )\n    })\n}\n\n/// Fallible version of [`read_body_json`] that allows testing response deserialization errors.\npub async fn try_read_body_json<T, B>(res: ServiceResponse<B>) -> Result<T, Box<dyn StdError>>\nwhere\n    B: MessageBody,\n    T: DeserializeOwned,\n{\n    let body = try_read_body(res)\n        .await\n        .map_err(Into::<Box<dyn StdError>>::into)?;\n    serde_json::from_slice(&body).map_err(Into::<Box<dyn StdError>>::into)\n}\n\n/// Helper function that returns a deserialized response body of a TestRequest\n///\n/// # Examples\n/// ```\n/// use actix_web::{App, test, web, HttpResponse, http::header};\n/// use serde::{Serialize, Deserialize};\n///\n/// #[derive(Serialize, Deserialize)]\n/// pub struct Person {\n///     id: String,\n///     name: String\n/// }\n///\n/// #[actix_web::test]\n/// async fn test_add_person() {\n///     let app = test::init_service(\n///         App::new().service(\n///             web::resource(\"/people\")\n///                 .route(web::post().to(|person: web::Json<Person>| async {\n///                     HttpResponse::Ok()\n///                         .json(person)})\n///                     ))\n///     ).await;\n///\n///     let payload = r#\"{\"id\":\"12345\",\"name\":\"User name\"}\"#.as_bytes();\n///\n///     let req = test::TestRequest::post()\n///         .uri(\"/people\")\n///         .header(header::CONTENT_TYPE, \"application/json\")\n///         .set_payload(payload)\n///         .to_request();\n///\n///     let result: Person = test::call_and_read_body_json(&mut app, req).await;\n/// }\n/// ```\n///\n/// # Panics\n/// Panics if:\n/// - service call returns an error body yields an error while it is being read;\n/// - body yields an error while it is being read;\n/// - received body is not a valid JSON representation of `T`.\npub async fn call_and_read_body_json<S, B, T>(app: &S, req: Request) -> T\nwhere\n    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n    T: DeserializeOwned,\n{\n    try_call_and_read_body_json(app, req).await.unwrap()\n}\n\n/// Fallible version of [`call_and_read_body_json`] that allows testing service call errors.\npub async fn try_call_and_read_body_json<S, B, T>(\n    app: &S,\n    req: Request,\n) -> Result<T, Box<dyn StdError>>\nwhere\n    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n    T: DeserializeOwned,\n{\n    let res = try_call_service(app, req)\n        .await\n        .map_err(Into::<Box<dyn StdError>>::into)?;\n    try_read_body_json(res).await\n}\n\n#[doc(hidden)]\n#[deprecated(since = \"4.0.0\", note = \"Renamed to `call_and_read_body_json`.\")]\npub async fn read_response_json<S, B, T>(app: &S, req: Request) -> T\nwhere\n    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,\n    B: MessageBody,\n    T: DeserializeOwned,\n{\n    call_and_read_body_json(app, req).await\n}\n\n#[cfg(test)]\nmod tests {\n    use serde::{Deserialize, Serialize};\n\n    use super::*;\n    use crate::{\n        dev::ServiceRequest, http::header, test::TestRequest, web, App, HttpMessage, HttpResponse,\n    };\n\n    #[actix_rt::test]\n    async fn test_request_methods() {\n        let app = init_service(\n            App::new().service(\n                web::resource(\"/index.html\")\n                    .route(web::put().to(|| HttpResponse::Ok().body(\"put!\")))\n                    .route(web::patch().to(|| HttpResponse::Ok().body(\"patch!\")))\n                    .route(web::delete().to(|| HttpResponse::Ok().body(\"delete!\"))),\n            ),\n        )\n        .await;\n\n        let put_req = TestRequest::put()\n            .uri(\"/index.html\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .to_request();\n\n        let result = call_and_read_body(&app, put_req).await;\n        assert_eq!(result, Bytes::from_static(b\"put!\"));\n\n        let patch_req = TestRequest::patch()\n            .uri(\"/index.html\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .to_request();\n\n        let result = call_and_read_body(&app, patch_req).await;\n        assert_eq!(result, Bytes::from_static(b\"patch!\"));\n\n        let delete_req = TestRequest::delete().uri(\"/index.html\").to_request();\n        let result = call_and_read_body(&app, delete_req).await;\n        assert_eq!(result, Bytes::from_static(b\"delete!\"));\n    }\n\n    #[derive(Serialize, Deserialize, Debug)]\n    pub struct Person {\n        id: String,\n        name: String,\n    }\n\n    #[actix_rt::test]\n    async fn test_response_json() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        let payload = r#\"{\"id\":\"12345\",\"name\":\"User name\"}\"#.as_bytes();\n\n        let req = TestRequest::post()\n            .uri(\"/people\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .set_payload(payload)\n            .to_request();\n\n        let result: Person = call_and_read_body_json(&app, req).await;\n        assert_eq!(&result.id, \"12345\");\n    }\n\n    #[actix_rt::test]\n    async fn test_try_response_json_error() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        let payload = r#\"{\"id\":\"12345\",\"name\":\"User name\"}\"#.as_bytes();\n\n        let req = TestRequest::post()\n            .uri(\"/animals\") // Not registered to ensure an error occurs.\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .set_payload(payload)\n            .to_request();\n\n        let result: Result<Person, Box<dyn StdError>> =\n            try_call_and_read_body_json(&app, req).await;\n        assert!(result.is_err());\n    }\n\n    #[actix_rt::test]\n    async fn test_body_json() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        let payload = r#\"{\"id\":\"12345\",\"name\":\"User name\"}\"#.as_bytes();\n\n        let res = TestRequest::post()\n            .uri(\"/people\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .set_payload(payload)\n            .send_request(&app)\n            .await;\n\n        let result: Person = read_body_json(res).await;\n        assert_eq!(&result.name, \"User name\");\n    }\n\n    #[actix_rt::test]\n    async fn test_try_body_json_error() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        // Use a number for id to cause a deserialization error.\n        let payload = r#\"{\"id\":12345,\"name\":\"User name\"}\"#.as_bytes();\n\n        let res = TestRequest::post()\n            .uri(\"/people\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .set_payload(payload)\n            .send_request(&app)\n            .await;\n\n        let result: Result<Person, Box<dyn StdError>> = try_read_body_json(res).await;\n        assert!(result.is_err());\n    }\n\n    #[actix_rt::test]\n    async fn test_request_response_form() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        let payload = Person {\n            id: \"12345\".to_string(),\n            name: \"User name\".to_string(),\n        };\n\n        let req = TestRequest::post()\n            .uri(\"/people\")\n            .set_form(&payload)\n            .to_request();\n\n        assert_eq!(req.content_type(), \"application/x-www-form-urlencoded\");\n\n        let result: Person = call_and_read_body_json(&app, req).await;\n        assert_eq!(&result.id, \"12345\");\n        assert_eq!(&result.name, \"User name\");\n    }\n\n    #[actix_rt::test]\n    async fn test_response() {\n        let app = init_service(\n            App::new().service(\n                web::resource(\"/index.html\")\n                    .route(web::post().to(|| HttpResponse::Ok().body(\"welcome!\"))),\n            ),\n        )\n        .await;\n\n        let req = TestRequest::post()\n            .uri(\"/index.html\")\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .to_request();\n\n        let result = call_and_read_body(&app, req).await;\n        assert_eq!(result, Bytes::from_static(b\"welcome!\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_request_response_json() {\n        let app =\n            init_service(App::new().service(web::resource(\"/people\").route(\n                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n            )))\n            .await;\n\n        let payload = Person {\n            id: \"12345\".to_string(),\n            name: \"User name\".to_string(),\n        };\n\n        let req = TestRequest::post()\n            .uri(\"/people\")\n            .set_json(&payload)\n            .to_request();\n\n        assert_eq!(req.content_type(), \"application/json\");\n\n        let result: Person = call_and_read_body_json(&app, req).await;\n        assert_eq!(&result.id, \"12345\");\n        assert_eq!(&result.name, \"User name\");\n    }\n\n    #[actix_rt::test]\n    #[allow(dead_code)]\n    async fn return_opaque_types() {\n        fn test_app() -> App<\n            impl ServiceFactory<\n                ServiceRequest,\n                Config = (),\n                Response = ServiceResponse<impl MessageBody>,\n                Error = crate::Error,\n                InitError = (),\n            >,\n        > {\n            App::new().service(\n                web::resource(\"/people\").route(\n                    web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),\n                ),\n            )\n        }\n\n        async fn test_service(\n        ) -> impl Service<Request, Response = ServiceResponse<impl MessageBody>, Error = crate::Error>\n        {\n            init_service(test_app()).await\n        }\n\n        async fn compile_test(mut req: Vec<Request>) {\n            let svc = test_service().await;\n            call_service(&svc, req.pop().unwrap()).await;\n            call_and_read_body(&svc, req.pop().unwrap()).await;\n            read_body(call_service(&svc, req.pop().unwrap()).await).await;\n            let _: String = call_and_read_body_json(&svc, req.pop().unwrap()).await;\n            let _: String = read_body_json(call_service(&svc, req.pop().unwrap()).await).await;\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/thin_data.rs",
    "content": "use std::any::type_name;\n\nuse actix_utils::future::{ready, Ready};\n\nuse crate::{dev::Payload, error, FromRequest, HttpRequest};\n\n/// Application data wrapper and extractor for cheaply-cloned types.\n///\n/// Similar to the [`Data`] wrapper but for `Clone`/`Copy` types that are already an `Arc` internally,\n/// share state using some other means when cloned, or is otherwise static data that is very cheap\n/// to clone.\n///\n/// Unlike `Data`, this wrapper clones `T` during extraction. Therefore, it is the user's\n/// responsibility to ensure that clones of `T` do actually share the same state, otherwise state\n/// may be unexpectedly different across multiple requests.\n///\n/// Note that if your type is literally an `Arc<T>` then it's recommended to use the\n/// [`Data::from(arc)`][data_from_arc] conversion instead.\n///\n/// # Examples\n///\n/// ```\n/// use actix_web::{\n///     web::{self, ThinData},\n///     App, HttpResponse, Responder,\n/// };\n///\n/// // Use the `ThinData<T>` extractor to access a database connection pool.\n/// async fn index(ThinData(db_pool): ThinData<DbPool>) -> impl Responder {\n///     // database action ...\n///\n///     HttpResponse::Ok()\n/// }\n///\n/// # type DbPool = ();\n/// let db_pool = DbPool::default();\n///\n/// App::new()\n///     .app_data(ThinData(db_pool.clone()))\n///     .service(web::resource(\"/\").get(index))\n/// # ;\n/// ```\n///\n/// [`Data`]: crate::web::Data\n/// [data_from_arc]: crate::web::Data#impl-From<Arc<T>>-for-Data<T>\n#[derive(Debug, Clone)]\npub struct ThinData<T>(pub T);\n\nimpl_more::impl_as_ref!(ThinData<T> => T);\nimpl_more::impl_as_mut!(ThinData<T> => T);\nimpl_more::impl_deref_and_mut!(<T> in ThinData<T> => T);\n\nimpl<T: Clone + 'static> FromRequest for ThinData<T> {\n    type Error = crate::Error;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        ready(req.app_data::<Self>().cloned().ok_or_else(|| {\n            log::debug!(\n                \"Failed to extract `ThinData<{}>` for `{}` handler. For the ThinData extractor to work \\\n                correctly, wrap the data with `ThinData()` and pass it to `App::app_data()`. \\\n                Ensure that types align in both the set and retrieve calls.\",\n                type_name::<T>(),\n                req.match_name().unwrap_or(req.path())\n            );\n\n            error::ErrorInternalServerError(\n                \"Requested application data is not configured correctly. \\\n                View/enable debug logs for more details.\",\n            )\n        }))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::{Arc, Mutex};\n\n    use super::*;\n    use crate::{\n        http::StatusCode,\n        test::{call_service, init_service, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    type TestT = Arc<Mutex<u32>>;\n\n    #[actix_rt::test]\n    async fn thin_data() {\n        let test_data = TestT::default();\n\n        let app = init_service(App::new().app_data(ThinData(test_data.clone())).service(\n            web::resource(\"/\").to(|td: ThinData<TestT>| {\n                *td.lock().unwrap() += 1;\n                HttpResponse::Ok()\n            }),\n        ))\n        .await;\n\n        for _ in 0..3 {\n            let req = TestRequest::default().to_request();\n            let resp = call_service(&app, req).await;\n            assert_eq!(resp.status(), StatusCode::OK);\n        }\n\n        assert_eq!(*test_data.lock().unwrap(), 3);\n    }\n\n    #[actix_rt::test]\n    async fn thin_data_missing() {\n        let app = init_service(\n            App::new().service(web::resource(\"/\").to(|_: ThinData<u32>| HttpResponse::Ok())),\n        )\n        .await;\n\n        let req = TestRequest::default().to_request();\n        let resp = call_service(&app, req).await;\n        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/either.rs",
    "content": "//! For either helper, see [`Either`].\n\nuse std::{\n    future::Future,\n    mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse bytes::Bytes;\nuse futures_core::ready;\nuse pin_project_lite::pin_project;\n\nuse crate::{\n    body::EitherBody,\n    dev,\n    web::{Form, Json},\n    Error, FromRequest, HttpRequest, HttpResponse, Responder,\n};\n\n/// Combines two extractor or responder types into a single type.\n///\n/// # Extractor\n/// Provides a mechanism for trying two extractors, a primary and a fallback. Useful for\n/// \"polymorphic payloads\" where, for example, a form might be JSON or URL encoded.\n///\n/// It is important to note that this extractor, by necessity, buffers the entire request payload\n/// as part of its implementation. Though, it does respect any `PayloadConfig` maximum size limits.\n///\n/// ```\n/// use actix_web::{post, web, Either};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     name: String,\n/// }\n///\n/// // handler that accepts form as JSON or form-urlencoded.\n/// #[post(\"/\")]\n/// async fn index(form: Either<web::Json<Info>, web::Form<Info>>) -> String {\n///     let name: String = match form {\n///         Either::Left(json) => json.name.to_owned(),\n///         Either::Right(form) => form.name.to_owned(),\n///     };\n///\n///     format!(\"Welcome {}!\", name)\n/// }\n/// ```\n///\n/// # Responder\n/// It may be desirable to use a concrete type for a response with multiple branches. As long as\n/// both types implement `Responder`, so will the `Either` type, enabling it to be used as a\n/// handler's return type.\n///\n/// All properties of a response are determined by the Responder branch returned.\n///\n/// ```\n/// use actix_web::{get, Either, Error, HttpResponse};\n///\n/// #[get(\"/\")]\n/// async fn index() -> Either<&'static str, Result<HttpResponse, Error>> {\n///     if 1 == 2 {\n///         // respond with Left variant\n///         Either::Left(\"Bad data\")\n///     } else {\n///         // respond with Right variant\n///         Either::Right(\n///             Ok(HttpResponse::Ok()\n///                 .content_type(mime::TEXT_HTML)\n///                 .body(\"<p>Hello!</p>\"))\n///         )\n///     }\n/// }\n/// ```\n#[derive(Debug, PartialEq, Eq)]\npub enum Either<L, R> {\n    /// A value of type `L`.\n    Left(L),\n\n    /// A value of type `R`.\n    Right(R),\n}\n\nimpl<T> Either<Form<T>, Json<T>> {\n    pub fn into_inner(self) -> T {\n        match self {\n            Either::Left(form) => form.into_inner(),\n            Either::Right(form) => form.into_inner(),\n        }\n    }\n}\n\nimpl<T> Either<Json<T>, Form<T>> {\n    pub fn into_inner(self) -> T {\n        match self {\n            Either::Left(form) => form.into_inner(),\n            Either::Right(form) => form.into_inner(),\n        }\n    }\n}\n\n#[cfg(test)]\nimpl<L, R> Either<L, R> {\n    pub(self) fn unwrap_left(self) -> L {\n        match self {\n            Either::Left(data) => data,\n            Either::Right(_) => {\n                panic!(\"Cannot unwrap Left branch. Either contains an `R` type.\")\n            }\n        }\n    }\n\n    pub(self) fn unwrap_right(self) -> R {\n        match self {\n            Either::Left(_) => {\n                panic!(\"Cannot unwrap Right branch. Either contains an `L` type.\")\n            }\n            Either::Right(data) => data,\n        }\n    }\n}\n\n/// See [here](#responder) for example of usage as a handler return type.\nimpl<L, R> Responder for Either<L, R>\nwhere\n    L: Responder,\n    R: Responder,\n{\n    type Body = EitherBody<L::Body, R::Body>;\n\n    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {\n        match self {\n            Either::Left(a) => a.respond_to(req).map_into_left_body(),\n            Either::Right(b) => b.respond_to(req).map_into_right_body(),\n        }\n    }\n}\n\n/// A composite error resulting from failure to extract an `Either<L, R>`.\n///\n/// The implementation of `Into<actix_web::Error>` will return the payload buffering error or the\n/// error from the primary extractor. To access the fallback error, use a match clause.\n#[derive(Debug)]\npub enum EitherExtractError<L, R> {\n    /// Error from payload buffering, such as exceeding payload max size limit.\n    Bytes(Error),\n\n    /// Error from primary and fallback extractors.\n    Extract(L, R),\n}\n\nimpl<L, R> From<EitherExtractError<L, R>> for Error\nwhere\n    L: Into<Error>,\n    R: Into<Error>,\n{\n    fn from(err: EitherExtractError<L, R>) -> Error {\n        match err {\n            EitherExtractError::Bytes(err) => err,\n            EitherExtractError::Extract(a_err, _b_err) => a_err.into(),\n        }\n    }\n}\n\n/// See [here](#extractor) for example of usage as an extractor.\nimpl<L, R> FromRequest for Either<L, R>\nwhere\n    L: FromRequest + 'static,\n    R: FromRequest + 'static,\n{\n    type Error = EitherExtractError<L::Error, R::Error>;\n    type Future = EitherExtractFut<L, R>;\n\n    fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n        EitherExtractFut {\n            req: req.clone(),\n            state: EitherExtractState::Bytes {\n                bytes: Bytes::from_request(req, payload),\n            },\n        }\n    }\n}\n\npin_project! {\n    pub struct EitherExtractFut<L, R>\n    where\n        R: FromRequest,\n        L: FromRequest,\n    {\n        req: HttpRequest,\n        #[pin]\n        state: EitherExtractState<L, R>,\n    }\n}\n\npin_project! {\n    #[project = EitherExtractProj]\n    pub enum EitherExtractState<L, R>\n    where\n        L: FromRequest,\n        R: FromRequest,\n    {\n        Bytes {\n            #[pin]\n            bytes: <Bytes as FromRequest>::Future,\n        },\n        Left {\n            #[pin]\n            left: L::Future,\n            fallback: Bytes,\n        },\n        Right {\n            #[pin]\n            right: R::Future,\n            left_err: Option<L::Error>,\n        },\n    }\n}\n\nimpl<R, RF, RE, L, LF, LE> Future for EitherExtractFut<L, R>\nwhere\n    L: FromRequest<Future = LF, Error = LE>,\n    R: FromRequest<Future = RF, Error = RE>,\n    LF: Future<Output = Result<L, LE>> + 'static,\n    RF: Future<Output = Result<R, RE>> + 'static,\n    LE: Into<Error>,\n    RE: Into<Error>,\n{\n    type Output = Result<Either<L, R>, EitherExtractError<LE, RE>>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let mut this = self.project();\n        let ready = loop {\n            let next = match this.state.as_mut().project() {\n                EitherExtractProj::Bytes { bytes } => {\n                    let res = ready!(bytes.poll(cx));\n                    match res {\n                        Ok(bytes) => {\n                            let fallback = bytes.clone();\n                            let left = L::from_request(this.req, &mut dev::Payload::from(bytes));\n                            EitherExtractState::Left { left, fallback }\n                        }\n                        Err(err) => break Err(EitherExtractError::Bytes(err)),\n                    }\n                }\n                EitherExtractProj::Left { left, fallback } => {\n                    let res = ready!(left.poll(cx));\n                    match res {\n                        Ok(extracted) => break Ok(Either::Left(extracted)),\n                        Err(left_err) => {\n                            let right = R::from_request(\n                                this.req,\n                                &mut dev::Payload::from(mem::take(fallback)),\n                            );\n                            EitherExtractState::Right {\n                                left_err: Some(left_err),\n                                right,\n                            }\n                        }\n                    }\n                }\n                EitherExtractProj::Right { right, left_err } => {\n                    let res = ready!(right.poll(cx));\n                    match res {\n                        Ok(data) => break Ok(Either::Right(data)),\n                        Err(err) => {\n                            break Err(EitherExtractError::Extract(left_err.take().unwrap(), err));\n                        }\n                    }\n                }\n            };\n            this.state.set(next);\n        };\n        Poll::Ready(ready)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use serde::{Deserialize, Serialize};\n\n    use super::*;\n    use crate::test::TestRequest;\n\n    #[derive(Debug, Clone, Serialize, Deserialize)]\n    struct TestForm {\n        hello: String,\n    }\n\n    #[actix_rt::test]\n    async fn test_either_extract_first_try() {\n        let (req, mut pl) = TestRequest::default()\n            .set_form(TestForm {\n                hello: \"world\".to_owned(),\n            })\n            .to_http_parts();\n\n        let form = Either::<Form<TestForm>, Json<TestForm>>::from_request(&req, &mut pl)\n            .await\n            .unwrap()\n            .unwrap_left()\n            .into_inner();\n        assert_eq!(&form.hello, \"world\");\n    }\n\n    #[actix_rt::test]\n    async fn test_either_extract_fallback() {\n        let (req, mut pl) = TestRequest::default()\n            .set_json(TestForm {\n                hello: \"world\".to_owned(),\n            })\n            .to_http_parts();\n\n        let form = Either::<Form<TestForm>, Json<TestForm>>::from_request(&req, &mut pl)\n            .await\n            .unwrap()\n            .unwrap_right()\n            .into_inner();\n        assert_eq!(&form.hello, \"world\");\n    }\n\n    #[actix_rt::test]\n    async fn test_either_extract_recursive_fallback() {\n        let (req, mut pl) = TestRequest::default()\n            .set_payload(Bytes::from_static(b\"!@$%^&*()\"))\n            .to_http_parts();\n\n        let payload =\n            Either::<Either<Form<TestForm>, Json<TestForm>>, Bytes>::from_request(&req, &mut pl)\n                .await\n                .unwrap()\n                .unwrap_right();\n        assert_eq!(&payload.as_ref(), &b\"!@$%^&*()\");\n    }\n\n    #[actix_rt::test]\n    async fn test_either_extract_recursive_fallback_inner() {\n        let (req, mut pl) = TestRequest::default()\n            .set_json(TestForm {\n                hello: \"world\".to_owned(),\n            })\n            .to_http_parts();\n\n        let form =\n            Either::<Either<Form<TestForm>, Json<TestForm>>, Bytes>::from_request(&req, &mut pl)\n                .await\n                .unwrap()\n                .unwrap_left()\n                .unwrap_right()\n                .into_inner();\n        assert_eq!(&form.hello, \"world\");\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/form.rs",
    "content": "//! For URL encoded form helper documentation, see [`Form`].\n\nuse std::{\n    borrow::Cow,\n    fmt,\n    future::Future,\n    ops,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_http::Payload;\nuse bytes::BytesMut;\nuse encoding_rs::{Encoding, UTF_8};\nuse futures_core::{future::LocalBoxFuture, ready};\nuse futures_util::{FutureExt as _, StreamExt as _};\nuse serde::{de::DeserializeOwned, Serialize};\n\n#[cfg(feature = \"__compress\")]\nuse crate::dev::Decompress;\nuse crate::{\n    body::EitherBody, error::UrlencodedError, extract::FromRequest, http::header::CONTENT_LENGTH,\n    web, Error, HttpMessage, HttpRequest, HttpResponse, Responder,\n};\n\n/// URL encoded payload extractor and responder.\n///\n/// `Form` has two uses: URL encoded responses, and extracting typed data from URL request payloads.\n///\n/// # Extractor\n/// To extract typed data from a request body, the inner type `T` must implement the\n/// [`DeserializeOwned`] trait.\n///\n/// Use [`FormConfig`] to configure extraction options.\n///\n/// ## Examples\n/// ```\n/// use actix_web::{post, web};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     name: String,\n/// }\n///\n/// // This handler is only called if:\n/// // - request headers declare the content type as `application/x-www-form-urlencoded`\n/// // - request payload deserializes into an `Info` struct from the URL encoded format\n/// #[post(\"/\")]\n/// async fn index(web::Form(form): web::Form<Info>) -> String {\n///     format!(\"Welcome {}!\", form.name)\n/// }\n/// ```\n///\n/// # Responder\n/// The `Form` type also allows you to create URL encoded responses by returning a value of type\n/// `Form<T>` where `T` is the type to be URL encoded, as long as `T` implements [`Serialize`].\n///\n/// ## Examples\n/// ```\n/// use actix_web::{get, web};\n/// use serde::Serialize;\n///\n/// #[derive(Serialize)]\n/// struct SomeForm {\n///     name: String,\n///     age: u8\n/// }\n///\n/// // Response will have:\n/// // - status: 200 OK\n/// // - header: `Content-Type: application/x-www-form-urlencoded`\n/// // - body: `name=actix&age=123`\n/// #[get(\"/\")]\n/// async fn index() -> web::Form<SomeForm> {\n///     web::Form(SomeForm {\n///         name: \"actix\".to_owned(),\n///         age: 123\n///     })\n/// }\n/// ```\n///\n/// # Panics\n/// URL encoded forms consist of unordered `key=value` pairs, therefore they cannot be decoded into\n/// any type which depends upon data ordering (eg. tuples). Trying to do so will result in a panic.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]\npub struct Form<T>(pub T);\n\nimpl<T> Form<T> {\n    /// Unwrap into inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T> ops::Deref for Form<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T> ops::DerefMut for Form<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n\nimpl<T> Serialize for Form<T>\nwhere\n    T: Serialize,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.0.serialize(serializer)\n    }\n}\n\n/// See [here](#extractor) for example of usage as an extractor.\nimpl<T> FromRequest for Form<T>\nwhere\n    T: DeserializeOwned + 'static,\n{\n    type Error = Error;\n    type Future = FormExtractFut<T>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n        let FormConfig { limit, err_handler } = FormConfig::from_req(req).clone();\n\n        FormExtractFut {\n            fut: UrlEncoded::new(req, payload).limit(limit),\n            req: req.clone(),\n            err_handler,\n        }\n    }\n}\n\ntype FormErrHandler = Option<Rc<dyn Fn(UrlencodedError, &HttpRequest) -> Error>>;\n\npub struct FormExtractFut<T> {\n    fut: UrlEncoded<T>,\n    err_handler: FormErrHandler,\n    req: HttpRequest,\n}\n\nimpl<T> Future for FormExtractFut<T>\nwhere\n    T: DeserializeOwned + 'static,\n{\n    type Output = Result<Form<T>, Error>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        let res = ready!(Pin::new(&mut this.fut).poll(cx));\n\n        let res = match res {\n            Err(err) => match &this.err_handler {\n                Some(err_handler) => Err((err_handler)(err, &this.req)),\n                None => Err(err.into()),\n            },\n            Ok(item) => Ok(Form(item)),\n        };\n\n        Poll::Ready(res)\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for Form<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n/// See [here](#responder) for example of usage as a handler return type.\nimpl<T: Serialize> Responder for Form<T> {\n    type Body = EitherBody<String>;\n\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        match serde_urlencoded::to_string(&self.0) {\n            Ok(body) => match HttpResponse::Ok()\n                .content_type(mime::APPLICATION_WWW_FORM_URLENCODED)\n                .message_body(body)\n            {\n                Ok(res) => res.map_into_left_body(),\n                Err(err) => HttpResponse::from_error(err).map_into_right_body(),\n            },\n\n            Err(err) => {\n                HttpResponse::from_error(UrlencodedError::Serialize(err)).map_into_right_body()\n            }\n        }\n    }\n}\n\n/// [`Form`] extractor configuration.\n///\n/// ```\n/// use actix_web::{post, web, App, FromRequest, Result};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     username: String,\n/// }\n///\n/// // Custom `FormConfig` is applied to App.\n/// // Max payload size for URL encoded forms is set to 4kB.\n/// #[post(\"/\")]\n/// async fn index(form: web::Form<Info>) -> Result<String> {\n///     Ok(format!(\"Welcome {}!\", form.username))\n/// }\n///\n/// App::new()\n///     .app_data(web::FormConfig::default().limit(4096))\n///     .service(index);\n/// ```\n#[derive(Clone)]\npub struct FormConfig {\n    limit: usize,\n    err_handler: FormErrHandler,\n}\n\nimpl FormConfig {\n    /// Set maximum accepted payload size. By default this limit is 16kB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        self.limit = limit;\n        self\n    }\n\n    /// Set custom error handler\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(UrlencodedError, &HttpRequest) -> Error + 'static,\n    {\n        self.err_handler = Some(Rc::new(f));\n        self\n    }\n\n    /// Extract payload config from app data.\n    ///\n    /// Checks both `T` and `Data<T>`, in that order, and falls back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n}\n\n/// Allow shared refs used as default.\nconst DEFAULT_CONFIG: FormConfig = FormConfig {\n    limit: 16_384, // 2^14 bytes (~16kB)\n    err_handler: None,\n};\n\nimpl Default for FormConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n/// Future that resolves to some `T` when parsed from a URL encoded payload.\n///\n/// Form can be deserialized from any type `T` that implements [`serde::Deserialize`].\n///\n/// Returns error if:\n/// - content type is not `application/x-www-form-urlencoded`\n/// - content length is greater than [limit](UrlEncoded::limit())\npub struct UrlEncoded<T> {\n    #[cfg(feature = \"__compress\")]\n    stream: Option<Decompress<Payload>>,\n    #[cfg(not(feature = \"__compress\"))]\n    stream: Option<Payload>,\n\n    limit: usize,\n    length: Option<usize>,\n    encoding: &'static Encoding,\n    err: Option<UrlencodedError>,\n    fut: Option<LocalBoxFuture<'static, Result<T, UrlencodedError>>>,\n}\n\n#[allow(clippy::borrow_interior_mutable_const)]\nimpl<T> UrlEncoded<T> {\n    /// Create a new future to decode a URL encoded request payload.\n    pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self {\n        // check content type\n        if req.content_type().to_lowercase() != \"application/x-www-form-urlencoded\" {\n            return Self::err(UrlencodedError::ContentType);\n        }\n        let encoding = match req.encoding() {\n            Ok(enc) => enc,\n            Err(_) => return Self::err(UrlencodedError::ContentType),\n        };\n\n        let mut len = None;\n        if let Some(l) = req.headers().get(&CONTENT_LENGTH) {\n            if let Ok(s) = l.to_str() {\n                if let Ok(l) = s.parse::<usize>() {\n                    len = Some(l)\n                } else {\n                    return Self::err(UrlencodedError::UnknownLength);\n                }\n            } else {\n                return Self::err(UrlencodedError::UnknownLength);\n            }\n        };\n\n        let payload = {\n            cfg_if::cfg_if! {\n                if #[cfg(feature = \"__compress\")] {\n                    Decompress::from_headers(payload.take(), req.headers())\n                } else {\n                    payload.take()\n                }\n            }\n        };\n\n        UrlEncoded {\n            encoding,\n            stream: Some(payload),\n            limit: 32_768,\n            length: len,\n            fut: None,\n            err: None,\n        }\n    }\n\n    fn err(err: UrlencodedError) -> Self {\n        UrlEncoded {\n            stream: None,\n            limit: 32_768,\n            fut: None,\n            err: Some(err),\n            length: None,\n            encoding: UTF_8,\n        }\n    }\n\n    /// Set maximum accepted payload size. The default limit is 256kB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        self.limit = limit;\n        self\n    }\n}\n\nimpl<T> Future for UrlEncoded<T>\nwhere\n    T: DeserializeOwned + 'static,\n{\n    type Output = Result<T, UrlencodedError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        if let Some(ref mut fut) = self.fut {\n            return Pin::new(fut).poll(cx);\n        }\n\n        if let Some(err) = self.err.take() {\n            return Poll::Ready(Err(err));\n        }\n\n        // payload size\n        let limit = self.limit;\n        if let Some(len) = self.length.take() {\n            if len > limit {\n                return Poll::Ready(Err(UrlencodedError::Overflow { size: len, limit }));\n            }\n        }\n\n        // future\n        let encoding = self.encoding;\n        let mut stream = self.stream.take().unwrap();\n\n        self.fut = Some(\n            async move {\n                let mut body = BytesMut::with_capacity(8192);\n\n                while let Some(item) = stream.next().await {\n                    let chunk = item?;\n\n                    if (body.len() + chunk.len()) > limit {\n                        return Err(UrlencodedError::Overflow {\n                            size: body.len() + chunk.len(),\n                            limit,\n                        });\n                    } else {\n                        body.extend_from_slice(&chunk);\n                    }\n                }\n\n                if encoding == UTF_8 {\n                    serde_urlencoded::from_bytes::<T>(&body).map_err(UrlencodedError::Parse)\n                } else {\n                    let body = encoding\n                        .decode_without_bom_handling_and_without_replacement(&body)\n                        .map(Cow::into_owned)\n                        .ok_or(UrlencodedError::Encoding)?;\n\n                    serde_urlencoded::from_str::<T>(&body).map_err(UrlencodedError::Parse)\n                }\n            }\n            .boxed_local(),\n        );\n\n        self.poll(cx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::Bytes;\n    use serde::{Deserialize, Serialize};\n\n    use super::*;\n    use crate::{\n        http::{\n            header::{HeaderValue, CONTENT_TYPE},\n            StatusCode,\n        },\n        test::{assert_body_eq, TestRequest},\n    };\n\n    #[derive(Deserialize, Serialize, Debug, PartialEq)]\n    struct Info {\n        hello: String,\n        counter: i64,\n    }\n\n    #[actix_rt::test]\n    async fn test_form() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((CONTENT_LENGTH, 11))\n            .set_payload(Bytes::from_static(b\"hello=world&counter=123\"))\n            .to_http_parts();\n\n        let Form(s) = Form::<Info>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(\n            s,\n            Info {\n                hello: \"world\".into(),\n                counter: 123\n            }\n        );\n    }\n\n    fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {\n        match err {\n            UrlencodedError::Overflow { .. } => {\n                matches!(other, UrlencodedError::Overflow { .. })\n            }\n            UrlencodedError::UnknownLength => matches!(other, UrlencodedError::UnknownLength),\n            UrlencodedError::ContentType => matches!(other, UrlencodedError::ContentType),\n            _ => false,\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_urlencoded_error() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((CONTENT_LENGTH, \"xxxx\"))\n            .to_http_parts();\n        let info = UrlEncoded::<Info>::new(&req, &mut pl).await;\n        assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((CONTENT_LENGTH, \"1000000\"))\n            .to_http_parts();\n        let info = UrlEncoded::<Info>::new(&req, &mut pl).await;\n        assert!(eq(\n            info.err().unwrap(),\n            UrlencodedError::Overflow { size: 0, limit: 0 }\n        ));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, \"text/plain\"))\n            .insert_header((CONTENT_LENGTH, 10))\n            .to_http_parts();\n        let info = UrlEncoded::<Info>::new(&req, &mut pl).await;\n        assert!(eq(info.err().unwrap(), UrlencodedError::ContentType));\n    }\n\n    #[actix_rt::test]\n    async fn test_urlencoded() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .insert_header((CONTENT_LENGTH, 11))\n            .set_payload(Bytes::from_static(b\"hello=world&counter=123\"))\n            .to_http_parts();\n\n        let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();\n        assert_eq!(\n            info,\n            Info {\n                hello: \"world\".to_owned(),\n                counter: 123\n            }\n        );\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                CONTENT_TYPE,\n                \"application/x-www-form-urlencoded; charset=utf-8\",\n            ))\n            .insert_header((CONTENT_LENGTH, 11))\n            .set_payload(Bytes::from_static(b\"hello=world&counter=123\"))\n            .to_http_parts();\n\n        let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();\n        assert_eq!(\n            info,\n            Info {\n                hello: \"world\".to_owned(),\n                counter: 123\n            }\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_responder() {\n        let req = TestRequest::default().to_http_request();\n\n        let form = Form(Info {\n            hello: \"world\".to_string(),\n            counter: 123,\n        });\n        let res = form.respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(CONTENT_TYPE).unwrap(),\n            HeaderValue::from_static(\"application/x-www-form-urlencoded\")\n        );\n        assert_body_eq!(res, b\"hello=world&counter=123\");\n    }\n\n    #[actix_rt::test]\n    async fn test_with_config_in_data_wrapper() {\n        let ctype = HeaderValue::from_static(\"application/x-www-form-urlencoded\");\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, ctype))\n            .insert_header((CONTENT_LENGTH, HeaderValue::from_static(\"20\")))\n            .set_payload(Bytes::from_static(b\"hello=test&counter=4\"))\n            .app_data(web::Data::new(FormConfig::default().limit(10)))\n            .to_http_parts();\n\n        let s = Form::<Info>::from_request(&req, &mut pl).await;\n        assert!(s.is_err());\n\n        let err_str = s.err().unwrap().to_string();\n        assert!(err_str.starts_with(\"URL encoded payload is larger\"));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/header.rs",
    "content": "//! For header extractor helper documentation, see [`Header`](crate::types::Header).\n\nuse std::{fmt, ops};\n\nuse actix_utils::future::{ready, Ready};\n\nuse crate::{\n    dev::Payload, error::ParseError, extract::FromRequest, http::header::Header as ParseHeader,\n    HttpRequest,\n};\n\n/// Extract typed headers from the request.\n///\n/// To extract a header, the inner type `T` must implement the\n/// [`Header`](crate::http::header::Header) trait.\n///\n/// # Examples\n/// ```\n/// use actix_web::{get, web, http::header};\n///\n/// #[get(\"/\")]\n/// async fn index(date: web::Header<header::Date>) -> String {\n///     format!(\"Request was sent at {}\", date.to_string())\n/// }\n/// ```\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]\npub struct Header<T>(pub T);\n\nimpl<T> Header<T> {\n    /// Unwrap into the inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T> ops::Deref for Header<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T> ops::DerefMut for Header<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n\nimpl<T> fmt::Display for Header<T>\nwhere\n    T: fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.0, f)\n    }\n}\n\nimpl<T> FromRequest for Header<T>\nwhere\n    T: ParseHeader,\n{\n    type Error = ParseError;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        match ParseHeader::parse(req) {\n            Ok(header) => ready(Ok(Header(header))),\n            Err(err) => ready(Err(err)),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        http::{header, Method},\n        test::TestRequest,\n    };\n\n    #[actix_rt::test]\n    async fn test_header_extract() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))\n            .insert_header((header::ALLOW, header::Allow(vec![Method::GET])))\n            .to_http_parts();\n\n        let s = Header::<header::ContentType>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(s.into_inner().0, mime::APPLICATION_JSON);\n\n        let s = Header::<header::Allow>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(s.into_inner().0, vec![Method::GET]);\n\n        assert!(Header::<header::Date>::from_request(&req, &mut pl)\n            .await\n            .is_err());\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/html.rs",
    "content": "//! Semantic HTML responder. See [`Html`].\n\nuse crate::{\n    http::{\n        header::{self, ContentType, TryIntoHeaderValue},\n        StatusCode,\n    },\n    HttpRequest, HttpResponse, Responder,\n};\n\n/// Semantic HTML responder.\n///\n/// When used as a responder, creates a 200 OK response, sets the correct HTML content type, and\n/// uses the string passed to [`Html::new()`] as the body.\n///\n/// ```\n/// # use actix_web::web::Html;\n/// Html::new(\"<p>Hello, World!</p>\")\n/// # ;\n/// ```\n#[derive(Debug, Clone, PartialEq, Hash)]\npub struct Html(String);\n\nimpl Html {\n    /// Constructs a new `Html` responder.\n    pub fn new(html: impl Into<String>) -> Self {\n        Self(html.into())\n    }\n}\n\nimpl Responder for Html {\n    type Body = String;\n\n    fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {\n        let mut res = HttpResponse::with_body(StatusCode::OK, self.0);\n        res.headers_mut().insert(\n            header::CONTENT_TYPE,\n            ContentType::html().try_into_value().unwrap(),\n        );\n        res\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test::TestRequest;\n\n    #[test]\n    fn responder() {\n        let req = TestRequest::default().to_http_request();\n\n        let res = Html::new(\"<p>Hello, World!</p>\");\n        let res = res.respond_to(&req);\n\n        assert!(res.status().is_success());\n        assert!(res\n            .headers()\n            .get(header::CONTENT_TYPE)\n            .unwrap()\n            .to_str()\n            .unwrap()\n            .starts_with(\"text/html\"));\n        assert!(res.body().starts_with(\"<p>\"));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/json.rs",
    "content": "//! For JSON helper documentation, see [`Json`].\n\nuse std::{\n    fmt,\n    future::Future,\n    marker::PhantomData,\n    ops,\n    pin::Pin,\n    sync::Arc,\n    task::{Context, Poll},\n};\n\nuse actix_http::Payload;\nuse bytes::BytesMut;\nuse futures_core::{ready, Stream as _};\nuse serde::{de::DeserializeOwned, Serialize};\n\n#[cfg(feature = \"__compress\")]\nuse crate::dev::Decompress;\nuse crate::{\n    body::EitherBody,\n    error::{Error, JsonPayloadError},\n    extract::FromRequest,\n    http::header::{ContentLength, Header as _},\n    request::HttpRequest,\n    web, HttpMessage, HttpResponse, Responder,\n};\n\n/// JSON extractor and responder.\n///\n/// `Json` has two uses: JSON responses, and extracting typed data from JSON request payloads.\n///\n/// # Extractor\n/// To extract typed data from a request body, the inner type `T` must implement the\n/// [`serde::Deserialize`] trait.\n///\n/// Use [`JsonConfig`] to configure extraction options.\n///\n/// ```\n/// use actix_web::{post, web, App};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     username: String,\n/// }\n///\n/// /// deserialize `Info` from request's body\n/// #[post(\"/\")]\n/// async fn index(info: web::Json<Info>) -> String {\n///     format!(\"Welcome {}!\", info.username)\n/// }\n/// ```\n///\n/// # Responder\n/// The `Json` type  JSON formatted responses. A handler may return a value of type\n/// `Json<T>` where `T` is the type of a structure to serialize into JSON. The type `T` must\n/// implement [`serde::Serialize`].\n///\n/// ```\n/// use actix_web::{post, web, HttpRequest};\n/// use serde::Serialize;\n///\n/// #[derive(Serialize)]\n/// struct Info {\n///     name: String,\n/// }\n///\n/// #[post(\"/{name}\")]\n/// async fn index(req: HttpRequest) -> web::Json<Info> {\n///     web::Json(Info {\n///         name: req.match_info().get(\"name\").unwrap().to_owned(),\n///     })\n/// }\n/// ```\n#[derive(Debug)]\npub struct Json<T>(pub T);\n\nimpl<T> Json<T> {\n    /// Unwrap into inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T> ops::Deref for Json<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T> ops::DerefMut for Json<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for Json<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.0, f)\n    }\n}\n\nimpl<T: Serialize> Serialize for Json<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.0.serialize(serializer)\n    }\n}\n\n/// Creates response with OK status code, correct content type header, and serialized JSON payload.\n///\n/// If serialization failed\nimpl<T: Serialize> Responder for Json<T> {\n    type Body = EitherBody<String>;\n\n    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {\n        match serde_json::to_string(&self.0) {\n            Ok(body) => match HttpResponse::Ok()\n                .content_type(mime::APPLICATION_JSON)\n                .message_body(body)\n            {\n                Ok(res) => res.map_into_left_body(),\n                Err(err) => HttpResponse::from_error(err).map_into_right_body(),\n            },\n\n            Err(err) => {\n                HttpResponse::from_error(JsonPayloadError::Serialize(err)).map_into_right_body()\n            }\n        }\n    }\n}\n\n/// See [here](#extractor) for example of usage as an extractor.\nimpl<T: DeserializeOwned> FromRequest for Json<T> {\n    type Error = Error;\n    type Future = JsonExtractFut<T>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {\n        let config = JsonConfig::from_req(req);\n\n        let limit = config.limit;\n        let ctype_required = config.content_type_required;\n        let ctype_fn = config.content_type.as_deref();\n        let err_handler = config.err_handler.clone();\n\n        JsonExtractFut {\n            req: Some(req.clone()),\n            fut: JsonBody::new(req, payload, ctype_fn, ctype_required).limit(limit),\n            err_handler,\n        }\n    }\n}\n\ntype JsonErrorHandler = Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>;\n\npub struct JsonExtractFut<T> {\n    req: Option<HttpRequest>,\n    fut: JsonBody<T>,\n    err_handler: JsonErrorHandler,\n}\n\nimpl<T: DeserializeOwned> Future for JsonExtractFut<T> {\n    type Output = Result<Json<T>, Error>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        let res = ready!(Pin::new(&mut this.fut).poll(cx));\n\n        let res = match res {\n            Err(err) => {\n                let req = this.req.take().unwrap();\n                log::debug!(\n                    \"Failed to deserialize Json from payload. \\\n                         Request path: {}\",\n                    req.path()\n                );\n\n                if let Some(err_handler) = this.err_handler.as_ref() {\n                    Err((*err_handler)(err, &req))\n                } else {\n                    Err(err.into())\n                }\n            }\n            Ok(data) => Ok(Json(data)),\n        };\n\n        Poll::Ready(res)\n    }\n}\n\n/// `Json` extractor configuration.\n///\n/// # Examples\n/// ```\n/// use actix_web::{error, post, web, App, FromRequest, HttpResponse};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     name: String,\n/// }\n///\n/// // `Json` extraction is bound by custom `JsonConfig` applied to App.\n/// #[post(\"/\")]\n/// async fn index(info: web::Json<Info>) -> String {\n///     format!(\"Welcome {}!\", info.name)\n/// }\n///\n/// // custom `Json` extractor configuration\n/// let json_cfg = web::JsonConfig::default()\n///     // limit request payload size\n///     .limit(4096)\n///     // only accept text/plain content type\n///     .content_type(|mime| mime == mime::TEXT_PLAIN)\n///     // use custom error handler\n///     .error_handler(|err, req| {\n///         error::InternalError::from_response(err, HttpResponse::Conflict().into()).into()\n///     });\n///\n/// App::new()\n///     .app_data(json_cfg)\n///     .service(index);\n/// ```\n#[derive(Clone)]\npub struct JsonConfig {\n    limit: usize,\n    err_handler: JsonErrorHandler,\n    content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,\n    content_type_required: bool,\n}\n\nimpl JsonConfig {\n    /// Set maximum accepted payload size. By default this limit is 2MB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        self.limit = limit;\n        self\n    }\n\n    /// Set custom error handler.\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n\n    /// Set predicate for allowed content types.\n    pub fn content_type<F>(mut self, predicate: F) -> Self\n    where\n        F: Fn(mime::Mime) -> bool + Send + Sync + 'static,\n    {\n        self.content_type = Some(Arc::new(predicate));\n        self\n    }\n\n    /// Sets whether or not the request must have a `Content-Type` header to be parsed.\n    pub fn content_type_required(mut self, content_type_required: bool) -> Self {\n        self.content_type_required = content_type_required;\n        self\n    }\n\n    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n}\n\nconst DEFAULT_LIMIT: usize = 2_097_152; // 2 mb\n\n/// Allow shared refs used as default.\nconst DEFAULT_CONFIG: JsonConfig = JsonConfig {\n    limit: DEFAULT_LIMIT,\n    err_handler: None,\n    content_type: None,\n    content_type_required: true,\n};\n\nimpl Default for JsonConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n/// Future that resolves to some `T` when parsed from a JSON payload.\n///\n/// Can deserialize any type `T` that implements [`Deserialize`][serde::Deserialize].\n///\n/// Returns error if:\n/// - `Content-Type` is not `application/json` when `ctype_required` (passed to [`new`][Self::new])\n///   is `true`.\n/// - `Content-Length` is greater than [limit](JsonBody::limit()).\n/// - The payload, when consumed, is not valid JSON.\npub enum JsonBody<T> {\n    Error(Option<JsonPayloadError>),\n    Body {\n        limit: usize,\n        /// Length as reported by `Content-Length` header, if present.\n        length: Option<usize>,\n        #[cfg(feature = \"__compress\")]\n        payload: Decompress<Payload>,\n        #[cfg(not(feature = \"__compress\"))]\n        payload: Payload,\n        buf: BytesMut,\n        _res: PhantomData<T>,\n    },\n}\n\nimpl<T> Unpin for JsonBody<T> {}\n\nimpl<T: DeserializeOwned> JsonBody<T> {\n    /// Create a new future to decode a JSON request payload.\n    #[allow(clippy::borrow_interior_mutable_const)]\n    pub fn new(\n        req: &HttpRequest,\n        payload: &mut Payload,\n        ctype_fn: Option<&(dyn Fn(mime::Mime) -> bool + Send + Sync)>,\n        ctype_required: bool,\n    ) -> Self {\n        // check content-type\n        let can_parse_json = match (ctype_required, req.mime_type()) {\n            (true, Ok(Some(mime))) => {\n                mime.subtype() == mime::JSON\n                    || mime.suffix() == Some(mime::JSON)\n                    || ctype_fn.is_some_and(|predicate| predicate(mime))\n            }\n\n            // if content-type is expected but not parsable as mime type, bail\n            (true, _) => false,\n\n            // if content-type validation is disabled, assume payload is JSON\n            // even when content-type header is missing or invalid mime type\n            (false, _) => true,\n        };\n\n        if !can_parse_json {\n            return JsonBody::Error(Some(JsonPayloadError::ContentType));\n        }\n\n        let length = ContentLength::parse(req).ok().map(|x| x.0);\n\n        // Notice the content-length is not checked against limit of json config here.\n        // As the internal usage always call JsonBody::limit after JsonBody::new.\n        // And limit check to return an error variant of JsonBody happens there.\n\n        let payload = {\n            cfg_if::cfg_if! {\n                if #[cfg(feature = \"__compress\")] {\n                    Decompress::from_headers(payload.take(), req.headers())\n                } else {\n                    payload.take()\n                }\n            }\n        };\n\n        JsonBody::Body {\n            limit: DEFAULT_LIMIT,\n            length,\n            payload,\n            buf: BytesMut::with_capacity(8192),\n            _res: PhantomData,\n        }\n    }\n\n    /// Set maximum accepted payload size. The default limit is 2MB.\n    pub fn limit(self, limit: usize) -> Self {\n        match self {\n            JsonBody::Body {\n                length,\n                payload,\n                buf,\n                ..\n            } => {\n                if let Some(len) = length {\n                    if len > limit {\n                        return JsonBody::Error(Some(JsonPayloadError::OverflowKnownLength {\n                            length: len,\n                            limit,\n                        }));\n                    }\n                }\n\n                JsonBody::Body {\n                    limit,\n                    length,\n                    payload,\n                    buf,\n                    _res: PhantomData,\n                }\n            }\n            JsonBody::Error(err) => JsonBody::Error(err),\n        }\n    }\n}\n\nimpl<T: DeserializeOwned> Future for JsonBody<T> {\n    type Output = Result<T, JsonPayloadError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        match this {\n            JsonBody::Body {\n                limit,\n                buf,\n                payload,\n                ..\n            } => loop {\n                let res = ready!(Pin::new(&mut *payload).poll_next(cx));\n                match res {\n                    Some(chunk) => {\n                        let chunk = chunk?;\n                        let buf_len = buf.len() + chunk.len();\n                        if buf_len > *limit {\n                            return Poll::Ready(Err(JsonPayloadError::Overflow { limit: *limit }));\n                        } else {\n                            buf.extend_from_slice(&chunk);\n                        }\n                    }\n                    None => {\n                        let json = serde_json::from_slice::<T>(buf)\n                            .map_err(JsonPayloadError::Deserialize)?;\n                        return Poll::Ready(Ok(json));\n                    }\n                }\n            },\n            JsonBody::Error(err) => Poll::Ready(Err(err.take().unwrap())),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::Bytes;\n    use serde::{Deserialize, Serialize};\n\n    use super::*;\n    use crate::{\n        body,\n        error::InternalError,\n        http::{\n            header::{self, CONTENT_LENGTH, CONTENT_TYPE},\n            StatusCode,\n        },\n        test::{assert_body_eq, TestRequest},\n    };\n\n    #[derive(Serialize, Deserialize, PartialEq, Debug)]\n    struct MyObject {\n        name: String,\n    }\n\n    fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {\n        match err {\n            JsonPayloadError::Overflow { .. } => {\n                matches!(other, JsonPayloadError::Overflow { .. })\n            }\n            JsonPayloadError::OverflowKnownLength { .. } => {\n                matches!(other, JsonPayloadError::OverflowKnownLength { .. })\n            }\n            JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),\n            _ => false,\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_responder() {\n        let req = TestRequest::default().to_http_request();\n\n        let j = Json(MyObject {\n            name: \"test\".to_string(),\n        });\n        let res = j.respond_to(&req);\n        assert_eq!(res.status(), StatusCode::OK);\n        assert_eq!(\n            res.headers().get(header::CONTENT_TYPE).unwrap(),\n            header::HeaderValue::from_static(\"application/json\")\n        );\n        assert_body_eq!(res, b\"{\\\"name\\\":\\\"test\\\"}\");\n    }\n\n    #[actix_rt::test]\n    async fn test_custom_error_responder() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().limit(10).error_handler(|err, _| {\n                let msg = MyObject {\n                    name: \"invalid request\".to_string(),\n                };\n                let resp = HttpResponse::BadRequest().body(serde_json::to_string(&msg).unwrap());\n                InternalError::from_response(err, resp).into()\n            }))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        let resp = HttpResponse::from_error(s.unwrap_err());\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let body = body::to_bytes(resp.into_body()).await.unwrap();\n        let msg: MyObject = serde_json::from_slice(&body).unwrap();\n        assert_eq!(msg.name, \"invalid request\");\n    }\n\n    #[actix_rt::test]\n    async fn test_extract() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s.name, \"test\");\n        assert_eq!(\n            s.into_inner(),\n            MyObject {\n                name: \"test\".to_string()\n            }\n        );\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().limit(10))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(format!(\"{}\", s.err().unwrap())\n            .contains(\"JSON payload (16 bytes) is larger than allowed (limit: 10 bytes).\"));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(\n                JsonConfig::default()\n                    .limit(10)\n                    .error_handler(|_, _| JsonPayloadError::ContentType.into()),\n            )\n            .to_http_parts();\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(format!(\"{}\", s.err().unwrap()).contains(\"Content type error\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_json_body() {\n        let (req, mut pl) = TestRequest::default().to_http_parts();\n        let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;\n        assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/text\"),\n            ))\n            .to_http_parts();\n        let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;\n        assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"10000\"),\n            ))\n            .to_http_parts();\n\n        let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true)\n            .limit(100)\n            .await;\n        assert!(json_eq(\n            json.err().unwrap(),\n            JsonPayloadError::OverflowKnownLength {\n                length: 10000,\n                limit: 100\n            }\n        ));\n\n        let (mut req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .set_payload(Bytes::from_static(&[0u8; 1000]))\n            .to_http_parts();\n\n        req.head_mut().headers_mut().remove(header::CONTENT_LENGTH);\n        let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true)\n            .limit(100)\n            .await;\n\n        assert!(json_eq(\n            json.err().unwrap(),\n            JsonPayloadError::Overflow { limit: 100 }\n        ));\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .to_http_parts();\n\n        let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;\n        assert_eq!(\n            json.ok().unwrap(),\n            MyObject {\n                name: \"test\".to_owned()\n            }\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_with_json_and_bad_content_type() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"text/plain\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().limit(4096))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_err())\n    }\n\n    #[actix_rt::test]\n    async fn test_with_json_and_good_custom_content_type() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"text/plain\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().content_type(|mime: mime::Mime| {\n                mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN\n            }))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_ok())\n    }\n\n    #[actix_rt::test]\n    async fn test_with_json_and_bad_custom_content_type() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"text/html\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().content_type(|mime: mime::Mime| {\n                mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN\n            }))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_err())\n    }\n\n    #[actix_rt::test]\n    async fn test_json_with_no_content_type() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().content_type_required(false))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_ok())\n    }\n\n    #[actix_rt::test]\n    async fn test_json_ignoring_content_type() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"invalid/value\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(JsonConfig::default().content_type_required(false))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_ok());\n    }\n\n    #[actix_rt::test]\n    async fn test_with_config_in_data_wrapper() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON))\n            .insert_header((CONTENT_LENGTH, 16))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .app_data(web::Data::new(JsonConfig::default().limit(10)))\n            .to_http_parts();\n\n        let s = Json::<MyObject>::from_request(&req, &mut pl).await;\n        assert!(s.is_err());\n\n        let err_str = s.err().unwrap().to_string();\n        assert!(\n            err_str.contains(\"JSON payload (16 bytes) is larger than allowed (limit: 10 bytes).\")\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/mod.rs",
    "content": "//! Common extractors and responders.\n\nmod either;\nmod form;\nmod header;\nmod html;\nmod json;\nmod path;\nmod payload;\nmod query;\nmod readlines;\n\npub use self::{\n    either::{Either, EitherExtractError},\n    form::{Form, FormConfig, UrlEncoded},\n    header::Header,\n    html::Html,\n    json::{Json, JsonBody, JsonConfig},\n    path::{Path, PathConfig},\n    payload::{Payload, PayloadConfig},\n    query::{Query, QueryConfig},\n    readlines::Readlines,\n};\n"
  },
  {
    "path": "actix-web/src/types/path.rs",
    "content": "//! For path segment extractor documentation, see [`Path`].\n\nuse std::sync::Arc;\n\nuse actix_router::PathDeserializer;\nuse actix_utils::future::{ready, Ready};\nuse derive_more::{AsRef, Deref, DerefMut, Display, From};\nuse serde::de;\n\nuse crate::{\n    dev::Payload,\n    error::{Error, ErrorNotFound, PathError},\n    web::Data,\n    FromRequest, HttpRequest,\n};\n\n/// Extract typed data from request path segments.\n///\n/// Use [`PathConfig`] to configure extraction option.\n///\n/// Unlike, [`HttpRequest::match_info`], this extractor will fully percent-decode dynamic segments,\n/// including `/`, `%`, and `+`.\n///\n/// # Examples\n/// ```\n/// use actix_web::{get, web};\n///\n/// // extract path info from \"/{name}/{count}/index.html\" into tuple\n/// // {name}  - deserialize a String\n/// // {count} - deserialize a u32\n/// #[get(\"/{name}/{count}/index.html\")]\n/// async fn index(path: web::Path<(String, u32)>) -> String {\n///     let (name, count) = path.into_inner();\n///     format!(\"Welcome {}! {}\", name, count)\n/// }\n/// ```\n///\n/// Path segments also can be deserialized into any type that implements [`serde::Deserialize`].\n/// Path segment labels will be matched with struct field names.\n///\n/// ```\n/// use actix_web::{get, web};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     name: String,\n/// }\n///\n/// // extract `Info` from a path using serde\n/// #[get(\"/{name}\")]\n/// async fn index(info: web::Path<Info>) -> String {\n///     format!(\"Welcome {}!\", info.name)\n/// }\n/// ```\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, AsRef, Display, From)]\npub struct Path<T>(T);\n\nimpl<T> Path<T> {\n    /// Unwrap into inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\n/// See [here](#Examples) for example of usage as an extractor.\nimpl<T> FromRequest for Path<T>\nwhere\n    T: de::DeserializeOwned,\n{\n    type Error = Error;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        let error_handler = req\n            .app_data::<PathConfig>()\n            .or_else(|| req.app_data::<Data<PathConfig>>().map(Data::get_ref))\n            .and_then(|c| c.err_handler.clone());\n\n        ready(\n            de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))\n                .map(Path)\n                .map_err(move |err| {\n                    log::debug!(\n                        \"Failed during Path extractor deserialization. \\\n                         Request path: {:?}\",\n                        req.path()\n                    );\n\n                    if let Some(error_handler) = error_handler {\n                        let err = PathError::Deserialize(err);\n                        (error_handler)(err, req)\n                    } else {\n                        ErrorNotFound(err)\n                    }\n                }),\n        )\n    }\n}\n\n/// Path extractor configuration\n///\n/// ```\n/// use actix_web::web::PathConfig;\n/// use actix_web::{error, web, App, FromRequest, HttpResponse};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize, Debug)]\n/// enum Folder {\n///     #[serde(rename = \"inbox\")]\n///     Inbox,\n///\n///     #[serde(rename = \"outbox\")]\n///     Outbox,\n/// }\n///\n/// // deserialize `Info` from request's path\n/// async fn index(folder: web::Path<Folder>) -> String {\n///     format!(\"Selected folder: {:?}!\", folder)\n/// }\n///\n/// let app = App::new().service(\n///     web::resource(\"/messages/{folder}\")\n///         .app_data(PathConfig::default().error_handler(|err, req| {\n///             error::InternalError::from_response(\n///                 err,\n///                 HttpResponse::Conflict().into(),\n///             )\n///             .into()\n///         }))\n///         .route(web::post().to(index)),\n/// );\n/// ```\n#[derive(Clone, Default)]\npub struct PathConfig {\n    #[allow(clippy::type_complexity)]\n    err_handler: Option<Arc<dyn Fn(PathError, &HttpRequest) -> Error + Send + Sync>>,\n}\n\nimpl PathConfig {\n    /// Set custom error handler.\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(PathError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_router::ResourceDef;\n    use derive_more::Display;\n    use serde::Deserialize;\n\n    use super::*;\n    use crate::{error, http, test::TestRequest, HttpResponse};\n\n    #[derive(Deserialize, Debug, Display)]\n    #[display(\"MyStruct({}, {})\", key, value)]\n    struct MyStruct {\n        key: String,\n        value: String,\n    }\n\n    #[derive(Deserialize)]\n    struct Test2 {\n        key: String,\n        value: u32,\n    }\n\n    #[actix_rt::test]\n    async fn test_extract_path_single() {\n        let resource = ResourceDef::new(\"/{value}/\");\n\n        let mut req = TestRequest::with_uri(\"/32/\").to_srv_request();\n        resource.capture_match_info(req.match_info_mut());\n\n        let (req, mut pl) = req.into_parts();\n        assert_eq!(*Path::<i8>::from_request(&req, &mut pl).await.unwrap(), 32);\n        assert!(Path::<MyStruct>::from_request(&req, &mut pl).await.is_err());\n    }\n\n    #[allow(clippy::let_unit_value)]\n    #[actix_rt::test]\n    async fn test_tuple_extract() {\n        let resource = ResourceDef::new(\"/{key}/{value}/\");\n\n        let mut req = TestRequest::with_uri(\"/name/user1/?id=test\").to_srv_request();\n        resource.capture_match_info(req.match_info_mut());\n\n        let (req, mut pl) = req.into_parts();\n        let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(res.0, \"name\");\n        assert_eq!(res.1, \"user1\");\n\n        let (Path(a), Path(b)) =\n            <(Path<(String, String)>, Path<(String, String)>)>::from_request(&req, &mut pl)\n                .await\n                .unwrap();\n        assert_eq!(a.0, \"name\");\n        assert_eq!(a.1, \"user1\");\n        assert_eq!(b.0, \"name\");\n        assert_eq!(b.1, \"user1\");\n\n        let () = <()>::from_request(&req, &mut pl).await.unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn test_request_extract() {\n        let mut req = TestRequest::with_uri(\"/name/user1/?id=test\").to_srv_request();\n\n        let resource = ResourceDef::new(\"/{key}/{value}/\");\n        resource.capture_match_info(req.match_info_mut());\n\n        let (req, mut pl) = req.into_parts();\n        let mut s = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s.key, \"name\");\n        assert_eq!(s.value, \"user1\");\n        s.value = \"user2\".to_string();\n        assert_eq!(s.value, \"user2\");\n        assert_eq!(\n            format!(\"{}, {:?}\", s, s),\n            \"MyStruct(name, user2), Path(MyStruct { key: \\\"name\\\", value: \\\"user2\\\" })\"\n        );\n        let s = s.into_inner();\n        assert_eq!(s.value, \"user2\");\n\n        let Path(s) = Path::<(String, String)>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(s.0, \"name\");\n        assert_eq!(s.1, \"user1\");\n\n        let mut req = TestRequest::with_uri(\"/name/32/\").to_srv_request();\n        let resource = ResourceDef::new(\"/{key}/{value}/\");\n        resource.capture_match_info(req.match_info_mut());\n\n        let (req, mut pl) = req.into_parts();\n        let s = Path::<Test2>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s.as_ref().key, \"name\");\n        assert_eq!(s.value, 32);\n\n        let Path(s) = Path::<(String, u8)>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(s.0, \"name\");\n        assert_eq!(s.1, 32);\n\n        let res = Path::<Vec<String>>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n        assert_eq!(res[0], \"name\".to_owned());\n        assert_eq!(res[1], \"32\".to_owned());\n    }\n\n    #[actix_rt::test]\n    async fn paths_decoded() {\n        let resource = ResourceDef::new(\"/{key}/{value}\");\n        let mut req = TestRequest::with_uri(\"/na%2Bme/us%2Fer%254%32\").to_srv_request();\n        resource.capture_match_info(req.match_info_mut());\n\n        let (req, mut pl) = req.into_parts();\n        let path_items = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(path_items.key, \"na+me\");\n        assert_eq!(path_items.value, \"us/er%42\");\n        assert_eq!(req.match_info().as_str(), \"/na%2Bme/us%2Fer%2542\");\n    }\n\n    #[actix_rt::test]\n    async fn test_custom_err_handler() {\n        let (req, mut pl) = TestRequest::with_uri(\"/name/user1/\")\n            .app_data(PathConfig::default().error_handler(|err, _| {\n                error::InternalError::from_response(err, HttpResponse::Conflict().finish()).into()\n            }))\n            .to_http_parts();\n\n        let s = Path::<(usize,)>::from_request(&req, &mut pl)\n            .await\n            .unwrap_err();\n        let res = HttpResponse::from_error(s);\n\n        assert_eq!(res.status(), http::StatusCode::CONFLICT);\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/payload.rs",
    "content": "//! Basic binary and string payload extractors.\n\nuse std::{\n    borrow::Cow,\n    future::Future,\n    pin::Pin,\n    str,\n    task::{Context, Poll},\n};\n\nuse actix_http::error::PayloadError;\nuse actix_utils::future::{ready, Either, Ready};\nuse bytes::{Bytes, BytesMut};\nuse encoding_rs::{Encoding, UTF_8};\nuse futures_core::{ready, stream::Stream};\nuse mime::Mime;\n\nuse crate::{\n    body, dev, error::ErrorBadRequest, http::header, web, Error, FromRequest, HttpMessage,\n    HttpRequest,\n};\n\n/// Extract a request's raw payload stream.\n///\n/// See [`PayloadConfig`] for important notes when using this advanced extractor.\n///\n/// # Examples\n/// ```\n/// use std::future::Future;\n/// use futures_util::StreamExt as _;\n/// use actix_web::{post, web};\n///\n/// // `body: web::Payload` parameter extracts raw payload stream from request\n/// #[post(\"/\")]\n/// async fn index(mut body: web::Payload) -> actix_web::Result<String> {\n///     // for demonstration only; in a normal case use the `Bytes` extractor\n///     // collect payload stream into a bytes object\n///     let mut bytes = web::BytesMut::new();\n///     while let Some(item) = body.next().await {\n///         bytes.extend_from_slice(&item?);\n///     }\n///\n///     Ok(format!(\"Request Body Bytes:\\n{:?}\", bytes))\n/// }\n/// ```\npub struct Payload(dev::Payload);\n\nimpl Payload {\n    /// Unwrap to inner Payload type.\n    #[inline]\n    pub fn into_inner(self) -> dev::Payload {\n        self.0\n    }\n\n    /// Buffers payload from request up to `limit` bytes.\n    ///\n    /// This method is preferred over [`Payload::to_bytes()`] since it will not lead to unexpected\n    /// memory exhaustion from massive payloads. Note that the other primitive extractors such as\n    /// [`Bytes`] and [`String`], as well as extractors built on top of them, already have this sort\n    /// of protection according to the configured (or default) [`PayloadConfig`].\n    ///\n    /// # Errors\n    ///\n    /// - The outer error type, [`BodyLimitExceeded`](body::BodyLimitExceeded), is returned when the\n    ///   payload is larger than `limit`.\n    /// - The inner error type is [the normal Actix Web error](crate::Error) and is only returned if\n    ///   the payload stream yields an error for some reason. Such cases are usually caused by\n    ///   unrecoverable connection issues.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use actix_web::{error, web::Payload, Responder};\n    ///\n    /// async fn limited_payload_handler(pl: Payload) -> actix_web::Result<impl Responder> {\n    ///     match pl.to_bytes_limited(5).await {\n    ///         Ok(res) => res,\n    ///         Err(err) => Err(error::ErrorPayloadTooLarge(err)),\n    ///     }\n    /// }\n    /// ```\n    pub async fn to_bytes_limited(\n        self,\n        limit: usize,\n    ) -> Result<crate::Result<Bytes>, body::BodyLimitExceeded> {\n        let stream = body::BodyStream::new(self.0);\n\n        match body::to_bytes_limited(stream, limit).await {\n            Ok(Ok(body)) => Ok(Ok(body)),\n            Ok(Err(err)) => Ok(Err(err.into())),\n            Err(err) => Err(err),\n        }\n    }\n\n    /// Buffers entire payload from request.\n    ///\n    /// Use of this method is discouraged unless you know for certain that requests will not be\n    /// large enough to exhaust memory. If this is not known, prefer [`Payload::to_bytes_limited()`]\n    /// or one of the higher level extractors like [`Bytes`] or [`String`] that implement size\n    /// limits according to the configured (or default) [`PayloadConfig`].\n    ///\n    /// # Errors\n    ///\n    /// An error is only returned if the payload stream yields an error for some reason. Such cases\n    /// are usually caused by unrecoverable connection issues.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use actix_web::{error, web::Payload, Responder};\n    ///\n    /// async fn payload_handler(pl: Payload) -> actix_web::Result<impl Responder> {\n    ///     pl.to_bytes().await\n    /// }\n    /// ```\n    pub async fn to_bytes(self) -> crate::Result<Bytes> {\n        let stream = body::BodyStream::new(self.0);\n        Ok(body::to_bytes(stream).await?)\n    }\n}\n\nimpl Stream for Payload {\n    type Item = Result<Bytes, PayloadError>;\n\n    #[inline]\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        Pin::new(&mut self.0).poll_next(cx)\n    }\n}\n\n/// See [here](#Examples) for example of usage as an extractor.\nimpl FromRequest for Payload {\n    type Error = Error;\n    type Future = Ready<Result<Self, Self::Error>>;\n\n    #[inline]\n    fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n        ready(Ok(Payload(payload.take())))\n    }\n}\n\n/// Extract binary data from a request's payload.\n///\n/// Collects request payload stream into a [Bytes] instance.\n///\n/// Use [`PayloadConfig`] to configure extraction process.\n///\n/// # Examples\n/// ```\n/// use actix_web::{post, web};\n///\n/// /// extract binary data from request\n/// #[post(\"/\")]\n/// async fn index(body: web::Bytes) -> String {\n///     format!(\"Body {:?}!\", body)\n/// }\n/// ```\nimpl FromRequest for Bytes {\n    type Error = Error;\n    type Future = Either<BytesExtractFut, Ready<Result<Bytes, Error>>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n        // allow both Config and Data<Config>\n        let cfg = PayloadConfig::from_req(req);\n\n        if let Err(err) = cfg.check_mimetype(req) {\n            return Either::right(ready(Err(err)));\n        }\n\n        Either::left(BytesExtractFut {\n            body_fut: HttpMessageBody::new(req, payload).limit(cfg.limit),\n        })\n    }\n}\n\n/// Future for `Bytes` extractor.\npub struct BytesExtractFut {\n    body_fut: HttpMessageBody,\n}\n\nimpl Future for BytesExtractFut {\n    type Output = Result<Bytes, Error>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        Pin::new(&mut self.body_fut).poll(cx).map_err(Into::into)\n    }\n}\n\n/// Extract text information from a request's body.\n///\n/// Text extractor automatically decode body according to the request's charset.\n///\n/// Use [`PayloadConfig`] to configure extraction process.\n///\n/// # Examples\n/// ```\n/// use actix_web::{post, web, FromRequest};\n///\n/// // extract text data from request\n/// #[post(\"/\")]\n/// async fn index(text: String) -> String {\n///     format!(\"Body {}!\", text)\n/// }\nimpl FromRequest for String {\n    type Error = Error;\n    type Future = Either<StringExtractFut, Ready<Result<String, Error>>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {\n        let cfg = PayloadConfig::from_req(req);\n\n        // check content-type\n        if let Err(err) = cfg.check_mimetype(req) {\n            return Either::right(ready(Err(err)));\n        }\n\n        // check charset\n        let encoding = match req.encoding() {\n            Ok(enc) => enc,\n            Err(err) => return Either::right(ready(Err(err.into()))),\n        };\n        let limit = cfg.limit;\n        let body_fut = HttpMessageBody::new(req, payload).limit(limit);\n\n        Either::left(StringExtractFut { body_fut, encoding })\n    }\n}\n\n/// Future for `String` extractor.\npub struct StringExtractFut {\n    body_fut: HttpMessageBody,\n    encoding: &'static Encoding,\n}\n\nimpl Future for StringExtractFut {\n    type Output = Result<String, Error>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let encoding = self.encoding;\n\n        Pin::new(&mut self.body_fut).poll(cx).map(|out| {\n            let body = out?;\n            bytes_to_string(body, encoding)\n        })\n    }\n}\n\nfn bytes_to_string(body: Bytes, encoding: &'static Encoding) -> Result<String, Error> {\n    if encoding == UTF_8 {\n        Ok(str::from_utf8(body.as_ref())\n            .map_err(|_| ErrorBadRequest(\"Can not decode body\"))?\n            .to_owned())\n    } else {\n        Ok(encoding\n            .decode_without_bom_handling_and_without_replacement(&body)\n            .map(Cow::into_owned)\n            .ok_or_else(|| ErrorBadRequest(\"Can not decode body\"))?)\n    }\n}\n\n/// Configuration for request payloads.\n///\n/// Applies to the built-in [`Bytes`] and [`String`] extractors.\n/// Note that the [`Payload`] extractor does not automatically check\n/// conformance with this configuration to allow more flexibility when\n/// building extractors on top of [`Payload`].\n///\n/// By default, the payload size limit is 256kB and there is no mime type condition.\n///\n/// To use this, add an instance of it to your [`app`](crate::App), [`scope`](crate::Scope)\n/// or [`resource`](crate::Resource) through the associated `.app_data()` method.\n#[derive(Clone)]\npub struct PayloadConfig {\n    limit: usize,\n    mimetype: Option<Mime>,\n}\n\nimpl PayloadConfig {\n    /// Create new instance with a size limit (in bytes) and no mime type condition.\n    pub fn new(limit: usize) -> Self {\n        Self {\n            limit,\n            ..Default::default()\n        }\n    }\n\n    /// Set maximum accepted payload size in bytes. The default limit is 256KiB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        self.limit = limit;\n        self\n    }\n\n    /// Set required mime type of the request. By default mime type is not enforced.\n    pub fn mimetype(mut self, mt: Mime) -> Self {\n        self.mimetype = Some(mt);\n        self\n    }\n\n    fn check_mimetype(&self, req: &HttpRequest) -> Result<(), Error> {\n        // check content-type\n        if let Some(ref mt) = self.mimetype {\n            match req.mime_type() {\n                Ok(Some(ref req_mt)) => {\n                    if mt != req_mt {\n                        return Err(ErrorBadRequest(\"Unexpected Content-Type\"));\n                    }\n                }\n                Ok(None) => {\n                    return Err(ErrorBadRequest(\"Content-Type is expected\"));\n                }\n                Err(err) => {\n                    return Err(err.into());\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall\n    /// back to the default payload config if neither is found.\n    fn from_req(req: &HttpRequest) -> &Self {\n        req.app_data::<Self>()\n            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))\n            .unwrap_or(&DEFAULT_CONFIG)\n    }\n}\n\nconst DEFAULT_CONFIG_LIMIT: usize = 262_144; // 2^18 bytes (~256kB)\n\n/// Allow shared refs used as defaults.\nconst DEFAULT_CONFIG: PayloadConfig = PayloadConfig {\n    limit: DEFAULT_CONFIG_LIMIT,\n    mimetype: None,\n};\n\nimpl Default for PayloadConfig {\n    fn default() -> Self {\n        DEFAULT_CONFIG\n    }\n}\n\n/// Future that resolves to a complete HTTP body payload.\n///\n/// By default only 256kB payload is accepted before `PayloadError::Overflow` is returned.\n/// Use `MessageBody::limit()` method to change upper limit.\npub struct HttpMessageBody {\n    limit: usize,\n    length: Option<usize>,\n    #[cfg(feature = \"__compress\")]\n    stream: dev::Decompress<dev::Payload>,\n    #[cfg(not(feature = \"__compress\"))]\n    stream: dev::Payload,\n    buf: BytesMut,\n    err: Option<PayloadError>,\n}\n\nimpl HttpMessageBody {\n    /// Create `MessageBody` for request.\n    #[allow(clippy::borrow_interior_mutable_const)]\n    pub fn new(req: &HttpRequest, payload: &mut dev::Payload) -> HttpMessageBody {\n        let mut length = None;\n        let mut err = None;\n\n        if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {\n            match l.to_str() {\n                Ok(s) => match s.parse::<usize>() {\n                    Ok(l) => {\n                        if l > DEFAULT_CONFIG_LIMIT {\n                            err = Some(PayloadError::Overflow);\n                        }\n                        length = Some(l)\n                    }\n                    Err(_) => err = Some(PayloadError::UnknownLength),\n                },\n                Err(_) => err = Some(PayloadError::UnknownLength),\n            }\n        }\n\n        let stream = {\n            cfg_if::cfg_if! {\n                if #[cfg(feature = \"__compress\")] {\n                    dev::Decompress::from_headers(payload.take(), req.headers())\n                } else {\n                    payload.take()\n                }\n            }\n        };\n\n        HttpMessageBody {\n            stream,\n            limit: DEFAULT_CONFIG_LIMIT,\n            length,\n            buf: BytesMut::with_capacity(8192),\n            err,\n        }\n    }\n\n    /// Change max size of payload. By default max size is 256kB\n    pub fn limit(mut self, limit: usize) -> Self {\n        if let Some(l) = self.length {\n            self.err = if l > limit {\n                Some(PayloadError::Overflow)\n            } else {\n                None\n            };\n        }\n        self.limit = limit;\n        self\n    }\n}\n\nimpl Future for HttpMessageBody {\n    type Output = Result<Bytes, PayloadError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        if let Some(err) = this.err.take() {\n            return Poll::Ready(Err(err));\n        }\n\n        loop {\n            let res = ready!(Pin::new(&mut this.stream).poll_next(cx));\n            match res {\n                Some(chunk) => {\n                    let chunk = chunk?;\n                    if this.buf.len() + chunk.len() > this.limit {\n                        return Poll::Ready(Err(PayloadError::Overflow));\n                    } else {\n                        this.buf.extend_from_slice(&chunk);\n                    }\n                }\n                None => return Poll::Ready(Ok(this.buf.split().freeze())),\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        http::StatusCode,\n        test::{call_service, init_service, read_body, TestRequest},\n        App, Responder,\n    };\n\n    #[actix_rt::test]\n    async fn payload_to_bytes() {\n        async fn payload_handler(pl: Payload) -> crate::Result<impl Responder> {\n            pl.to_bytes().await\n        }\n\n        async fn limited_payload_handler(pl: Payload) -> crate::Result<impl Responder> {\n            match pl.to_bytes_limited(5).await {\n                Ok(res) => res,\n                Err(_limited) => Err(ErrorBadRequest(\"too big\")),\n            }\n        }\n\n        let srv = init_service(\n            App::new()\n                .route(\"/all\", web::to(payload_handler))\n                .route(\"limited\", web::to(limited_payload_handler)),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/all\")\n            .set_payload(\"1234567890\")\n            .to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        let body = read_body(res).await;\n        assert_eq!(body, \"1234567890\");\n\n        let req = TestRequest::with_uri(\"/limited\")\n            .set_payload(\"1234567890\")\n            .to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/limited\")\n            .set_payload(\"12345\")\n            .to_request();\n        let res = call_service(&srv, req).await;\n        assert_eq!(res.status(), StatusCode::OK);\n        let body = read_body(res).await;\n        assert_eq!(body, \"12345\");\n    }\n\n    #[actix_rt::test]\n    async fn test_payload_config() {\n        let req = TestRequest::default().to_http_request();\n        let cfg = PayloadConfig::default().mimetype(mime::APPLICATION_JSON);\n        assert!(cfg.check_mimetype(&req).is_err());\n\n        let req = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/x-www-form-urlencoded\"))\n            .to_http_request();\n        assert!(cfg.check_mimetype(&req).is_err());\n\n        let req = TestRequest::default()\n            .insert_header((header::CONTENT_TYPE, \"application/json\"))\n            .to_http_request();\n        assert!(cfg.check_mimetype(&req).is_ok());\n    }\n\n    // allow deprecated App::data\n    #[allow(deprecated)]\n    #[actix_rt::test]\n    async fn test_config_recall_locations() {\n        async fn bytes_handler(_: Bytes) -> impl Responder {\n            \"payload is probably json bytes\"\n        }\n\n        async fn string_handler(_: String) -> impl Responder {\n            \"payload is probably json string\"\n        }\n\n        let srv = init_service(\n            App::new()\n                .service(\n                    web::resource(\"/bytes-app-data\")\n                        .app_data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))\n                        .route(web::get().to(bytes_handler)),\n                )\n                .service(\n                    web::resource(\"/bytes-data\")\n                        .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))\n                        .route(web::get().to(bytes_handler)),\n                )\n                .service(\n                    web::resource(\"/string-app-data\")\n                        .app_data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))\n                        .route(web::get().to(string_handler)),\n                )\n                .service(\n                    web::resource(\"/string-data\")\n                        .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))\n                        .route(web::get().to(string_handler)),\n                ),\n        )\n        .await;\n\n        let req = TestRequest::with_uri(\"/bytes-app-data\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/bytes-data\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/string-app-data\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/string-data\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);\n\n        let req = TestRequest::with_uri(\"/bytes-app-data\")\n            .insert_header(header::ContentType(mime::APPLICATION_JSON))\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/bytes-data\")\n            .insert_header(header::ContentType(mime::APPLICATION_JSON))\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/string-app-data\")\n            .insert_header(header::ContentType(mime::APPLICATION_JSON))\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let req = TestRequest::with_uri(\"/string-data\")\n            .insert_header(header::ContentType(mime::APPLICATION_JSON))\n            .to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n    }\n\n    #[actix_rt::test]\n    async fn test_bytes() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_LENGTH, \"11\"))\n            .set_payload(Bytes::from_static(b\"hello=world\"))\n            .to_http_parts();\n\n        let s = Bytes::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s, Bytes::from_static(b\"hello=world\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_string() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_LENGTH, \"11\"))\n            .set_payload(Bytes::from_static(b\"hello=world\"))\n            .to_http_parts();\n\n        let s = String::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s, \"hello=world\");\n    }\n\n    #[actix_rt::test]\n    async fn test_message_body() {\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_LENGTH, \"xxxx\"))\n            .to_srv_request()\n            .into_parts();\n        let res = HttpMessageBody::new(&req, &mut pl).await;\n        match res.err().unwrap() {\n            PayloadError::UnknownLength => {}\n            _ => unreachable!(\"error\"),\n        }\n\n        let (req, mut pl) = TestRequest::default()\n            .insert_header((header::CONTENT_LENGTH, \"1000000\"))\n            .to_srv_request()\n            .into_parts();\n        let res = HttpMessageBody::new(&req, &mut pl).await;\n        match res.err().unwrap() {\n            PayloadError::Overflow => {}\n            _ => unreachable!(\"error\"),\n        }\n\n        let (req, mut pl) = TestRequest::default()\n            .set_payload(Bytes::from_static(b\"test\"))\n            .to_http_parts();\n        let res = HttpMessageBody::new(&req, &mut pl).await;\n        assert_eq!(res.ok().unwrap(), Bytes::from_static(b\"test\"));\n\n        let (req, mut pl) = TestRequest::default()\n            .set_payload(Bytes::from_static(b\"11111111111111\"))\n            .to_http_parts();\n        let res = HttpMessageBody::new(&req, &mut pl).limit(5).await;\n        match res.err().unwrap() {\n            PayloadError::Overflow => {}\n            _ => unreachable!(\"error\"),\n        }\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/query.rs",
    "content": "//! For query parameter extractor documentation, see [`Query`].\n\nuse std::{fmt, ops, sync::Arc};\n\nuse actix_utils::future::{ok, ready, Ready};\nuse serde::de::DeserializeOwned;\n\nuse crate::{dev::Payload, error::QueryPayloadError, Error, FromRequest, HttpRequest};\n\n/// Extract typed information from the request's query.\n///\n/// To extract typed data from the URL query string, the inner type `T` must implement the\n/// [`DeserializeOwned`] trait.\n///\n/// Use [`QueryConfig`] to configure extraction process.\n///\n/// # Panics\n/// A query string consists of unordered `key=value` pairs, therefore it cannot be decoded into any\n/// type which depends upon data ordering (eg. tuples). Trying to do so will result in a panic.\n///\n/// # Examples\n/// ```\n/// use actix_web::{get, web};\n/// use serde::Deserialize;\n///\n/// #[derive(Debug, Deserialize)]\n/// pub enum ResponseType {\n///    Token,\n///    Code\n/// }\n///\n/// #[derive(Debug, Deserialize)]\n/// pub struct AuthRequest {\n///    id: u64,\n///    response_type: ResponseType,\n/// }\n///\n/// // Deserialize `AuthRequest` struct from query string.\n/// // This handler gets called only if the request's query parameters contain both fields.\n/// // A valid request path for this handler would be `/?id=64&response_type=Code\"`.\n/// #[get(\"/\")]\n/// async fn index(info: web::Query<AuthRequest>) -> String {\n///     format!(\"Authorization request for id={} and type={:?}!\", info.id, info.response_type)\n/// }\n///\n/// // To access the entire underlying query struct, use `.into_inner()`.\n/// #[get(\"/debug1\")]\n/// async fn debug1(info: web::Query<AuthRequest>) -> String {\n///     dbg!(\"Authorization object = {:?}\", info.into_inner());\n///     \"OK\".to_string()\n/// }\n///\n/// // Or use destructuring, which is equivalent to `.into_inner()`.\n/// #[get(\"/debug2\")]\n/// async fn debug2(web::Query(info): web::Query<AuthRequest>) -> String {\n///     dbg!(\"Authorization object = {:?}\", info);\n///     \"OK\".to_string()\n/// }\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]\npub struct Query<T>(pub T);\n\nimpl<T> Query<T> {\n    /// Unwrap into inner `T` value.\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T: DeserializeOwned> Query<T> {\n    /// Deserialize a `T` from the URL encoded query parameter string.\n    ///\n    /// ```\n    /// # use std::collections::HashMap;\n    /// # use actix_web::web::Query;\n    /// let numbers = Query::<HashMap<String, u32>>::from_query(\"one=1&two=2\").unwrap();\n    /// assert_eq!(numbers.get(\"one\"), Some(&1));\n    /// assert_eq!(numbers.get(\"two\"), Some(&2));\n    /// assert!(numbers.get(\"three\").is_none());\n    /// ```\n    pub fn from_query(query_str: &str) -> Result<Self, QueryPayloadError> {\n        serde_urlencoded::from_str::<T>(query_str)\n            .map(Self)\n            .map_err(QueryPayloadError::Deserialize)\n    }\n}\n\nimpl<T> ops::Deref for Query<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T> ops::DerefMut for Query<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n\nimpl<T: fmt::Display> fmt::Display for Query<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n/// See [here](#Examples) for example of usage as an extractor.\nimpl<T: DeserializeOwned> FromRequest for Query<T> {\n    type Error = Error;\n    type Future = Ready<Result<Self, Error>>;\n\n    #[inline]\n    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {\n        let error_handler = req\n            .app_data::<QueryConfig>()\n            .and_then(|c| c.err_handler.clone());\n\n        serde_urlencoded::from_str::<T>(req.query_string())\n            .map(|val| ok(Query(val)))\n            .unwrap_or_else(move |err| {\n                let err = QueryPayloadError::Deserialize(err);\n\n                log::debug!(\n                    \"Failed during Query extractor deserialization. \\\n                     Request path: {:?}\",\n                    req.path()\n                );\n\n                let err = if let Some(error_handler) = error_handler {\n                    (error_handler)(err, req)\n                } else {\n                    err.into()\n                };\n\n                ready(Err(err))\n            })\n    }\n}\n\n/// Query extractor configuration.\n///\n/// # Examples\n/// ```\n/// use actix_web::{error, get, web, App, FromRequest, HttpResponse};\n/// use serde::Deserialize;\n///\n/// #[derive(Deserialize)]\n/// struct Info {\n///     username: String,\n/// }\n///\n/// /// deserialize `Info` from request's querystring\n/// #[get(\"/\")]\n/// async fn index(info: web::Query<Info>) -> String {\n///     format!(\"Welcome {}!\", info.username)\n/// }\n///\n/// // custom `Query` extractor configuration\n/// let query_cfg = web::QueryConfig::default()\n///     // use custom error handler\n///     .error_handler(|err, req| {\n///         error::InternalError::from_response(err, HttpResponse::Conflict().finish()).into()\n///     });\n///\n/// App::new()\n///     .app_data(query_cfg)\n///     .service(index);\n/// ```\n#[derive(Clone, Default)]\npub struct QueryConfig {\n    #[allow(clippy::type_complexity)]\n    err_handler: Option<Arc<dyn Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync>>,\n}\n\nimpl QueryConfig {\n    /// Set custom error handler\n    pub fn error_handler<F>(mut self, f: F) -> Self\n    where\n        F: Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,\n    {\n        self.err_handler = Some(Arc::new(f));\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::StatusCode;\n    use derive_more::Display;\n    use serde::Deserialize;\n\n    use super::*;\n    use crate::{error::InternalError, test::TestRequest, HttpResponse};\n\n    #[derive(Deserialize, Debug, Display)]\n    struct Id {\n        id: String,\n    }\n\n    #[actix_rt::test]\n    async fn test_service_request_extract() {\n        let req = TestRequest::with_uri(\"/name/user1/\").to_srv_request();\n        assert!(Query::<Id>::from_query(req.query_string()).is_err());\n\n        let req = TestRequest::with_uri(\"/name/user1/?id=test\").to_srv_request();\n        let mut s = Query::<Id>::from_query(req.query_string()).unwrap();\n\n        assert_eq!(s.id, \"test\");\n        assert_eq!(\n            format!(\"{}, {:?}\", s, s),\n            \"test, Query(Id { id: \\\"test\\\" })\"\n        );\n\n        s.id = \"test1\".to_string();\n        let s = s.into_inner();\n        assert_eq!(s.id, \"test1\");\n    }\n\n    #[actix_rt::test]\n    async fn test_request_extract() {\n        let req = TestRequest::with_uri(\"/name/user1/\").to_srv_request();\n        let (req, mut pl) = req.into_parts();\n        assert!(Query::<Id>::from_request(&req, &mut pl).await.is_err());\n\n        let req = TestRequest::with_uri(\"/name/user1/?id=test\").to_srv_request();\n        let (req, mut pl) = req.into_parts();\n\n        let mut s = Query::<Id>::from_request(&req, &mut pl).await.unwrap();\n        assert_eq!(s.id, \"test\");\n        assert_eq!(\n            format!(\"{}, {:?}\", s, s),\n            \"test, Query(Id { id: \\\"test\\\" })\"\n        );\n\n        s.id = \"test1\".to_string();\n        let s = s.into_inner();\n        assert_eq!(s.id, \"test1\");\n    }\n\n    #[actix_rt::test]\n    #[should_panic]\n    async fn test_tuple_panic() {\n        let req = TestRequest::with_uri(\"/?one=1&two=2\").to_srv_request();\n        let (req, mut pl) = req.into_parts();\n\n        Query::<(u32, u32)>::from_request(&req, &mut pl)\n            .await\n            .unwrap();\n    }\n\n    #[actix_rt::test]\n    async fn test_custom_error_responder() {\n        let req = TestRequest::with_uri(\"/name/user1/\")\n            .app_data(QueryConfig::default().error_handler(|e, _| {\n                let resp = HttpResponse::UnprocessableEntity().finish();\n                InternalError::from_response(e, resp).into()\n            }))\n            .to_srv_request();\n\n        let (req, mut pl) = req.into_parts();\n        let query = Query::<Id>::from_request(&req, &mut pl).await;\n\n        assert!(query.is_err());\n        assert_eq!(\n            query\n                .unwrap_err()\n                .as_response_error()\n                .error_response()\n                .status(),\n            StatusCode::UNPROCESSABLE_ENTITY\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web/src/types/readlines.rs",
    "content": "//! For request line reader documentation, see [`Readlines`].\n\nuse std::{\n    borrow::Cow,\n    pin::Pin,\n    str,\n    task::{Context, Poll},\n};\n\nuse bytes::{Bytes, BytesMut};\nuse encoding_rs::{Encoding, UTF_8};\nuse futures_core::{ready, stream::Stream};\n\nuse crate::{\n    dev::Payload,\n    error::{PayloadError, ReadlinesError},\n    HttpMessage,\n};\n\n/// Stream that reads request line by line.\npub struct Readlines<T: HttpMessage> {\n    stream: Payload<T::Stream>,\n    buf: BytesMut,\n    limit: usize,\n    checked_buff: bool,\n    encoding: &'static Encoding,\n    err: Option<ReadlinesError>,\n}\n\nimpl<T> Readlines<T>\nwhere\n    T: HttpMessage,\n    T::Stream: Stream<Item = Result<Bytes, PayloadError>> + Unpin,\n{\n    /// Create a new stream to read request line by line.\n    pub fn new(req: &mut T) -> Self {\n        let encoding = match req.encoding() {\n            Ok(enc) => enc,\n            Err(err) => return Self::err(err.into()),\n        };\n\n        Readlines {\n            stream: req.take_payload(),\n            buf: BytesMut::with_capacity(262_144),\n            limit: 262_144,\n            checked_buff: true,\n            err: None,\n            encoding,\n        }\n    }\n\n    /// Set maximum accepted payload size. The default limit is 256kB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        self.limit = limit;\n        self\n    }\n\n    fn err(err: ReadlinesError) -> Self {\n        Readlines {\n            stream: Payload::None,\n            buf: BytesMut::new(),\n            limit: 262_144,\n            checked_buff: true,\n            encoding: UTF_8,\n            err: Some(err),\n        }\n    }\n\n    /// Decodes one complete logical line using the request's configured encoding.\n    ///\n    /// Callers are expected to pass only the bytes that belong to the line being yielded,\n    /// whether they came from the internal buffer, the current payload chunk, or both.\n    fn decode(encoding: &'static Encoding, bytes: &[u8]) -> Result<String, ReadlinesError> {\n        if encoding == UTF_8 {\n            str::from_utf8(bytes)\n                .map_err(|_| ReadlinesError::EncodingError)\n                .map(str::to_owned)\n        } else {\n            encoding\n                .decode_without_bom_handling_and_without_replacement(bytes)\n                .map(Cow::into_owned)\n                .ok_or(ReadlinesError::EncodingError)\n        }\n    }\n}\n\nimpl<T> Stream for Readlines<T>\nwhere\n    T: HttpMessage,\n    T::Stream: Stream<Item = Result<Bytes, PayloadError>> + Unpin,\n{\n    type Item = Result<String, ReadlinesError>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.get_mut();\n\n        if let Some(err) = this.err.take() {\n            return Poll::Ready(Some(Err(err)));\n        }\n\n        // check if there is a newline in the buffer\n        if !this.checked_buff {\n            let mut found: Option<usize> = None;\n            for (ind, b) in this.buf.iter().enumerate() {\n                if *b == b'\\n' {\n                    found = Some(ind);\n                    break;\n                }\n            }\n            if let Some(ind) = found {\n                // check if line is longer than limit\n                if ind + 1 > this.limit {\n                    return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));\n                }\n                let line = Self::decode(this.encoding, &this.buf.split_to(ind + 1))?;\n                return Poll::Ready(Some(Ok(line)));\n            }\n            this.checked_buff = true;\n        }\n\n        // poll req for more bytes\n        match ready!(Pin::new(&mut this.stream).poll_next(cx)) {\n            Some(Ok(mut bytes)) => {\n                // check if there is a newline in bytes\n                let mut found: Option<usize> = None;\n                for (ind, b) in bytes.iter().enumerate() {\n                    if *b == b'\\n' {\n                        found = Some(ind);\n                        break;\n                    }\n                }\n                if let Some(ind) = found {\n                    // check if line is longer than limit\n                    if this.buf.len() + ind + 1 > this.limit {\n                        return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));\n                    }\n\n                    this.buf.extend_from_slice(&bytes.split_to(ind + 1));\n                    let line = Self::decode(this.encoding, &this.buf)?;\n                    this.buf.clear();\n\n                    // buffer bytes following the returned line\n                    this.buf.extend_from_slice(&bytes);\n                    this.checked_buff = this.buf.is_empty();\n                    return Poll::Ready(Some(Ok(line)));\n                }\n                this.buf.extend_from_slice(&bytes);\n                Poll::Pending\n            }\n\n            None => {\n                if this.buf.is_empty() {\n                    return Poll::Ready(None);\n                }\n                if this.buf.len() > this.limit {\n                    return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));\n                }\n                let line = Self::decode(this.encoding, &this.buf)?;\n                this.buf.clear();\n                Poll::Ready(Some(Ok(line)))\n            }\n\n            Some(Err(err)) => Poll::Ready(Some(Err(ReadlinesError::from(err)))),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{\n        pin::Pin,\n        task::{Context, Poll},\n    };\n\n    use actix_http::{h1, Request};\n    use futures_util::{task::noop_waker_ref, StreamExt as _};\n\n    use super::*;\n    use crate::{error::ReadlinesError, test::TestRequest};\n\n    #[actix_rt::test]\n    async fn test_readlines() {\n        let mut req = TestRequest::default()\n            .set_payload(Bytes::from_static(\n                b\"Lorem Ipsum is simply dummy text of the printing and typesetting\\n\\\n                  industry. Lorem Ipsum has been the industry's standard dummy\\n\\\n                  Contrary to popular belief, Lorem Ipsum is not simply random text.\",\n            ))\n            .to_request();\n\n        let mut stream = Readlines::new(&mut req);\n        assert_eq!(\n            stream.next().await.unwrap().unwrap(),\n            \"Lorem Ipsum is simply dummy text of the printing and typesetting\\n\"\n        );\n\n        assert_eq!(\n            stream.next().await.unwrap().unwrap(),\n            \"industry. Lorem Ipsum has been the industry's standard dummy\\n\"\n        );\n\n        assert_eq!(\n            stream.next().await.unwrap().unwrap(),\n            \"Contrary to popular belief, Lorem Ipsum is not simply random text.\"\n        );\n    }\n\n    #[test]\n    fn test_readlines_limit_across_chunks() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let payload: actix_http::Payload = payload.into();\n        let mut req = Request::with_payload(payload);\n        let mut stream = Readlines::new(&mut req).limit(10);\n        let mut cx = Context::from_waker(noop_waker_ref());\n\n        sender.feed_data(Bytes::from_static(b\"AAAAAAAAAA\"));\n        assert!(matches!(\n            Pin::new(&mut stream).poll_next(&mut cx),\n            Poll::Pending\n        ));\n\n        sender.feed_data(Bytes::from_static(b\"A\\n\"));\n        assert!(matches!(\n            Pin::new(&mut stream).poll_next(&mut cx),\n            Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)))\n        ));\n    }\n\n    #[test]\n    fn test_readlines_returns_full_line_across_chunks() {\n        let (mut sender, payload) = h1::Payload::create(false);\n        let payload: actix_http::Payload = payload.into();\n        let mut req = Request::with_payload(payload);\n        let mut stream = Readlines::new(&mut req);\n        let mut cx = Context::from_waker(noop_waker_ref());\n\n        sender.feed_data(Bytes::from_static(b\"hello \"));\n        assert!(matches!(\n            Pin::new(&mut stream).poll_next(&mut cx),\n            Poll::Pending\n        ));\n\n        sender.feed_data(Bytes::from_static(b\"world\\nnext\"));\n        assert!(matches!(\n            Pin::new(&mut stream).poll_next(&mut cx),\n            Poll::Ready(Some(Ok(ref line))) if line == \"hello world\\n\"\n        ));\n    }\n}\n"
  },
  {
    "path": "actix-web/src/web.rs",
    "content": "//! Essentials helper functions and types for application registration.\n//!\n//! # Request Extractors\n//! - [`Data`]: Application data item\n//! - [`ThinData`]: Cheap-to-clone application data item\n//! - [`ReqData`]: Request-local data item\n//! - [`Path`]: URL path parameters / dynamic segments\n//! - [`Query`]: URL query parameters\n//! - [`Header`]: Typed header\n//! - [`Json`]: JSON payload\n//! - [`Form`]: URL-encoded payload\n//! - [`Bytes`]: Raw payload\n//!\n//! # Responders\n//! - [`Json`]: JSON response\n//! - [`Form`]: URL-encoded response\n//! - [`Bytes`]: Raw bytes response\n//! - [`Redirect`](Redirect::to): Convenient redirect responses\n\nuse std::{borrow::Cow, future::Future};\n\nuse actix_router::IntoPatterns;\npub use bytes::{Buf, BufMut, Bytes, BytesMut};\n\npub use crate::{\n    config::ServiceConfig, data::Data, redirect::Redirect, request_data::ReqData,\n    thin_data::ThinData, types::*,\n};\nuse crate::{\n    error::BlockingError, http::Method, service::WebService, FromRequest, Handler, Resource,\n    Responder, Route, Scope,\n};\n\n/// Creates a new resource for a specific path.\n///\n/// Resources may have dynamic path segments. For example, a resource with the path `/a/{name}/c`\n/// would match all incoming requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.\n///\n/// A dynamic segment is specified in the form `{identifier}`, where the identifier can be used\n/// later in a request handler to access the matched value for that segment. This is done by looking\n/// up the identifier in the `Path` object returned by [`HttpRequest::match_info()`](crate::HttpRequest::match_info) method.\n///\n/// By default, each segment matches the regular expression `[^{}/]+`.\n///\n/// You can also specify a custom regex in the form `{identifier:regex}`:\n///\n/// For instance, to route `GET`-requests on any route matching `/users/{userid}/{friend}` and store\n/// `userid` and `friend` in the exposed `Path` object:\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App, HttpResponse};\n///\n/// let app = App::new().service(\n///     web::resource(\"/users/{userid}/{friend}\")\n///         .route(web::get().to(|| HttpResponse::Ok()))\n///         .route(web::head().to(|| HttpResponse::MethodNotAllowed()))\n/// );\n/// ```\npub fn resource<T: IntoPatterns>(path: T) -> Resource {\n    Resource::new(path)\n}\n\n/// Creates scope for common path prefix.\n///\n/// Scopes collect multiple paths under a common path prefix. The scope's path can contain dynamic\n/// path segments.\n///\n/// # Avoid Trailing Slashes\n/// Avoid using trailing slashes in the scope prefix (e.g., `web::scope(\"/scope/\")`). It will almost\n/// certainly not have the expected behavior. See the [documentation on resource definitions][pat]\n/// to understand why this is the case and how to correctly construct scope/prefix definitions.\n///\n/// # Examples\n/// In this example, three routes are set up (and will handle any method):\n/// - `/{project_id}/path1`\n/// - `/{project_id}/path2`\n/// - `/{project_id}/path3`\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App, HttpResponse};\n///\n/// let app = App::new().service(\n///     web::scope(\"/{project_id}\")\n///         .service(web::resource(\"/path1\").to(|| HttpResponse::Ok()))\n///         .service(web::resource(\"/path2\").to(|| HttpResponse::Ok()))\n///         .service(web::resource(\"/path3\").to(|| HttpResponse::MethodNotAllowed()))\n/// );\n/// ```\n///\n/// [pat]: crate::dev::ResourceDef#prefix-resources\npub fn scope(path: &str) -> Scope {\n    Scope::new(path)\n}\n\n/// Creates a new un-configured route.\npub fn route() -> Route {\n    Route::new()\n}\n\nmacro_rules! method_route {\n    ($method_fn:ident, $method_const:ident) => {\n        #[doc = concat!(\" Creates a new route with `\", stringify!($method_const), \"` method guard.\")]\n        ///\n        /// # Examples\n        #[doc = concat!(\" In this example, one `\", stringify!($method_const), \" /{project_id}` route is set up:\")]\n        /// ```\n        /// use actix_web::{web, App, HttpResponse};\n        ///\n        /// let app = App::new().service(\n        ///     web::resource(\"/{project_id}\")\n        #[doc = concat!(\"         .route(web::\", stringify!($method_fn), \"().to(|| HttpResponse::Ok()))\")]\n        ///\n        /// );\n        /// ```\n        pub fn $method_fn() -> Route {\n            method(Method::$method_const)\n        }\n    };\n}\n\nmethod_route!(get, GET);\nmethod_route!(post, POST);\nmethod_route!(put, PUT);\nmethod_route!(patch, PATCH);\nmethod_route!(delete, DELETE);\nmethod_route!(head, HEAD);\nmethod_route!(trace, TRACE);\n\n/// Creates a new route with specified method guard.\n///\n/// # Examples\n/// In this example, one `GET /{project_id}` route is set up:\n///\n/// ```\n/// use actix_web::{web, http, App, HttpResponse};\n///\n/// let app = App::new().service(\n///     web::resource(\"/{project_id}\")\n///         .route(web::method(http::Method::GET).to(|| HttpResponse::Ok()))\n/// );\n/// ```\npub fn method(method: Method) -> Route {\n    Route::new().method(method)\n}\n\n/// Creates a new any-method route with handler.\n///\n/// ```\n/// use actix_web::{web, App, HttpResponse, Responder};\n///\n/// async fn index() -> impl Responder {\n///    HttpResponse::Ok()\n/// }\n///\n/// App::new().service(\n///     web::resource(\"/\").route(\n///         web::to(index))\n/// );\n/// ```\npub fn to<F, Args>(handler: F) -> Route\nwhere\n    F: Handler<Args>,\n    Args: FromRequest + 'static,\n    F::Output: Responder + 'static,\n{\n    Route::new().to(handler)\n}\n\n/// Creates a raw service for a specific path.\n///\n/// ```\n/// use actix_web::{dev, web, guard, App, Error, HttpResponse};\n///\n/// async fn my_service(req: dev::ServiceRequest) -> Result<dev::ServiceResponse, Error> {\n///     Ok(req.into_response(HttpResponse::Ok().finish()))\n/// }\n///\n/// let app = App::new().service(\n///     web::service(\"/users/*\")\n///         .guard(guard::Header(\"content-type\", \"text/plain\"))\n///         .finish(my_service)\n/// );\n/// ```\npub fn service<T: IntoPatterns>(path: T) -> WebService {\n    WebService::new(path)\n}\n\n/// Create a relative or absolute redirect.\n///\n/// See [`Redirect`] docs for usage details.\n///\n/// # Examples\n/// ```\n/// use actix_web::{web, App};\n///\n/// let app = App::new()\n///     // the client will resolve this redirect to /api/to-path\n///     .service(web::redirect(\"/api/from-path\", \"to-path\"));\n/// ```\npub fn redirect(from: impl Into<Cow<'static, str>>, to: impl Into<Cow<'static, str>>) -> Redirect {\n    Redirect::new(from, to)\n}\n\n/// Executes blocking function on a thread pool, returns future that resolves to result of the\n/// function execution.\npub fn block<F, R>(f: F) -> impl Future<Output = Result<R, BlockingError>>\nwhere\n    F: FnOnce() -> R + Send + 'static,\n    R: Send + 'static,\n{\n    let fut = actix_rt::task::spawn_blocking(f);\n    async { fut.await.map_err(|_| BlockingError) }\n}\n"
  },
  {
    "path": "actix-web/tests/compression.rs",
    "content": "use actix_http::ContentEncoding;\nuse actix_web::{\n    http::{header, StatusCode},\n    middleware::Compress,\n    web, App, HttpResponse,\n};\nuse bytes::Bytes;\n\nmod utils;\n\nstatic LOREM: &[u8] = include_bytes!(\"fixtures/lorem.txt\");\nstatic LOREM_GZIP: &[u8] = include_bytes!(\"fixtures/lorem.txt.gz\");\nstatic LOREM_BR: &[u8] = include_bytes!(\"fixtures/lorem.txt.br\");\nstatic LOREM_ZSTD: &[u8] = include_bytes!(\"fixtures/lorem.txt.zst\");\nstatic LOREM_XZ: &[u8] = include_bytes!(\"fixtures/lorem.txt.xz\");\n\nmacro_rules! test_server {\n    () => {\n        actix_test::start(|| {\n            App::new()\n                .wrap(Compress::default())\n                .route(\n                    \"/static\",\n                    web::to(|| async { HttpResponse::Ok().body(LOREM) }),\n                )\n                .route(\n                    \"/static-gzip\",\n                    web::to(|| async {\n                        HttpResponse::Ok()\n                            // signal to compressor that content should not be altered\n                            // signal to client that content is encoded\n                            .insert_header(ContentEncoding::Gzip)\n                            .body(LOREM_GZIP)\n                    }),\n                )\n                .route(\n                    \"/static-br\",\n                    web::to(|| async {\n                        HttpResponse::Ok()\n                            // signal to compressor that content should not be altered\n                            // signal to client that content is encoded\n                            .insert_header(ContentEncoding::Brotli)\n                            .body(LOREM_BR)\n                    }),\n                )\n                .route(\n                    \"/static-zstd\",\n                    web::to(|| async {\n                        HttpResponse::Ok()\n                            // signal to compressor that content should not be altered\n                            // signal to client that content is encoded\n                            .insert_header(ContentEncoding::Zstd)\n                            .body(LOREM_ZSTD)\n                    }),\n                )\n                .route(\n                    \"/static-xz\",\n                    web::to(|| async {\n                        HttpResponse::Ok()\n                            // signal to compressor that content should not be altered\n                            // signal to client that content is encoded as 7zip\n                            .insert_header((header::CONTENT_ENCODING, \"xz\"))\n                            .body(LOREM_XZ)\n                    }),\n                )\n                .route(\n                    \"/echo\",\n                    web::to(|body: Bytes| async move { HttpResponse::Ok().body(body) }),\n                )\n        })\n    };\n}\n\n#[actix_rt::test]\nasync fn negotiate_encoding_identity() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static\")\n        .insert_header((header::ACCEPT_ENCODING, \"identity\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn negotiate_encoding_gzip() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static\")\n        .insert_header((header::ACCEPT_ENCODING, \"gzip, br;q=0.8, zstd;q=0.5\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"gzip\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    let mut res = srv\n        .post(\"/static\")\n        .no_decompress()\n        .insert_header((header::ACCEPT_ENCODING, \"gzip, br;q=0.8, zstd;q=0.5\"))\n        .send()\n        .await\n        .unwrap();\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), LOREM);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn negotiate_encoding_br() {\n    let srv = test_server!();\n\n    // check that brotli content-encoding header is returned\n\n    let req = srv\n        .post(\"/static\")\n        .insert_header((header::ACCEPT_ENCODING, \"br, zstd, gzip\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"br\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    // check that brotli is preferred even when later in (q-less) list\n\n    let req = srv\n        .post(\"/static\")\n        .insert_header((header::ACCEPT_ENCODING, \"gzip, zstd, br\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"br\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    // check that returned content is actually brotli encoded\n\n    let mut res = srv\n        .post(\"/static\")\n        .no_decompress()\n        .insert_header((header::ACCEPT_ENCODING, \"br, zstd, gzip\"))\n        .send()\n        .await\n        .unwrap();\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::brotli::decode(bytes), LOREM);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn negotiate_encoding_zstd() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static\")\n        .insert_header((header::ACCEPT_ENCODING, \"zstd, gzip, br;q=0.8\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"zstd\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    let mut res = srv\n        .post(\"/static\")\n        .no_decompress()\n        .insert_header((header::ACCEPT_ENCODING, \"zstd, gzip, br;q=0.8\"))\n        .send()\n        .await\n        .unwrap();\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::zstd::decode(bytes), LOREM);\n\n    srv.stop().await;\n}\n\n#[cfg(all(\n    feature = \"compress-brotli\",\n    feature = \"compress-gzip\",\n    feature = \"compress-zstd\",\n))]\n#[actix_rt::test]\nasync fn client_encoding_prefers_brotli() {\n    let srv = test_server!();\n\n    let req = srv.post(\"/static\").send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"br\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn gzip_no_decompress() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static-gzip\")\n        // don't decompress response body\n        .no_decompress()\n        // signal that we want a compressed body\n        .insert_header((header::ACCEPT_ENCODING, \"gzip, br;q=0.8, zstd;q=0.5\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"gzip\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM_GZIP));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn manual_custom_coding() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static-xz\")\n        // don't decompress response body\n        .no_decompress()\n        // signal that we want a compressed body\n        .insert_header((header::ACCEPT_ENCODING, \"xz\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"xz\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM_XZ));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn deny_identity_coding() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static\")\n        // signal that we want a compressed body\n        .insert_header((header::ACCEPT_ENCODING, \"br, identity;q=0\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"br\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn deny_identity_coding_no_decompress() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static-br\")\n        // don't decompress response body\n        .no_decompress()\n        // signal that we want a compressed body\n        .insert_header((header::ACCEPT_ENCODING, \"br, identity;q=0\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"br\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM_BR));\n\n    srv.stop().await;\n}\n\n// TODO: fix test\n// currently fails because negotiation doesn't consider unknown encoding types\n#[ignore]\n#[actix_rt::test]\nasync fn deny_identity_for_manual_coding() {\n    let srv = test_server!();\n\n    let req = srv\n        .post(\"/static-xz\")\n        // don't decompress response body\n        .no_decompress()\n        // signal that we want a compressed body\n        .insert_header((header::ACCEPT_ENCODING, \"xz, identity;q=0\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), \"xz\");\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(LOREM_XZ));\n\n    srv.stop().await;\n}\n"
  },
  {
    "path": "actix-web/tests/fixtures/lorem.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin interdum tincidunt lacus, sed tempor lorem consectetur et. Pellentesque et egestas sem, at cursus massa. Nunc feugiat elit sit amet ipsum commodo luctus. Proin auctor dignissim pharetra. Integer iaculis quam a tellus auctor, vitae auctor nisl viverra. Nullam consequat maximus porttitor. Pellentesque tortor enim, molestie at varius non, tempor non nibh. Suspendisse tempus erat lorem, vel faucibus magna blandit vel. Sed pellentesque ligula augue, vitae fermentum eros blandit et. Cras dignissim in massa ut varius. Vestibulum commodo nunc sit amet pellentesque dignissim.\n\nDonec imperdiet blandit lobortis. Suspendisse fringilla nunc quis venenatis tempor. Nunc tempor sed erat sed convallis. Pellentesque aliquet elit lectus, quis vulputate arcu pharetra sed. Etiam laoreet aliquet arcu cursus vehicula. Maecenas odio odio, elementum faucibus sollicitudin vitae, pellentesque ac purus. Donec venenatis faucibus lorem, et finibus lacus tincidunt vitae. Quisque laoreet metus sapien, vitae euismod mauris lobortis malesuada. Integer sit amet elementum turpis. Maecenas ex mauris, dapibus eu placerat vitae, rutrum convallis enim. Nulla vitae orci ultricies, sagittis turpis et, lacinia dui. Praesent egestas urna turpis, sit amet feugiat mauris tristique eu. Quisque id tempor libero. Donec ullamcorper dapibus lorem, vel consequat risus congue a.\n\nNullam dignissim ut lectus vitae tempor. Pellentesque ut odio fringilla, volutpat mi et, vulputate tellus. Fusce eget diam non odio tincidunt viverra eu vel augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam sed eleifend purus, vitae aliquam orci. Cras fringilla justo eget tempus bibendum. Phasellus venenatis, odio nec pulvinar commodo, quam neque lacinia turpis, ut rutrum tortor massa eu nulla. Vivamus tincidunt ut lectus a gravida. Donec varius mi quis enim interdum ultrices. Sed aliquam consectetur nisi vitae viverra. Praesent nec ligula egestas, porta lectus sed, consectetur augue.\n"
  },
  {
    "path": "actix-web/tests/introspection.rs",
    "content": "#![cfg(feature = \"experimental-introspection\")]\n\nuse actix_web::{guard, test, web, App, HttpResponse};\n\nasync fn introspection_handler(\n    tree: web::Data<actix_web::introspection::IntrospectionTree>,\n) -> HttpResponse {\n    HttpResponse::Ok()\n        .content_type(\"application/json\")\n        .body(tree.report_as_json())\n}\n\nasync fn externals_handler(\n    tree: web::Data<actix_web::introspection::IntrospectionTree>,\n) -> HttpResponse {\n    HttpResponse::Ok()\n        .content_type(\"application/json\")\n        .body(tree.report_externals_as_json())\n}\n\nfn find_item<'a>(items: &'a [serde_json::Value], path: &str) -> &'a serde_json::Value {\n    items\n        .iter()\n        .find(|item| item.get(\"full_path\").and_then(|v| v.as_str()) == Some(path))\n        .unwrap_or_else(|| panic!(\"missing route for {path}\"))\n}\n\nfn find_external<'a>(items: &'a [serde_json::Value], name: &str) -> &'a serde_json::Value {\n    items\n        .iter()\n        .find(|item| item.get(\"name\").and_then(|v| v.as_str()) == Some(name))\n        .unwrap_or_else(|| panic!(\"missing external resource for {name}\"))\n}\n\n#[actix_rt::test]\nasync fn introspection_report_includes_details_and_metadata() {\n    let app = test::init_service(\n        App::new()\n            .external_resource(\"app-external\", \"https://example.com/{id}\")\n            .service(\n                web::resource([\"/alpha\", \"/beta\"])\n                    .name(\"multi\")\n                    .route(web::get().to(HttpResponse::Ok)),\n            )\n            .service(\n                web::resource(\"/guarded\")\n                    .guard(guard::Header(\"accept\", \"text/plain\"))\n                    .route(web::get().to(HttpResponse::Ok)),\n            )\n            .service(\n                web::scope(\"/scoped\")\n                    .guard(guard::Header(\"x-scope\", \"1\"))\n                    .configure(|cfg| {\n                        cfg.external_resource(\"scope-external\", \"https://scope.example/{id}\");\n                    })\n                    .service(web::resource(\"/item\").route(web::get().to(HttpResponse::Ok))),\n            )\n            .service(web::resource(\"/introspection\").route(web::get().to(introspection_handler)))\n            .service(\n                web::resource(\"/introspection/externals\").route(web::get().to(externals_handler)),\n            ),\n    )\n    .await;\n\n    let req = test::TestRequest::get().uri(\"/introspection\").to_request();\n    let resp = test::call_service(&app, req).await;\n    assert!(resp.status().is_success());\n\n    let body = test::read_body(resp).await;\n    let items: Vec<serde_json::Value> =\n        serde_json::from_slice(&body).expect(\"invalid introspection json\");\n\n    let alpha = find_item(&items, \"/alpha\");\n    let patterns = alpha\n        .get(\"patterns\")\n        .and_then(|v| v.as_array())\n        .expect(\"patterns missing\");\n    let patterns = patterns\n        .iter()\n        .filter_map(|v| v.as_str())\n        .collect::<Vec<_>>();\n    assert!(patterns.contains(&\"/alpha\"));\n    assert!(patterns.contains(&\"/beta\"));\n    assert_eq!(\n        alpha.get(\"resource_name\").and_then(|v| v.as_str()),\n        Some(\"multi\")\n    );\n    assert_eq!(\n        alpha.get(\"resource_type\").and_then(|v| v.as_str()),\n        Some(\"resource\")\n    );\n\n    let guarded = find_item(&items, \"/guarded\");\n    let guards = guarded\n        .get(\"guards\")\n        .and_then(|v| v.as_array())\n        .expect(\"guards missing\");\n    assert!(guards\n        .iter()\n        .any(|v| v.as_str() == Some(\"Header(accept, text/plain)\")));\n\n    let guard_details = guarded\n        .get(\"guards_detail\")\n        .and_then(|v| v.as_array())\n        .expect(\"guards_detail missing\");\n    assert!(!guard_details.is_empty());\n\n    let alpha_guards = alpha\n        .get(\"guards\")\n        .and_then(|v| v.as_array())\n        .expect(\"alpha guards missing\");\n    let alpha_guard_details = alpha\n        .get(\"guards_detail\")\n        .and_then(|v| v.as_array())\n        .expect(\"alpha guards_detail missing\");\n    assert!(alpha_guards.is_empty());\n    assert!(!alpha_guard_details.is_empty());\n\n    let scoped = find_item(&items, \"/scoped\");\n    assert_eq!(\n        scoped.get(\"resource_type\").and_then(|v| v.as_str()),\n        Some(\"scope\")\n    );\n    let scoped_guards = scoped\n        .get(\"guards\")\n        .and_then(|v| v.as_array())\n        .expect(\"scoped guards missing\");\n    assert!(scoped_guards\n        .iter()\n        .any(|v| v.as_str() == Some(\"Header(x-scope, 1)\")));\n\n    let req = test::TestRequest::get()\n        .uri(\"/introspection/externals\")\n        .to_request();\n    let resp = test::call_service(&app, req).await;\n    assert!(resp.status().is_success());\n\n    let body = test::read_body(resp).await;\n    let externals: Vec<serde_json::Value> =\n        serde_json::from_slice(&body).expect(\"invalid externals json\");\n\n    let app_external = find_external(&externals, \"app-external\");\n    let app_patterns = app_external\n        .get(\"patterns\")\n        .and_then(|v| v.as_array())\n        .expect(\"app external patterns missing\");\n    assert!(app_patterns\n        .iter()\n        .any(|v| v.as_str() == Some(\"https://example.com/{id}\")));\n    assert_eq!(\n        app_external.get(\"origin_scope\").and_then(|v| v.as_str()),\n        Some(\"/\")\n    );\n\n    let scope_external = find_external(&externals, \"scope-external\");\n    let scope_patterns = scope_external\n        .get(\"patterns\")\n        .and_then(|v| v.as_array())\n        .expect(\"scope external patterns missing\");\n    assert!(scope_patterns\n        .iter()\n        .any(|v| v.as_str() == Some(\"https://scope.example/{id}\")));\n    assert_eq!(\n        scope_external.get(\"origin_scope\").and_then(|v| v.as_str()),\n        Some(\"/scoped\")\n    );\n}\n"
  },
  {
    "path": "actix-web/tests/test-macro-import-conflict.rs",
    "content": "//! Checks that test macro does not cause problems in the presence of imports named \"test\" that\n//! could be either a module with test items or the \"test with runtime\" macro itself.\n//!\n//! Before actix/actix-net#399 was implemented, this macro was running twice. The first run output\n//! `#[test]` and it got run again and since it was in scope.\n//!\n//! Prevented by using the fully-qualified test marker (`#[::core::prelude::v1::test]`).\n\nuse actix_web::test;\n\n#[actix_web::test]\nasync fn test_macro_naming_conflict() {\n    let _req = test::TestRequest::default();\n    assert_eq!(async { 1 }.await, 1);\n}\n"
  },
  {
    "path": "actix-web/tests/test_error_propagation.rs",
    "content": "use std::sync::Arc;\n\nuse actix_utils::future::{ok, Ready};\nuse actix_web::{\n    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},\n    get,\n    test::{call_service, init_service, TestRequest},\n    ResponseError,\n};\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::lock::Mutex;\n\n#[derive(Debug, Clone)]\npub struct MyError;\n\nimpl ResponseError for MyError {}\n\nimpl std::fmt::Display for MyError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"A custom error\")\n    }\n}\n\n#[get(\"/test\")]\nasync fn test() -> Result<actix_web::HttpResponse, actix_web::error::Error> {\n    Err(MyError.into())\n}\n\n#[derive(Clone)]\npub struct SpyMiddleware(Arc<Mutex<Option<bool>>>);\n\nimpl<S, B> Transform<S, ServiceRequest> for SpyMiddleware\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = actix_web::Error;\n    type Transform = Middleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ok(Middleware {\n            was_error: self.0.clone(),\n            service,\n        })\n    }\n}\n\n#[doc(hidden)]\npub struct Middleware<S> {\n    was_error: Arc<Mutex<Option<bool>>>,\n    service: S,\n}\n\nimpl<S, B> Service<ServiceRequest> for Middleware<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = actix_web::Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let lock = self.was_error.clone();\n        let response_future = self.service.call(req);\n        Box::pin(async move {\n            let response = response_future.await;\n            if let Ok(success) = &response {\n                *lock.lock().await = Some(success.response().error().is_some());\n            }\n            response\n        })\n    }\n}\n\n#[actix_rt::test]\nasync fn error_cause_should_be_propagated_to_middlewares() {\n    let lock = Arc::new(Mutex::new(None));\n    let spy_middleware = SpyMiddleware(lock.clone());\n\n    let app = init_service(\n        actix_web::App::new()\n            .wrap(spy_middleware.clone())\n            .service(test),\n    )\n    .await;\n\n    call_service(&app, TestRequest::with_uri(\"/test\").to_request()).await;\n\n    let was_error_captured = lock.lock().await.unwrap();\n    assert!(was_error_captured);\n}\n"
  },
  {
    "path": "actix-web/tests/test_httpserver.rs",
    "content": "#[cfg(feature = \"openssl\")]\nextern crate tls_openssl as openssl;\n\nuse std::{sync::mpsc, thread, time::Duration};\n\nuse actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};\n\n#[actix_rt::test]\nasync fn test_start() {\n    let addr = actix_test::unused_addr();\n    let (tx, rx) = mpsc::channel();\n\n    thread::spawn(move || {\n        actix_rt::System::new()\n            .block_on(async {\n                let srv = HttpServer::new(|| {\n                    App::new().service(\n                        web::resource(\"/\")\n                            .route(web::to(|| async { HttpResponse::Ok().body(\"test\") })),\n                    )\n                })\n                .workers(1)\n                .backlog(1)\n                .max_connections(10)\n                .max_connection_rate(10)\n                .keep_alive(Duration::from_secs(10))\n                .client_request_timeout(Duration::from_secs(5))\n                .client_disconnect_timeout(Duration::ZERO)\n                .server_hostname(\"localhost\")\n                .system_exit()\n                .disable_signals()\n                .bind(format!(\"{}\", addr))\n                .unwrap()\n                .run();\n\n                tx.send(srv.handle()).unwrap();\n\n                srv.await\n            })\n            .unwrap();\n    });\n\n    let srv = rx.recv().unwrap();\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().timeout(Duration::from_millis(100)))\n        .finish();\n\n    let host = format!(\"http://{}\", addr);\n    let response = client.get(host.clone()).send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // Attempt to start a second server using the same address.\n    let result = HttpServer::new(|| {\n        App::new().service(\n            web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(\"test\") })),\n        )\n    })\n    .workers(1)\n    .backlog(1)\n    .max_connections(10)\n    .max_connection_rate(10)\n    .keep_alive(Duration::from_secs(10))\n    .client_request_timeout(Duration::from_secs(5))\n    .client_disconnect_timeout(Duration::ZERO)\n    .server_hostname(\"localhost\")\n    .system_exit()\n    .disable_signals()\n    .bind(format!(\"{}\", addr));\n\n    // This should fail: the address is in use.\n    assert!(result.is_err());\n\n    srv.stop(false).await;\n}\n\n#[cfg(feature = \"openssl\")]\nfn ssl_acceptor() -> openssl::ssl::SslAcceptorBuilder {\n    use openssl::{\n        pkey::PKey,\n        ssl::{SslAcceptor, SslMethod},\n        x509::X509,\n    };\n\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_file = cert.pem();\n    let key_file = key_pair.serialize_pem();\n\n    let cert = X509::from_pem(cert_file.as_bytes()).unwrap();\n    let key = PKey::private_key_from_pem(key_file.as_bytes()).unwrap();\n\n    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();\n    builder.set_certificate(&cert).unwrap();\n    builder.set_private_key(&key).unwrap();\n\n    builder\n}\n\n#[actix_rt::test]\n#[cfg(feature = \"openssl\")]\nasync fn test_start_ssl() {\n    use actix_web::HttpRequest;\n    use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};\n\n    let addr = actix_test::unused_addr();\n    let (tx, rx) = mpsc::channel();\n\n    thread::spawn(move || {\n        actix_rt::System::new()\n            .block_on(async {\n                let builder = ssl_acceptor();\n\n                let srv = HttpServer::new(|| {\n                    App::new().service(web::resource(\"/\").route(web::to(|req: HttpRequest| {\n                        assert!(req.app_config().secure());\n                        async { HttpResponse::Ok().body(\"test\") }\n                    })))\n                })\n                .workers(1)\n                .shutdown_timeout(1)\n                .system_exit()\n                .disable_signals()\n                .bind_openssl(format!(\"{}\", addr), builder)\n                .unwrap();\n\n                let srv = srv.run();\n                tx.send(srv.handle()).unwrap();\n\n                srv.await\n            })\n            .unwrap()\n    });\n    let srv = rx.recv().unwrap();\n\n    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n    builder.set_verify(SslVerifyMode::NONE);\n    let _ = builder\n        .set_alpn_protos(b\"\\x02h2\\x08http/1.1\")\n        .map_err(|e| log::error!(\"Can not set alpn protocol: {:?}\", e));\n\n    let client = awc::Client::builder()\n        .connector(\n            awc::Connector::new()\n                .openssl(builder.build())\n                .timeout(Duration::from_millis(100)),\n        )\n        .finish();\n\n    let host = format!(\"https://{}\", addr);\n    let response = client.get(host.clone()).send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop(false).await;\n}\n\nasync fn assert_tcp_nodelay_config(nodelay: bool) {\n    let addr = actix_test::unused_addr();\n    let (tx, rx) = mpsc::channel();\n\n    thread::spawn(move || {\n        actix_rt::System::new()\n            .block_on(async move {\n                let srv = HttpServer::new(move || {\n                    let expected = nodelay;\n\n                    App::new().service(web::resource(\"/\").route(web::to(\n                        move |req: HttpRequest| {\n                            let expected = expected;\n\n                            async move {\n                                let actual = req.conn_data::<bool>().copied().unwrap_or(!expected);\n                                if actual == expected {\n                                    HttpResponse::Ok().finish()\n                                } else {\n                                    HttpResponse::InternalServerError().finish()\n                                }\n                            }\n                        },\n                    )))\n                })\n                .workers(1)\n                .tcp_nodelay(nodelay)\n                .on_connect(move |io, ext| {\n                    if let Some(io) = io.downcast_ref::<actix_web::rt::net::TcpStream>() {\n                        ext.insert(io.nodelay().unwrap());\n                    }\n                })\n                .bind(format!(\"{}\", addr))\n                .unwrap()\n                .run();\n\n                tx.send(srv.handle()).unwrap();\n                srv.await\n            })\n            .unwrap()\n    });\n\n    let srv = rx.recv().unwrap();\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().timeout(Duration::from_millis(100)))\n        .finish();\n\n    let response = client.get(format!(\"http://{}\", addr)).send().await.unwrap();\n    assert!(response.status().is_success());\n\n    srv.stop(false).await;\n}\n\n#[actix_rt::test]\nasync fn test_tcp_nodelay_enabled() {\n    assert_tcp_nodelay_config(true).await;\n}\n\n#[actix_rt::test]\nasync fn test_tcp_nodelay_disabled() {\n    assert_tcp_nodelay_config(false).await;\n}\n"
  },
  {
    "path": "actix-web/tests/test_server.rs",
    "content": "#[cfg(feature = \"openssl\")]\nextern crate tls_openssl as openssl;\n#[cfg(feature = \"rustls-0_23\")]\nextern crate tls_rustls as rustls;\n\nuse std::{\n    future::Future,\n    io::{Read, Write},\n    pin::Pin,\n    task::{Context, Poll},\n    time::Duration,\n};\n\nuse actix_web::{\n    cookie::Cookie,\n    http::{header, StatusCode},\n    middleware::{Compress, NormalizePath, TrailingSlash},\n    web, App, Error, HttpResponse,\n};\nuse bytes::Bytes;\nuse futures_core::ready;\n#[cfg(feature = \"openssl\")]\nuse openssl::{\n    pkey::PKey,\n    ssl::{SslAcceptor, SslMethod},\n    x509::X509,\n};\nuse rand::distr::{Alphanumeric, SampleString as _};\n\nmod utils;\n\nconst S: &str = \"Hello World \";\nconst STR: &str = const_str::repeat!(S, 100);\n\n#[cfg(feature = \"openssl\")]\nfn openssl_config() -> SslAcceptor {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_file = cert.pem();\n    let key_file = key_pair.serialize_pem();\n\n    let cert = X509::from_pem(cert_file.as_bytes()).unwrap();\n    let key = PKey::private_key_from_pem(key_file.as_bytes()).unwrap();\n\n    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();\n    builder.set_certificate(&cert).unwrap();\n    builder.set_private_key(&key).unwrap();\n\n    builder.set_alpn_select_callback(|_, protos| {\n        const H2: &[u8] = b\"\\x02h2\";\n        if protos.windows(3).any(|window| window == H2) {\n            Ok(b\"h2\")\n        } else {\n            Err(openssl::ssl::AlpnError::NOACK)\n        }\n    });\n    builder.set_alpn_protos(b\"\\x02h2\").unwrap();\n\n    builder.build()\n}\n\nstruct TestBody {\n    data: Bytes,\n    chunk_size: usize,\n    delay: Pin<Box<actix_rt::time::Sleep>>,\n}\n\nimpl TestBody {\n    fn new(data: Bytes, chunk_size: usize) -> Self {\n        TestBody {\n            data,\n            chunk_size,\n            delay: Box::pin(actix_rt::time::sleep(std::time::Duration::from_millis(10))),\n        }\n    }\n}\n\nimpl futures_core::stream::Stream for TestBody {\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        ready!(Pin::new(&mut self.delay).poll(cx));\n\n        self.delay = Box::pin(actix_rt::time::sleep(std::time::Duration::from_millis(10)));\n        let chunk_size = std::cmp::min(self.chunk_size, self.data.len());\n        let chunk = self.data.split_to(chunk_size);\n        if chunk.is_empty() {\n            Poll::Ready(None)\n        } else {\n            Poll::Ready(Some(Ok(chunk)))\n        }\n    }\n}\n\n#[actix_rt::test]\nasync fn test_body() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(STR) })))\n    });\n\n    let mut res = srv.get(\"/\").send().await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n// enforcing an encoding per-response is removed\n// #[actix_rt::test]\n// async fn test_body_encoding_override() {\n//     let srv = actix_test::start_with(actix_test::config().h1(), || {\n//         App::new()\n//             .wrap(Compress::default())\n//             .service(web::resource(\"/\").route(web::to(|| {\n//                 HttpResponse::Ok()\n//                     .encode_with(ContentEncoding::Deflate)\n//                     .body(STR)\n//             })))\n//             .service(web::resource(\"/raw\").route(web::to(|| {\n//                 let mut res = HttpResponse::with_body(actix_web::http::StatusCode::OK, STR);\n//                 res.encode_with(ContentEncoding::Deflate);\n//                 res.map_into_boxed_body()\n//             })))\n//     });\n\n//     // Builder\n//     let mut res = srv\n//         .get(\"/\")\n//         .no_decompress()\n//         .append_header((ACCEPT_ENCODING, \"deflate\"))\n//         .send()\n//         .await\n//         .unwrap();\n//     assert_eq!(res.status(), StatusCode::OK);\n\n//     let bytes = res.body().await.unwrap();\n//     assert_eq!(utils::deflate::decode(bytes), STR.as_bytes());\n\n//     // Raw Response\n//     let mut res = srv\n//         .request(actix_web::http::Method::GET, srv.url(\"/raw\"))\n//         .no_decompress()\n//         .append_header((ACCEPT_ENCODING, \"deflate\"))\n//         .send()\n//         .await\n//         .unwrap();\n//     assert_eq!(res.status(), StatusCode::OK);\n\n//     let bytes = res.body().await.unwrap();\n//     assert_eq!(utils::deflate::decode(bytes), STR.as_bytes());\n\n//     srv.stop().await;\n// }\n\n#[actix_rt::test]\nasync fn body_gzip_large() {\n    let data = STR.repeat(10);\n    let srv_data = data.clone();\n\n    let srv = actix_test::start_with(actix_test::config().h1(), move || {\n        let data = srv_data.clone();\n\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(move || {\n                let data = data.clone();\n                async move { HttpResponse::Ok().body(data.clone()) }\n            })))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .no_decompress()\n        .append_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), data.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_gzip_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 70_000);\n    let srv_data = data.clone();\n\n    let srv = actix_test::start_with(actix_test::config().h1(), move || {\n        let data = srv_data.clone();\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(move || {\n                let data = data.clone();\n                async move { HttpResponse::Ok().body(data.clone()) }\n            })))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .no_decompress()\n        .append_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), data.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_chunked_implicit() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::get().to(|| async {\n                HttpResponse::Ok().streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))\n            })))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .no_decompress()\n        .append_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(\n        res.headers().get(header::TRANSFER_ENCODING).unwrap(),\n        \"chunked\"\n    );\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_br_streaming() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(|| async {\n                HttpResponse::Ok().streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))\n            })))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .append_header((header::ACCEPT_ENCODING, \"br\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::brotli::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_head_binary() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(\n            web::resource(\"/\")\n                .route(web::head().to(move || async { HttpResponse::Ok().body(STR) })),\n        )\n    });\n\n    let mut res = srv.head(\"/\").send().await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let len = res.headers().get(header::CONTENT_LENGTH).unwrap();\n    assert_eq!(format!(\"{}\", STR.len()), len.to_str().unwrap());\n\n    let bytes = res.body().await.unwrap();\n    assert!(bytes.is_empty());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_no_chunking() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move || async {\n            HttpResponse::Ok()\n                .no_chunking(STR.len() as u64)\n                .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))\n        })))\n    });\n\n    let mut res = srv.get(\"/\").send().await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n    assert!(!res.headers().contains_key(header::TRANSFER_ENCODING));\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_deflate() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().wrap(Compress::default()).service(\n            web::resource(\"/\").route(web::to(move || async { HttpResponse::Ok().body(STR) })),\n        )\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .append_header((header::ACCEPT_ENCODING, \"deflate\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::deflate::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_brotli() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().wrap(Compress::default()).service(\n            web::resource(\"/\").route(web::to(move || async { HttpResponse::Ok().body(STR) })),\n        )\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .append_header((header::ACCEPT_ENCODING, \"br\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::brotli::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_zstd() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().wrap(Compress::default()).service(\n            web::resource(\"/\").route(web::to(move || async { HttpResponse::Ok().body(STR) })),\n        )\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .append_header((header::ACCEPT_ENCODING, \"zstd\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::zstd::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_body_zstd_streaming() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(move || async {\n                HttpResponse::Ok().streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))\n            })))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .append_header((header::ACCEPT_ENCODING, \"zstd\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::zstd::decode(bytes), STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_zstd_encoding() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"zstd\"))\n        .send_body(utils::zstd::encode(STR));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_zstd_encoding_large() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 320_000);\n\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(\n            web::resource(\"/\")\n                .app_data(web::PayloadConfig::new(320_000))\n                .route(web::to(move |body: Bytes| async {\n                    HttpResponse::Ok().streaming(TestBody::new(body, 10240))\n                })),\n        )\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"zstd\"))\n        .send_body(utils::zstd::encode(&data));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().limit(320_000).await.unwrap();\n    assert_eq!(bytes, data.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_encoding() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n                HttpResponse::Ok().body(body)\n            })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .insert_header((header::CONTENT_ENCODING, \"gzip\"))\n        .send_body(utils::gzip::encode(STR));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_gzip_encoding() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"gzip\"))\n        .send_body(utils::gzip::encode(STR));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, STR.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_gzip_encoding_large() {\n    let data = STR.repeat(10);\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let req = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"gzip\"))\n        .send_body(utils::gzip::encode(&data));\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, data);\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_reading_gzip_encoding_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 60_000);\n\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"gzip\"))\n        .send_body(utils::gzip::encode(&data));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, data.as_bytes());\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_reading_deflate_encoding() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"deflate\"))\n        .send_body(utils::deflate::encode(STR));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_reading_deflate_encoding_large() {\n    let data = STR.repeat(10);\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"deflate\"))\n        .send_body(utils::deflate::encode(&data));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from(data));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_reading_deflate_encoding_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 160_000);\n\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"deflate\"))\n        .send_body(utils::deflate::encode(&data));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes.len(), data.len());\n    assert_eq!(bytes, Bytes::from(data));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_brotli_encoding() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(web::resource(\"/\").route(web::to(move |body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        })))\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"br\"))\n        .send_body(utils::brotli::encode(STR));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_brotli_encoding_large() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 320_000);\n\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new().service(\n            web::resource(\"/\")\n                .app_data(web::PayloadConfig::new(320_000))\n                .route(web::to(move |body: Bytes| async {\n                    HttpResponse::Ok().streaming(TestBody::new(body, 10240))\n                })),\n        )\n    });\n\n    let request = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"br\"))\n        .send_body(utils::brotli::encode(&data));\n    let mut res = request.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().limit(320_000).await.unwrap();\n    assert_eq!(bytes, Bytes::from(data));\n\n    srv.stop().await;\n}\n\n#[cfg(feature = \"openssl\")]\n#[actix_rt::test]\nasync fn test_brotli_encoding_large_openssl() {\n    use actix_web::http::header;\n\n    let data = STR.repeat(10);\n    let srv = actix_test::start_with(actix_test::config().openssl(openssl_config()), move || {\n        App::new().service(web::resource(\"/\").route(web::to(|bytes: Bytes| async {\n            // echo decompressed request body back in response\n            HttpResponse::Ok()\n                .insert_header(header::ContentEncoding::Identity)\n                .body(bytes)\n        })))\n    });\n\n    let mut res = srv\n        .post(\"/\")\n        .append_header((header::CONTENT_ENCODING, \"br\"))\n        .send_body(utils::brotli::encode(&data))\n        .await\n        .unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from(data));\n\n    srv.stop().await;\n}\n\n#[cfg(feature = \"rustls-0_23\")]\nmod plus_rustls {\n    use rustls::{pki_types::PrivateKeyDer, ServerConfig as RustlsServerConfig};\n    use rustls_pki_types::PrivatePkcs8KeyDer;\n\n    use super::*;\n\n    fn tls_config() -> RustlsServerConfig {\n        let rcgen::CertifiedKey { cert, key_pair } =\n            rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n        let cert_chain = vec![cert.der().clone()];\n        let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));\n\n        RustlsServerConfig::builder()\n            .with_no_client_auth()\n            .with_single_cert(cert_chain, key_der)\n            .unwrap()\n    }\n\n    #[actix_rt::test]\n    async fn test_reading_deflate_encoding_large_random_rustls() {\n        let data = Alphanumeric.sample_string(&mut rand::rng(), 160_000);\n\n        let srv = actix_test::start_with(actix_test::config().rustls_0_23(tls_config()), || {\n            App::new().service(web::resource(\"/\").route(web::to(|bytes: Bytes| async {\n                // echo decompressed request body back in response\n                HttpResponse::Ok()\n                    .insert_header(header::ContentEncoding::Identity)\n                    .body(bytes)\n            })))\n        });\n\n        let req = srv\n            .post(\"/\")\n            .insert_header((header::CONTENT_ENCODING, \"deflate\"))\n            .send_stream(TestBody::new(\n                Bytes::from(utils::deflate::encode(&data)),\n                1024,\n            ));\n\n        let mut res = req.await.unwrap();\n        assert_eq!(res.status(), StatusCode::OK);\n\n        let bytes = res.body().await.unwrap();\n        assert_eq!(bytes.len(), data.len());\n        assert_eq!(bytes, Bytes::from(data));\n\n        srv.stop().await;\n    }\n}\n\n#[actix_rt::test]\nasync fn test_server_cookies() {\n    use actix_web::http;\n\n    let srv = actix_test::start(|| {\n        App::new().default_service(web::to(|| async {\n            HttpResponse::Ok()\n                .cookie(\n                    Cookie::build(\"first\", \"first_value\")\n                        .http_only(true)\n                        .finish(),\n                )\n                .cookie(Cookie::new(\"second\", \"first_value\"))\n                .cookie(Cookie::new(\"second\", \"second_value\"))\n                .finish()\n        }))\n    });\n\n    let req = srv.get(\"/\");\n    let res = req.send().await.unwrap();\n    assert!(res.status().is_success());\n\n    {\n        let first_cookie = Cookie::build(\"first\", \"first_value\")\n            .http_only(true)\n            .finish();\n        let second_cookie = Cookie::new(\"second\", \"first_value\");\n\n        let cookies = res.cookies().expect(\"To have cookies\");\n        assert_eq!(cookies.len(), 3);\n        if cookies[0] == first_cookie {\n            assert_eq!(cookies[1], second_cookie);\n        } else {\n            assert_eq!(cookies[0], second_cookie);\n            assert_eq!(cookies[1], first_cookie);\n        }\n\n        let first_cookie = first_cookie.to_string();\n        let second_cookie = second_cookie.to_string();\n        // Check that we have exactly two instances of raw cookie headers\n        let cookies = res\n            .headers()\n            .get_all(http::header::SET_COOKIE)\n            .map(|header| header.to_str().expect(\"To str\").to_string())\n            .collect::<Vec<_>>();\n        assert_eq!(cookies.len(), 3);\n        if cookies[0] == first_cookie {\n            assert_eq!(cookies[1], second_cookie);\n        } else {\n            assert_eq!(cookies[0], second_cookie);\n            assert_eq!(cookies[1], first_cookie);\n        }\n    }\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_slow_request() {\n    use std::net;\n\n    let srv = actix_test::start_with(\n        actix_test::config().client_request_timeout(Duration::from_millis(200)),\n        || App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n    );\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 408 Request Timeout\"));\n\n    let mut stream = net::TcpStream::connect(srv.addr()).unwrap();\n    let _ = stream.write_all(b\"GET /test/tests/test HTTP/1.1\\r\\n\");\n    let mut data = String::new();\n    let _ = stream.read_to_string(&mut data);\n    assert!(data.starts_with(\"HTTP/1.1 408 Request Timeout\"));\n\n    srv.stop().await;\n}\n\n#[actix_rt::test]\nasync fn test_normalize() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(NormalizePath::new(TrailingSlash::Trim))\n            .service(web::resource(\"/one\").route(web::to(HttpResponse::Ok)))\n    });\n\n    let res = srv.get(\"/one/\").send().await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    srv.stop().await\n}\n\n// allow deprecated App::data\n#[allow(deprecated)]\n#[actix_rt::test]\nasync fn test_data_drop() {\n    use std::sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    };\n\n    struct TestData(Arc<AtomicUsize>);\n\n    impl TestData {\n        fn new(inner: Arc<AtomicUsize>) -> Self {\n            let _ = inner.fetch_add(1, Ordering::SeqCst);\n            Self(inner)\n        }\n    }\n\n    impl Clone for TestData {\n        fn clone(&self) -> Self {\n            let inner = self.0.clone();\n            let _ = inner.fetch_add(1, Ordering::SeqCst);\n            Self(inner)\n        }\n    }\n\n    impl Drop for TestData {\n        fn drop(&mut self) {\n            self.0.fetch_sub(1, Ordering::SeqCst);\n        }\n    }\n\n    let num = Arc::new(AtomicUsize::new(0));\n    let data = TestData::new(num.clone());\n    assert_eq!(num.load(Ordering::SeqCst), 1);\n\n    let srv = actix_test::start(move || {\n        let data = data.clone();\n\n        App::new()\n            .data(data)\n            .service(web::resource(\"/\").to(|_data: web::Data<TestData>| async { \"ok\" }))\n    });\n\n    assert!(srv.get(\"/\").send().await.unwrap().status().is_success());\n    srv.stop().await;\n\n    assert_eq!(num.load(Ordering::SeqCst), 0);\n}\n\n#[actix_rt::test]\nasync fn test_accept_encoding_no_match() {\n    let srv = actix_test::start_with(actix_test::config().h1(), || {\n        App::new()\n            .wrap(Compress::default())\n            .service(web::resource(\"/\").route(web::to(HttpResponse::Ok)))\n    });\n\n    let mut res = srv\n        .get(\"/\")\n        .insert_header((header::ACCEPT_ENCODING, \"xz, identity;q=0\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n\n    assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);\n    assert_eq!(res.headers().get(header::CONTENT_ENCODING), None);\n\n    let bytes = res.body().await.unwrap();\n    // body should contain the supported encodings\n    assert!(!bytes.is_empty());\n\n    srv.stop().await;\n}\n"
  },
  {
    "path": "actix-web/tests/test_streaming_response.rs",
    "content": "use std::{\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_web::{\n    http::header::{self, HeaderValue},\n    HttpResponse,\n};\nuse bytes::Bytes;\nuse futures_core::Stream;\n\nstruct FixedSizeStream {\n    data: Vec<u8>,\n    yielded: bool,\n}\n\nimpl FixedSizeStream {\n    fn new(size: usize) -> Self {\n        Self {\n            data: vec![0u8; size],\n            yielded: false,\n        }\n    }\n}\n\nimpl Stream for FixedSizeStream {\n    type Item = Result<Bytes, std::io::Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        if self.yielded {\n            Poll::Ready(None)\n        } else {\n            self.yielded = true;\n            let data = std::mem::take(&mut self.data);\n            Poll::Ready(Some(Ok(Bytes::from(data))))\n        }\n    }\n}\n\n#[actix_rt::test]\nasync fn test_streaming_response_with_content_length() {\n    let stream = FixedSizeStream::new(100);\n\n    let resp = HttpResponse::Ok()\n        .append_header((header::CONTENT_LENGTH, \"100\"))\n        .streaming(stream);\n\n    assert_eq!(\n        resp.headers().get(header::CONTENT_LENGTH),\n        Some(&HeaderValue::from_static(\"100\")),\n        \"Content-Length should be preserved when explicitly set\"\n    );\n\n    let has_chunked = resp\n        .headers()\n        .get(header::TRANSFER_ENCODING)\n        .map(|v| v.to_str().unwrap_or(\"\"))\n        .unwrap_or(\"\")\n        .contains(\"chunked\");\n\n    assert!(\n        !has_chunked,\n        \"chunked should not be used when Content-Length is provided\"\n    );\n\n    assert_eq!(\n        resp.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"application/octet-stream\")),\n        \"Content-Type should default to application/octet-stream\"\n    );\n}\n\n#[actix_rt::test]\nasync fn test_streaming_response_default_content_type() {\n    let stream = FixedSizeStream::new(50);\n\n    let resp = HttpResponse::Ok().streaming(stream);\n\n    assert_eq!(\n        resp.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"application/octet-stream\")),\n        \"Content-Type should default to application/octet-stream\"\n    );\n}\n\n#[actix_rt::test]\nasync fn test_streaming_response_user_defined_content_type() {\n    let stream = FixedSizeStream::new(25);\n\n    let resp = HttpResponse::Ok()\n        .insert_header((header::CONTENT_TYPE, \"text/plain\"))\n        .streaming(stream);\n\n    assert_eq!(\n        resp.headers().get(header::CONTENT_TYPE),\n        Some(&HeaderValue::from_static(\"text/plain\")),\n        \"User-defined Content-Type should be preserved\"\n    );\n}\n\n#[actix_rt::test]\nasync fn test_streaming_response_empty_stream() {\n    let stream = FixedSizeStream::new(0);\n\n    let resp = HttpResponse::Ok()\n        .append_header((header::CONTENT_LENGTH, \"0\"))\n        .streaming(stream);\n\n    assert_eq!(\n        resp.headers().get(header::CONTENT_LENGTH),\n        Some(&HeaderValue::from_static(\"0\")),\n        \"Content-Length 0 should be preserved for empty streams\"\n    );\n}\n"
  },
  {
    "path": "actix-web/tests/test_weird_poll.rs",
    "content": "//! Regression test for https://github.com/actix/actix-web/issues/1321\n\n// use actix_http::body::{BodyStream, MessageBody};\n// use bytes::Bytes;\n// use futures_channel::oneshot;\n// use futures_util::{\n//     stream::once,\n//     task::{noop_waker, Context},\n// };\n\n// #[test]\n// fn weird_poll() {\n//     let (sender, receiver) = oneshot::channel();\n//     let mut body_stream = Ok(BodyStream::new(once(async {\n//         let x = Box::new(0);\n//         let y = &x;\n//         receiver.await.unwrap();\n//         let _z = **y;\n//         Ok::<_, ()>(Bytes::new())\n//     })));\n\n//     let waker = noop_waker();\n//     let mut cx = Context::from_waker(&waker);\n\n//     let _ = body_stream.as_mut().unwrap().poll_next(&mut cx);\n//     sender.send(()).unwrap();\n//     let _ = std::mem::replace(&mut body_stream, Err([0; 32]))\n//         .unwrap()\n//         .poll_next(&mut cx);\n// }\n"
  },
  {
    "path": "actix-web/tests/utils.rs",
    "content": "// compiling some tests will trigger unused function warnings even though other tests use them\n#![allow(dead_code)]\n\nuse std::io::{Read as _, Write as _};\n\npub mod gzip {\n    use flate2::{read::GzDecoder, write::GzEncoder, Compression};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = GzEncoder::new(Vec::new(), Compression::fast());\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = GzDecoder::new(bytes.as_ref());\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod deflate {\n    use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = ZlibEncoder::new(Vec::new(), Compression::fast());\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = ZlibDecoder::new(bytes.as_ref());\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod brotli {\n    use ::brotli::{reader::Decompressor as BrotliDecoder, CompressorWriter as BrotliEncoder};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = BrotliEncoder::new(\n            Vec::new(),\n            8 * 1024, // 32 KiB buffer\n            3,        // BROTLI_PARAM_QUALITY\n            22,       // BROTLI_PARAM_LGWIN\n        );\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.flush().unwrap();\n        encoder.into_inner()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = BrotliDecoder::new(bytes.as_ref(), 8_096);\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod zstd {\n    use ::zstd::stream::{read::Decoder, write::Encoder};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = Encoder::new(Vec::new(), 3).unwrap();\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = Decoder::new(bytes.as_ref()).unwrap();\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n"
  },
  {
    "path": "actix-web/tests/weird_poll.rs",
    "content": "//! Regression test for https://github.com/actix/actix-web/issues/1321\n\n// use actix_http::body::{BodyStream, MessageBody};\n// use bytes::Bytes;\n// use futures_channel::oneshot;\n// use futures_util::{\n//     stream::once,\n//     task::{noop_waker, Context},\n// };\n\n// #[test]\n// fn weird_poll() {\n//     let (sender, receiver) = oneshot::channel();\n//     let mut body_stream = Ok(BodyStream::new(once(async {\n//         let x = Box::new(0);\n//         let y = &x;\n//         receiver.await.unwrap();\n//         let _z = **y;\n//         Ok::<_, ()>(Bytes::new())\n//     })));\n\n//     let waker = noop_waker();\n//     let mut cx = Context::from_waker(&waker);\n\n//     let _ = body_stream.as_mut().unwrap().poll_next(&mut cx);\n//     sender.send(()).unwrap();\n//     let _ = std::mem::replace(&mut body_stream, Err([0; 32]))\n//         .unwrap()\n//         .poll_next(&mut cx);\n// }\n"
  },
  {
    "path": "actix-web-actors/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n## 4.3.1 <!-- v4.3.1+deprecated -->\n\n- Reduce memory usage by `take`-ing (rather than `split`-ing) the encoded buffer when yielding bytes in the response stream.\n- Mark crate as deprecated.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 4.3.0\n\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 4.2.0\n\n- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.\n\n## 4.1.0\n\n- Add support for `actix` version `0.13`. [#2675]\n\n[#2675]: https://github.com/actix/actix-web/pull/2675\n\n## 4.0.0\n\n- No significant changes since `4.0.0-beta.12`.\n\n## 4.0.0-beta.12\n\n- No significant changes since `4.0.0-beta.11`.\n\n## 4.0.0-beta.11\n\n- No significant changes since `4.0.0-beta.10`.\n\n## 4.0.0-beta.10\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 4.0.0-beta.9\n\n- No significant changes since `4.0.0-beta.8`.\n\n## 4.0.0-beta.8\n\n- Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920]\n- Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n[#1920]: https://github.com/actix/actix-web/pull/1920\n\n## 4.0.0-beta.7\n\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n## 4.0.0-beta.6\n\n- Update `actix` to `0.12`. [#2277]\n\n[#2277]: https://github.com/actix/actix-web/pull/2277\n\n## 4.0.0-beta.5\n\n- No notable changes.\n\n## 4.0.0-beta.4\n\n- No notable changes.\n\n## 4.0.0-beta.3\n\n- No notable changes.\n\n## 4.0.0-beta.2\n\n- No notable changes.\n\n## 4.0.0-beta.1\n\n- Update `pin-project` to `1.0`.\n- Update `bytes` to `1.0`. [#1813]\n- `WebsocketContext::text` now takes an `Into<bytestring::ByteString>`. [#1864]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1864]: https://github.com/actix/actix-web/pull/1864\n\n## 3.0.0\n\n- No significant changes from `3.0.0-beta.2`.\n\n## 3.0.0-beta.2\n\n- Update `actix-*` dependencies to latest versions.\n\n## 3.0.0-beta.1\n\n- Update `actix-web` & `actix-http` dependencies to beta.1\n- Bump minimum supported Rust version to 1.40\n\n## 3.0.0-alpha.1\n\n- Update the actix-web dependency to 3.0.0-alpha.1\n- Update the actix dependency to 0.10.0-alpha.2\n- Update the actix-http dependency to 2.0.0-alpha.3\n\n## 2.0.0\n\n- Release\n\n## 2.0.0-alpha.1\n\n- Migrate to actix-web 2.0.0\n\n## 1.0.4\n\n- Allow comma-separated websocket subprotocols without spaces (#1172)\n\n## 1.0.3\n\n- Update actix-web and actix-http dependencies\n\n## 1.0.2\n\n- Add `ws::start_with_addr()`, returning the address of the created actor, along with the `HttpResponse`.\n\n- Add support for specifying protocols on websocket handshake #835\n\n## 1.0.1\n\n- Allow to use custom ws codec with `WebsocketContext` #925\n\n## 1.0.0\n\n- Update actix-http and actix-web\n\n## 0.1.0-alpha.3\n\n- Update actix-http and actix-web\n\n## 0.1.0-alpha.2\n\n- Update actix-http and actix-web\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "actix-web-actors/Cargo.toml",
    "content": "[package]\nname = \"actix-web-actors\"\nversion = \"4.3.1+deprecated\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\"]\ndescription = \"Actix actors support for Actix Web\"\nkeywords = [\"actix\", \"http\", \"web\", \"framework\", \"async\"]\nhomepage.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n  \"actix::*\",\n  \"actix_http::*\",\n  \"actix_web::*\",\n  \"bytes::*\",\n  \"bytestring::*\",\n  \"futures_core::*\",\n]\n\n[dependencies]\nactix = { version = \">=0.12, <0.14\", default-features = false }\nactix-codec = \"0.5\"\nactix-http = \"3\"\nactix-web = { version = \"4\", default-features = false, features = [\"ws\"] }\n\nbytes = \"1\"\nbytestring = \"1\"\nfutures-core = { version = \"0.3.17\", default-features = false }\npin-project-lite = \"0.2\"\ntokio = { version = \"1.38.2\", features = [\"sync\"] }\ntokio-util = { version = \"0.7\", features = [\"codec\"] }\n\n[dev-dependencies]\nactix-rt = \"2.2\"\nactix-test = \"0.1\"\nactix-web = { version = \"4\", features = [\"macros\"] }\nawc = { version = \"3\", default-features = false }\n\nenv_logger = \"0.11\"\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"std\"] }\nmime = \"0.3\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-web-actors/README.md",
    "content": "# `actix-web-actors`\n\n> Actix actors support for Actix Web.\n>\n> This crate is deprecated. Migrate to [`actix-ws`](https://crates.io/crates/actix-ws).\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors)\n[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.3.1)](https://docs.rs/actix-web-actors/4.3.1)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![License](https://img.shields.io/crates/l/actix-web-actors.svg)\n<br />\n![maintenance-status](https://img.shields.io/badge/maintenance-deprecated-red.svg)\n[![Download](https://img.shields.io/crates/d/actix-web-actors.svg)](https://crates.io/crates/actix-web-actors)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": "actix-web-actors/src/context.rs",
    "content": "use std::{\n    collections::VecDeque,\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix::{\n    dev::{AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, ToEnvelope},\n    fut::ActorFuture,\n    Actor, ActorContext, ActorState, Addr, AsyncContext, Handler, Message, SpawnHandle,\n};\nuse actix_web::error::Error;\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse tokio::sync::oneshot::Sender;\n\n/// Execution context for HTTP actors\n///\n/// # Example\n///\n/// A demonstration of [server-sent events](https://developer.mozilla.org/docs/Web/API/Server-sent_events) using actors:\n///\n/// ```no_run\n/// use std::time::Duration;\n///\n/// use actix::{Actor, AsyncContext};\n/// use actix_web::{get, http::header, App, HttpResponse, HttpServer};\n/// use actix_web_actors::HttpContext;\n/// use bytes::Bytes;\n///\n/// struct MyActor {\n///     count: usize,\n/// }\n///\n/// impl Actor for MyActor {\n///     type Context = HttpContext<Self>;\n///\n///     fn started(&mut self, ctx: &mut Self::Context) {\n///         ctx.run_later(Duration::from_millis(100), Self::write);\n///     }\n/// }\n///\n/// impl MyActor {\n///     fn write(&mut self, ctx: &mut HttpContext<Self>) {\n///         self.count += 1;\n///         if self.count > 3 {\n///             ctx.write_eof()\n///         } else {\n///             ctx.write(Bytes::from(format!(\"event: count\\ndata: {}\\n\\n\", self.count)));\n///             ctx.run_later(Duration::from_millis(100), Self::write);\n///         }\n///     }\n/// }\n///\n/// #[get(\"/\")]\n/// async fn index() -> HttpResponse {\n///     HttpResponse::Ok()\n///         .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM))\n///         .streaming(HttpContext::create(MyActor { count: 0 }))\n/// }\n///\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     HttpServer::new(|| App::new().service(index))\n///         .bind((\"127.0.0.1\", 8080))?\n///         .run()\n///         .await\n/// }\n/// ```\npub struct HttpContext<A>\nwhere\n    A: Actor<Context = HttpContext<A>>,\n{\n    inner: ContextParts<A>,\n    stream: VecDeque<Option<Bytes>>,\n}\n\nimpl<A> ActorContext for HttpContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    fn stop(&mut self) {\n        self.inner.stop();\n    }\n    fn terminate(&mut self) {\n        self.inner.terminate()\n    }\n    fn state(&self) -> ActorState {\n        self.inner.state()\n    }\n}\n\nimpl<A> AsyncContext<A> for HttpContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    #[inline]\n    fn spawn<F>(&mut self, fut: F) -> SpawnHandle\n    where\n        F: ActorFuture<A, Output = ()> + 'static,\n    {\n        self.inner.spawn(fut)\n    }\n\n    #[inline]\n    fn wait<F>(&mut self, fut: F)\n    where\n        F: ActorFuture<A, Output = ()> + 'static,\n    {\n        self.inner.wait(fut)\n    }\n\n    #[doc(hidden)]\n    #[inline]\n    fn waiting(&self) -> bool {\n        self.inner.waiting()\n            || self.inner.state() == ActorState::Stopping\n            || self.inner.state() == ActorState::Stopped\n    }\n\n    #[inline]\n    fn cancel_future(&mut self, handle: SpawnHandle) -> bool {\n        self.inner.cancel_future(handle)\n    }\n\n    #[inline]\n    fn address(&self) -> Addr<A> {\n        self.inner.address()\n    }\n}\n\nimpl<A> HttpContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    #[inline]\n    /// Create a new HTTP Context from a request and an actor\n    pub fn create(actor: A) -> impl Stream<Item = Result<Bytes, Error>> {\n        let mb = Mailbox::default();\n        let ctx = HttpContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            stream: VecDeque::new(),\n        };\n        HttpContextFut::new(ctx, actor, mb)\n    }\n\n    /// Create a new HTTP Context\n    pub fn with_factory<F>(f: F) -> impl Stream<Item = Result<Bytes, Error>>\n    where\n        F: FnOnce(&mut Self) -> A + 'static,\n    {\n        let mb = Mailbox::default();\n        let mut ctx = HttpContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            stream: VecDeque::new(),\n        };\n\n        let act = f(&mut ctx);\n        HttpContextFut::new(ctx, act, mb)\n    }\n}\n\nimpl<A> HttpContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    /// Write payload\n    #[inline]\n    pub fn write(&mut self, data: Bytes) {\n        self.stream.push_back(Some(data));\n    }\n\n    /// Indicate end of streaming payload. Also this method calls `Self::close`.\n    #[inline]\n    pub fn write_eof(&mut self) {\n        self.stream.push_back(None);\n    }\n\n    /// Handle of the running future\n    ///\n    /// SpawnHandle is the handle returned by `AsyncContext::spawn()` method.\n    pub fn handle(&self) -> SpawnHandle {\n        self.inner.curr_handle()\n    }\n}\n\nimpl<A> AsyncContextParts<A> for HttpContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    fn parts(&mut self) -> &mut ContextParts<A> {\n        &mut self.inner\n    }\n}\n\nstruct HttpContextFut<A>\nwhere\n    A: Actor<Context = HttpContext<A>>,\n{\n    fut: ContextFut<A, HttpContext<A>>,\n}\n\nimpl<A> HttpContextFut<A>\nwhere\n    A: Actor<Context = HttpContext<A>>,\n{\n    fn new(ctx: HttpContext<A>, act: A, mailbox: Mailbox<A>) -> Self {\n        let fut = ContextFut::new(ctx, act, mailbox);\n        HttpContextFut { fut }\n    }\n}\n\nimpl<A> Stream for HttpContextFut<A>\nwhere\n    A: Actor<Context = HttpContext<A>>,\n{\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        if self.fut.alive() {\n            let _ = Pin::new(&mut self.fut).poll(cx);\n        }\n\n        // frames\n        if let Some(data) = self.fut.ctx().stream.pop_front() {\n            Poll::Ready(data.map(Ok))\n        } else if self.fut.alive() {\n            Poll::Pending\n        } else {\n            Poll::Ready(None)\n        }\n    }\n}\n\nimpl<A, M> ToEnvelope<A, M> for HttpContext<A>\nwhere\n    A: Actor<Context = HttpContext<A>> + Handler<M>,\n    M: Message + Send + 'static,\n    M::Result: Send,\n{\n    fn pack(msg: M, tx: Option<Sender<M::Result>>) -> Envelope<A> {\n        Envelope::new(msg, tx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use actix_web::{\n        http::StatusCode,\n        test::{call_service, init_service, read_body, TestRequest},\n        web, App, HttpResponse,\n    };\n\n    use super::*;\n\n    struct MyActor {\n        count: usize,\n    }\n\n    impl Actor for MyActor {\n        type Context = HttpContext<Self>;\n\n        fn started(&mut self, ctx: &mut Self::Context) {\n            ctx.run_later(Duration::from_millis(100), Self::write);\n        }\n    }\n\n    impl MyActor {\n        fn write(&mut self, ctx: &mut HttpContext<Self>) {\n            self.count += 1;\n            if self.count > 3 {\n                ctx.write_eof()\n            } else {\n                ctx.write(Bytes::from(format!(\"LINE-{}\", self.count)));\n                ctx.run_later(Duration::from_millis(100), Self::write);\n            }\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_default_resource() {\n        let srv = init_service(App::new().service(web::resource(\"/test\").to(|| async {\n            HttpResponse::Ok().streaming(HttpContext::create(MyActor { count: 0 }))\n        })))\n        .await;\n\n        let req = TestRequest::with_uri(\"/test\").to_request();\n        let resp = call_service(&srv, req).await;\n        assert_eq!(resp.status(), StatusCode::OK);\n\n        let body = read_body(resp).await;\n        assert_eq!(body, Bytes::from_static(b\"LINE-1LINE-2LINE-3\"));\n    }\n}\n"
  },
  {
    "path": "actix-web-actors/src/lib.rs",
    "content": "//! Actix actors support for Actix Web.\n//!\n//! This crate is deprecated. Migrate to [`actix-ws`](https://crates.io/crates/actix-ws).\n//!\n//! # Examples\n//!\n//! ```no_run\n//! use actix::{Actor, StreamHandler};\n//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};\n//! use actix_web_actors::ws;\n//!\n//! /// Define Websocket actor\n//! struct MyWs;\n//!\n//! impl Actor for MyWs {\n//!     type Context = ws::WebsocketContext<Self>;\n//! }\n//!\n//! /// Handler for ws::Message message\n//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {\n//!     fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {\n//!         match msg {\n//!             Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),\n//!             Ok(ws::Message::Text(text)) => ctx.text(text),\n//!             Ok(ws::Message::Binary(bin)) => ctx.binary(bin),\n//!             _ => (),\n//!         }\n//!     }\n//! }\n//!\n//! #[get(\"/ws\")]\n//! async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {\n//!     ws::start(MyWs, &req, stream)\n//! }\n//!\n//! #[actix_web::main]\n//! async fn main() -> std::io::Result<()> {\n//!     HttpServer::new(|| App::new().service(index))\n//!         .bind((\"127.0.0.1\", 8080))?\n//!         .run()\n//!         .await\n//! }\n//! ```\n//!\n//! # Documentation & Community Resources\n//! In addition to this API documentation, several other resources are available:\n//!\n//! * [Website & User Guide](https://actix.rs/)\n//! * [Documentation for `actix_web`](actix_web)\n//! * [Examples Repository](https://github.com/actix/examples)\n//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x)\n//!\n//! To get started navigating the API docs, you may consider looking at the following pages first:\n//!\n//! * [`ws`]: This module provides actor support for WebSockets.\n//!\n//! * [`HttpContext`]: This struct provides actor support for streaming HTTP responses.\n//!\n\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\nmod context;\npub mod ws;\n\npub use self::context::HttpContext;\n"
  },
  {
    "path": "actix-web-actors/src/ws.rs",
    "content": "//! Websocket integration.\n//!\n//! # Examples\n//!\n//! ```no_run\n//! use actix::{Actor, StreamHandler};\n//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};\n//! use actix_web_actors::ws;\n//!\n//! /// Define Websocket actor\n//! struct MyWs;\n//!\n//! impl Actor for MyWs {\n//!     type Context = ws::WebsocketContext<Self>;\n//! }\n//!\n//! /// Handler for ws::Message message\n//! impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {\n//!     fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {\n//!         match msg {\n//!             Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),\n//!             Ok(ws::Message::Text(text)) => ctx.text(text),\n//!             Ok(ws::Message::Binary(bin)) => ctx.binary(bin),\n//!             _ => (),\n//!         }\n//!     }\n//! }\n//!\n//! #[get(\"/ws\")]\n//! async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {\n//!     ws::start(MyWs, &req, stream)\n//! }\n//!\n//! const MAX_FRAME_SIZE: usize = 16_384; // 16KiB\n//!\n//! #[get(\"/custom-ws\")]\n//! async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {\n//!     // Create a Websocket session with a specific max frame size, and protocols.\n//!     ws::WsResponseBuilder::new(MyWs, &req, stream)\n//!         .frame_size(MAX_FRAME_SIZE)\n//!         .protocols(&[\"A\", \"B\"])\n//!         .start()\n//! }\n//!\n//! #[actix_web::main]\n//! async fn main() -> std::io::Result<()> {\n//!     HttpServer::new(|| {\n//!             App::new()\n//!                 .service(websocket)\n//!                 .service(custom_websocket)\n//!         })\n//!         .bind((\"127.0.0.1\", 8080))?\n//!         .run()\n//!         .await\n//! }\n//! ```\n//!\n\nuse std::{\n    collections::VecDeque,\n    future::Future,\n    io, mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix::{\n    dev::{\n        AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, StreamHandler, ToEnvelope,\n    },\n    fut::ActorFuture,\n    Actor, ActorContext, ActorState, Addr, AsyncContext, Handler, Message as ActixMessage,\n    SpawnHandle,\n};\nuse actix_http::ws::{hash_key, Codec};\npub use actix_http::ws::{CloseCode, CloseReason, Frame, HandshakeError, Message, ProtocolError};\nuse actix_web::{\n    error::{Error, PayloadError},\n    http::{\n        header::{self, HeaderValue},\n        Method, StatusCode,\n    },\n    HttpRequest, HttpResponse, HttpResponseBuilder,\n};\nuse bytes::{Bytes, BytesMut};\nuse bytestring::ByteString;\nuse futures_core::Stream;\nuse pin_project_lite::pin_project;\nuse tokio::sync::oneshot;\nuse tokio_util::codec::{Decoder as _, Encoder as _};\n\n/// Builder for Websocket session response.\n///\n/// # Examples\n///\n/// ```no_run\n/// # use actix::{Actor, StreamHandler};\n/// # use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer};\n/// # use actix_web_actors::ws;\n/// #\n/// # struct MyWs;\n/// #\n/// # impl Actor for MyWs {\n/// #     type Context = ws::WebsocketContext<Self>;\n/// # }\n/// #\n/// # /// Handler for ws::Message message\n/// # impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {\n/// #     fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {}\n/// # }\n/// #\n/// #[get(\"/ws\")]\n/// async fn websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {\n///     ws::WsResponseBuilder::new(MyWs, &req, stream).start()\n/// }\n///\n/// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB\n///\n/// #[get(\"/custom-ws\")]\n/// async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {\n///     // Create a Websocket session with a specific max frame size, codec, and protocols.\n///     ws::WsResponseBuilder::new(MyWs, &req, stream)\n///         .codec(actix_http::ws::Codec::new())\n///         // This will overwrite the codec's max frame-size\n///         .frame_size(MAX_FRAME_SIZE)\n///         .protocols(&[\"A\", \"B\"])\n///         .start()\n/// }\n/// #\n/// # #[actix_web::main]\n/// # async fn main() -> std::io::Result<()> {\n/// #     HttpServer::new(|| {\n/// #             App::new()\n/// #                 .service(websocket)\n/// #                 .service(custom_websocket)\n/// #         })\n/// #         .bind((\"127.0.0.1\", 8080))?\n/// #         .run()\n/// #         .await\n/// # }\n/// ```\npub struct WsResponseBuilder<'a, A, T>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + StreamHandler<Result<Message, ProtocolError>>,\n    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n{\n    actor: A,\n    req: &'a HttpRequest,\n    stream: T,\n    codec: Option<Codec>,\n    protocols: Option<&'a [&'a str]>,\n    frame_size: Option<usize>,\n}\n\nimpl<'a, A, T> WsResponseBuilder<'a, A, T>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + StreamHandler<Result<Message, ProtocolError>>,\n    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n{\n    /// Construct a new `WsResponseBuilder` with actor, request, and payload stream.\n    ///\n    /// For usage example, see docs on [`WsResponseBuilder`] struct.\n    pub fn new(actor: A, req: &'a HttpRequest, stream: T) -> Self {\n        WsResponseBuilder {\n            actor,\n            req,\n            stream,\n            codec: None,\n            protocols: None,\n            frame_size: None,\n        }\n    }\n\n    /// Set the protocols for the session.\n    pub fn protocols(mut self, protocols: &'a [&'a str]) -> Self {\n        self.protocols = Some(protocols);\n        self\n    }\n\n    /// Set the max frame size for each message (in bytes).\n    ///\n    /// **Note**: This will override any given [`Codec`]'s max frame size.\n    pub fn frame_size(mut self, frame_size: usize) -> Self {\n        self.frame_size = Some(frame_size);\n        self\n    }\n\n    /// Set the [`Codec`] for the session. If [`Self::frame_size`] is also set, the given\n    /// [`Codec`]'s max frame size will be overridden.\n    pub fn codec(mut self, codec: Codec) -> Self {\n        self.codec = Some(codec);\n        self\n    }\n\n    fn handshake_resp(&self) -> Result<HttpResponseBuilder, HandshakeError> {\n        match self.protocols {\n            Some(protocols) => handshake_with_protocols(self.req, protocols),\n            None => handshake(self.req),\n        }\n    }\n\n    fn set_frame_size(&mut self) {\n        if let Some(frame_size) = self.frame_size {\n            match &mut self.codec {\n                Some(codec) => {\n                    // modify existing codec's max frame size\n                    let orig_codec = mem::take(codec);\n                    *codec = orig_codec.max_size(frame_size);\n                }\n\n                None => {\n                    // create a new codec with the given size\n                    self.codec = Some(Codec::new().max_size(frame_size));\n                }\n            }\n        }\n    }\n\n    /// Create a new Websocket context from an actor, request stream, and codec.\n    ///\n    /// Returns a pair, where the first item is an addr for the created actor, and the second item\n    /// is a stream intended to be set as part of the response\n    /// via [`HttpResponseBuilder::streaming()`].\n    fn create_with_codec_addr<S>(\n        actor: A,\n        stream: S,\n        codec: Codec,\n    ) -> (Addr<A>, impl Stream<Item = Result<Bytes, Error>>)\n    where\n        A: StreamHandler<Result<Message, ProtocolError>>,\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        let mb = Mailbox::default();\n        let mut ctx = WebsocketContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            messages: VecDeque::new(),\n        };\n        ctx.add_stream(WsStream::new(stream, codec.clone()));\n\n        let addr = ctx.address();\n\n        (addr, WebsocketContextFut::new(ctx, actor, mb, codec))\n    }\n\n    /// Perform WebSocket handshake and start actor.\n    ///\n    /// `req` is an [`HttpRequest`] that should be requesting a websocket protocol change.\n    /// `stream` should be a [`Bytes`] stream (such as `actix_web::web::Payload`) that contains a\n    /// stream of the body request.\n    ///\n    /// If there is a problem with the handshake, an error is returned.\n    ///\n    /// If successful, consume the [`WsResponseBuilder`] and return a [`HttpResponse`] wrapped in\n    /// a [`Result`].\n    pub fn start(mut self) -> Result<HttpResponse, Error> {\n        let mut res = self.handshake_resp()?;\n        self.set_frame_size();\n\n        match self.codec {\n            Some(codec) => {\n                let out_stream = WebsocketContext::with_codec(self.actor, self.stream, codec);\n                Ok(res.streaming(out_stream))\n            }\n            None => {\n                let out_stream = WebsocketContext::create(self.actor, self.stream);\n                Ok(res.streaming(out_stream))\n            }\n        }\n    }\n\n    /// Perform WebSocket handshake and start actor.\n    ///\n    /// `req` is an [`HttpRequest`] that should be requesting a websocket protocol change.\n    /// `stream` should be a [`Bytes`] stream (such as `actix_web::web::Payload`) that contains a\n    /// stream of the body request.\n    ///\n    /// If there is a problem with the handshake, an error is returned.\n    ///\n    /// If successful, returns a pair where the first item is an address for the created actor and\n    /// the second item is the [`HttpResponse`] that should be returned from the websocket request.\n    pub fn start_with_addr(mut self) -> Result<(Addr<A>, HttpResponse), Error> {\n        let mut res = self.handshake_resp()?;\n        self.set_frame_size();\n\n        match self.codec {\n            Some(codec) => {\n                let (addr, out_stream) =\n                    Self::create_with_codec_addr(self.actor, self.stream, codec);\n                Ok((addr, res.streaming(out_stream)))\n            }\n            None => {\n                let (addr, out_stream) =\n                    WebsocketContext::create_with_addr(self.actor, self.stream);\n                Ok((addr, res.streaming(out_stream)))\n            }\n        }\n    }\n}\n\n/// Perform WebSocket handshake and start actor.\n///\n/// To customize options, see [`WsResponseBuilder`].\npub fn start<A, T>(actor: A, req: &HttpRequest, stream: T) -> Result<HttpResponse, Error>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + StreamHandler<Result<Message, ProtocolError>>,\n    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n{\n    let mut res = handshake(req)?;\n    Ok(res.streaming(WebsocketContext::create(actor, stream)))\n}\n\n/// Perform WebSocket handshake and start actor.\n///\n/// `req` is an HTTP Request that should be requesting a websocket protocol change. `stream` should\n/// be a `Bytes` stream (such as `actix_web::web::Payload`) that contains a stream of the\n/// body request.\n///\n/// If there is a problem with the handshake, an error is returned.\n///\n/// If successful, returns a pair where the first item is an address for the created actor and the\n/// second item is the response that should be returned from the WebSocket request.\n#[deprecated(since = \"4.0.0\", note = \"Prefer `WsResponseBuilder::start_with_addr`.\")]\npub fn start_with_addr<A, T>(\n    actor: A,\n    req: &HttpRequest,\n    stream: T,\n) -> Result<(Addr<A>, HttpResponse), Error>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + StreamHandler<Result<Message, ProtocolError>>,\n    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n{\n    let mut res = handshake(req)?;\n    let (addr, out_stream) = WebsocketContext::create_with_addr(actor, stream);\n    Ok((addr, res.streaming(out_stream)))\n}\n\n/// Do WebSocket handshake and start ws actor.\n///\n/// `protocols` is a sequence of known protocols.\n#[deprecated(\n    since = \"4.0.0\",\n    note = \"Prefer `WsResponseBuilder` for setting protocols.\"\n)]\npub fn start_with_protocols<A, T>(\n    actor: A,\n    protocols: &[&str],\n    req: &HttpRequest,\n    stream: T,\n) -> Result<HttpResponse, Error>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + StreamHandler<Result<Message, ProtocolError>>,\n    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n{\n    let mut res = handshake_with_protocols(req, protocols)?;\n    Ok(res.streaming(WebsocketContext::create(actor, stream)))\n}\n\n/// Prepare WebSocket handshake response.\n///\n/// This function returns handshake `HttpResponse`, ready to send to peer. It does not perform\n/// any IO.\npub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeError> {\n    handshake_with_protocols(req, &[])\n}\n\n/// Prepare WebSocket handshake response.\n///\n/// This function returns handshake `HttpResponse`, ready to send to peer. It does not perform\n/// any IO.\n///\n/// `protocols` is a sequence of known protocols. On successful handshake, the returned response\n/// headers contain the first protocol in this list which the server also knows.\npub fn handshake_with_protocols(\n    req: &HttpRequest,\n    protocols: &[&str],\n) -> Result<HttpResponseBuilder, HandshakeError> {\n    // WebSocket accepts only GET\n    if *req.method() != Method::GET {\n        return Err(HandshakeError::GetMethodRequired);\n    }\n\n    // check for \"UPGRADE\" to WebSocket header\n    let has_hdr = if let Some(hdr) = req.headers().get(&header::UPGRADE) {\n        if let Ok(s) = hdr.to_str() {\n            s.to_ascii_lowercase().contains(\"websocket\")\n        } else {\n            false\n        }\n    } else {\n        false\n    };\n    if !has_hdr {\n        return Err(HandshakeError::NoWebsocketUpgrade);\n    }\n\n    // Upgrade connection\n    if !req.head().upgrade() {\n        return Err(HandshakeError::NoConnectionUpgrade);\n    }\n\n    // check supported version\n    if !req.headers().contains_key(&header::SEC_WEBSOCKET_VERSION) {\n        return Err(HandshakeError::NoVersionHeader);\n    }\n    let supported_ver = {\n        if let Some(hdr) = req.headers().get(&header::SEC_WEBSOCKET_VERSION) {\n            hdr == \"13\" || hdr == \"8\" || hdr == \"7\"\n        } else {\n            false\n        }\n    };\n    if !supported_ver {\n        return Err(HandshakeError::UnsupportedVersion);\n    }\n\n    // check client handshake for validity\n    if !req.headers().contains_key(&header::SEC_WEBSOCKET_KEY) {\n        return Err(HandshakeError::BadWebsocketKey);\n    }\n    let key = {\n        let key = req.headers().get(&header::SEC_WEBSOCKET_KEY).unwrap();\n        hash_key(key.as_ref())\n    };\n\n    // check requested protocols\n    let protocol = req\n        .headers()\n        .get(&header::SEC_WEBSOCKET_PROTOCOL)\n        .and_then(|req_protocols| {\n            let req_protocols = req_protocols.to_str().ok()?;\n            req_protocols\n                .split(',')\n                .map(|req_p| req_p.trim())\n                .find(|req_p| protocols.iter().any(|p| p == req_p))\n        });\n\n    let mut response = HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)\n        .upgrade(\"websocket\")\n        .insert_header((\n            header::SEC_WEBSOCKET_ACCEPT,\n            // key is known to be header value safe ascii\n            HeaderValue::from_bytes(&key).unwrap(),\n        ))\n        .take();\n\n    if let Some(protocol) = protocol {\n        response.insert_header((header::SEC_WEBSOCKET_PROTOCOL, protocol));\n    }\n\n    Ok(response)\n}\n\n/// Execution context for `WebSockets` actors\npub struct WebsocketContext<A>\nwhere\n    A: Actor<Context = WebsocketContext<A>>,\n{\n    inner: ContextParts<A>,\n    messages: VecDeque<Option<Message>>,\n}\n\nimpl<A> ActorContext for WebsocketContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    fn stop(&mut self) {\n        self.inner.stop();\n    }\n\n    fn terminate(&mut self) {\n        self.inner.terminate()\n    }\n\n    fn state(&self) -> ActorState {\n        self.inner.state()\n    }\n}\n\nimpl<A> AsyncContext<A> for WebsocketContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    fn spawn<F>(&mut self, fut: F) -> SpawnHandle\n    where\n        F: ActorFuture<A, Output = ()> + 'static,\n    {\n        self.inner.spawn(fut)\n    }\n\n    fn wait<F>(&mut self, fut: F)\n    where\n        F: ActorFuture<A, Output = ()> + 'static,\n    {\n        self.inner.wait(fut)\n    }\n\n    #[doc(hidden)]\n    #[inline]\n    fn waiting(&self) -> bool {\n        self.inner.waiting()\n            || self.inner.state() == ActorState::Stopping\n            || self.inner.state() == ActorState::Stopped\n    }\n\n    fn cancel_future(&mut self, handle: SpawnHandle) -> bool {\n        self.inner.cancel_future(handle)\n    }\n\n    #[inline]\n    fn address(&self) -> Addr<A> {\n        self.inner.address()\n    }\n}\n\nimpl<A> WebsocketContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    /// Create a new Websocket context from a request and an actor.\n    #[inline]\n    pub fn create<S>(actor: A, stream: S) -> impl Stream<Item = Result<Bytes, Error>>\n    where\n        A: StreamHandler<Result<Message, ProtocolError>>,\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        let (_, stream) = WebsocketContext::create_with_addr(actor, stream);\n        stream\n    }\n\n    /// Create a new Websocket context from a request and an actor.\n    ///\n    /// Returns a pair, where the first item is an addr for the created actor, and the second item\n    /// is a stream intended to be set as part of the response\n    /// via [`HttpResponseBuilder::streaming()`].\n    pub fn create_with_addr<S>(\n        actor: A,\n        stream: S,\n    ) -> (Addr<A>, impl Stream<Item = Result<Bytes, Error>>)\n    where\n        A: StreamHandler<Result<Message, ProtocolError>>,\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        let mb = Mailbox::default();\n        let mut ctx = WebsocketContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            messages: VecDeque::new(),\n        };\n        ctx.add_stream(WsStream::new(stream, Codec::new()));\n\n        let addr = ctx.address();\n\n        (addr, WebsocketContextFut::new(ctx, actor, mb, Codec::new()))\n    }\n\n    /// Create a new Websocket context from a request, an actor, and a codec\n    pub fn with_codec<S>(\n        actor: A,\n        stream: S,\n        codec: Codec,\n    ) -> impl Stream<Item = Result<Bytes, Error>>\n    where\n        A: StreamHandler<Result<Message, ProtocolError>>,\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        let mb = Mailbox::default();\n        let mut ctx = WebsocketContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            messages: VecDeque::new(),\n        };\n        ctx.add_stream(WsStream::new(stream, codec.clone()));\n\n        WebsocketContextFut::new(ctx, actor, mb, codec)\n    }\n\n    /// Create a new Websocket context\n    pub fn with_factory<S, F>(stream: S, f: F) -> impl Stream<Item = Result<Bytes, Error>>\n    where\n        F: FnOnce(&mut Self) -> A + 'static,\n        A: StreamHandler<Result<Message, ProtocolError>>,\n        S: Stream<Item = Result<Bytes, PayloadError>> + 'static,\n    {\n        let mb = Mailbox::default();\n        let mut ctx = WebsocketContext {\n            inner: ContextParts::new(mb.sender_producer()),\n            messages: VecDeque::new(),\n        };\n        ctx.add_stream(WsStream::new(stream, Codec::new()));\n\n        let act = f(&mut ctx);\n\n        WebsocketContextFut::new(ctx, act, mb, Codec::new())\n    }\n}\n\nimpl<A> WebsocketContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    /// Write payload\n    ///\n    /// This is a low-level function that accepts framed messages that should\n    /// be created using `Frame::message()`. If you want to send text or binary\n    /// data you should prefer the `text()` or `binary()` convenience functions\n    /// that handle the framing for you.\n    #[inline]\n    pub fn write_raw(&mut self, msg: Message) {\n        self.messages.push_back(Some(msg));\n    }\n\n    /// Send text frame\n    #[inline]\n    pub fn text(&mut self, text: impl Into<ByteString>) {\n        self.write_raw(Message::Text(text.into()));\n    }\n\n    /// Send binary frame\n    #[inline]\n    pub fn binary(&mut self, data: impl Into<Bytes>) {\n        self.write_raw(Message::Binary(data.into()));\n    }\n\n    /// Send ping frame\n    #[inline]\n    pub fn ping(&mut self, message: &[u8]) {\n        self.write_raw(Message::Ping(Bytes::copy_from_slice(message)));\n    }\n\n    /// Send pong frame\n    #[inline]\n    pub fn pong(&mut self, message: &[u8]) {\n        self.write_raw(Message::Pong(Bytes::copy_from_slice(message)));\n    }\n\n    /// Send close frame\n    #[inline]\n    pub fn close(&mut self, reason: Option<CloseReason>) {\n        self.write_raw(Message::Close(reason));\n    }\n\n    /// Handle of the running future\n    ///\n    /// SpawnHandle is the handle returned by `AsyncContext::spawn()` method.\n    pub fn handle(&self) -> SpawnHandle {\n        self.inner.curr_handle()\n    }\n\n    /// Set mailbox capacity\n    ///\n    /// By default mailbox capacity is 16 messages.\n    pub fn set_mailbox_capacity(&mut self, cap: usize) {\n        self.inner.set_mailbox_capacity(cap)\n    }\n}\n\nimpl<A> AsyncContextParts<A> for WebsocketContext<A>\nwhere\n    A: Actor<Context = Self>,\n{\n    fn parts(&mut self) -> &mut ContextParts<A> {\n        &mut self.inner\n    }\n}\n\nstruct WebsocketContextFut<A>\nwhere\n    A: Actor<Context = WebsocketContext<A>>,\n{\n    fut: ContextFut<A, WebsocketContext<A>>,\n    encoder: Codec,\n    buf: BytesMut,\n    closed: bool,\n}\n\nimpl<A> WebsocketContextFut<A>\nwhere\n    A: Actor<Context = WebsocketContext<A>>,\n{\n    fn new(ctx: WebsocketContext<A>, act: A, mailbox: Mailbox<A>, codec: Codec) -> Self {\n        let fut = ContextFut::new(ctx, act, mailbox);\n        WebsocketContextFut {\n            fut,\n            encoder: codec,\n            buf: BytesMut::new(),\n            closed: false,\n        }\n    }\n}\n\nimpl<A> Stream for WebsocketContextFut<A>\nwhere\n    A: Actor<Context = WebsocketContext<A>>,\n{\n    type Item = Result<Bytes, Error>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.get_mut();\n\n        if this.fut.alive() {\n            let _ = Pin::new(&mut this.fut).poll(cx);\n        }\n\n        // encode messages\n        while let Some(item) = this.fut.ctx().messages.pop_front() {\n            if let Some(msg) = item {\n                this.encoder.encode(msg, &mut this.buf)?;\n            } else {\n                this.closed = true;\n                break;\n            }\n        }\n\n        if !this.buf.is_empty() {\n            Poll::Ready(Some(Ok(std::mem::take(&mut this.buf).freeze())))\n        } else if this.fut.alive() && !this.closed {\n            Poll::Pending\n        } else {\n            Poll::Ready(None)\n        }\n    }\n}\n\nimpl<A, M> ToEnvelope<A, M> for WebsocketContext<A>\nwhere\n    A: Actor<Context = WebsocketContext<A>> + Handler<M>,\n    M: ActixMessage + Send + 'static,\n    M::Result: Send,\n{\n    fn pack(msg: M, tx: Option<oneshot::Sender<M::Result>>) -> Envelope<A> {\n        Envelope::new(msg, tx)\n    }\n}\n\npin_project! {\n    #[derive(Debug)]\n    struct WsStream<S> {\n        #[pin]\n        stream: S,\n        decoder: Codec,\n        buf: BytesMut,\n        closed: bool,\n    }\n}\n\nimpl<S> WsStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    fn new(stream: S, codec: Codec) -> Self {\n        Self {\n            stream,\n            decoder: codec,\n            buf: BytesMut::new(),\n            closed: false,\n        }\n    }\n}\n\nimpl<S> Stream for WsStream<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    type Item = Result<Message, ProtocolError>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let mut this = self.as_mut().project();\n\n        if !*this.closed {\n            loop {\n                match Pin::new(&mut this.stream).poll_next(cx) {\n                    Poll::Ready(Some(Ok(chunk))) => {\n                        this.buf.extend_from_slice(&chunk[..]);\n                    }\n                    Poll::Ready(None) => {\n                        *this.closed = true;\n                        break;\n                    }\n                    Poll::Pending => break,\n                    Poll::Ready(Some(Err(err))) => {\n                        return Poll::Ready(Some(Err(ProtocolError::Io(io::Error::other(err)))));\n                    }\n                }\n            }\n        }\n\n        match this.decoder.decode(this.buf)? {\n            None => {\n                if *this.closed {\n                    Poll::Ready(None)\n                } else {\n                    Poll::Pending\n                }\n            }\n            Some(frm) => {\n                let msg = match frm {\n                    Frame::Text(data) => Message::Text(\n                        ByteString::try_from(data)\n                            .map_err(|err| ProtocolError::Io(io::Error::other(err)))?,\n                    ),\n                    Frame::Binary(data) => Message::Binary(data),\n                    Frame::Ping(s) => Message::Ping(s),\n                    Frame::Pong(s) => Message::Pong(s),\n                    Frame::Close(reason) => Message::Close(reason),\n                    Frame::Continuation(item) => Message::Continuation(item),\n                };\n                Poll::Ready(Some(Ok(msg)))\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_web::test::TestRequest;\n\n    use super::*;\n\n    #[test]\n    fn test_handshake() {\n        let req = TestRequest::default()\n            .method(Method::POST)\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::GetMethodRequired,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default().to_http_request();\n        assert_eq!(\n            HandshakeError::NoWebsocketUpgrade,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((header::UPGRADE, header::HeaderValue::from_static(\"test\")))\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::NoWebsocketUpgrade,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::NoConnectionUpgrade,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::NoVersionHeader,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"5\"),\n            ))\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::UnsupportedVersion,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .to_http_request();\n        assert_eq!(\n            HandshakeError::BadWebsocketKey,\n            handshake(&req).err().unwrap()\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_KEY,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .to_http_request();\n\n        let resp = handshake(&req).unwrap().finish();\n        assert_eq!(StatusCode::SWITCHING_PROTOCOLS, resp.status());\n        assert_eq!(None, resp.headers().get(&header::CONTENT_LENGTH));\n        assert_eq!(None, resp.headers().get(&header::TRANSFER_ENCODING));\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_KEY,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_PROTOCOL,\n                header::HeaderValue::from_static(\"graphql\"),\n            ))\n            .to_http_request();\n\n        let protocols = [\"graphql\"];\n\n        assert_eq!(\n            StatusCode::SWITCHING_PROTOCOLS,\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .status()\n        );\n        assert_eq!(\n            Some(&header::HeaderValue::from_static(\"graphql\")),\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .headers()\n                .get(&header::SEC_WEBSOCKET_PROTOCOL)\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_KEY,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_PROTOCOL,\n                header::HeaderValue::from_static(\"p1, p2, p3\"),\n            ))\n            .to_http_request();\n\n        let protocols = vec![\"p3\", \"p2\"];\n\n        assert_eq!(\n            StatusCode::SWITCHING_PROTOCOLS,\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .status()\n        );\n        assert_eq!(\n            Some(&header::HeaderValue::from_static(\"p2\")),\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .headers()\n                .get(&header::SEC_WEBSOCKET_PROTOCOL)\n        );\n\n        let req = TestRequest::default()\n            .insert_header((\n                header::UPGRADE,\n                header::HeaderValue::from_static(\"websocket\"),\n            ))\n            .insert_header((\n                header::CONNECTION,\n                header::HeaderValue::from_static(\"upgrade\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_VERSION,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_KEY,\n                header::HeaderValue::from_static(\"13\"),\n            ))\n            .insert_header((\n                header::SEC_WEBSOCKET_PROTOCOL,\n                header::HeaderValue::from_static(\"p1,p2,p3\"),\n            ))\n            .to_http_request();\n\n        let protocols = vec![\"p3\", \"p2\"];\n\n        assert_eq!(\n            StatusCode::SWITCHING_PROTOCOLS,\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .status()\n        );\n        assert_eq!(\n            Some(&header::HeaderValue::from_static(\"p2\")),\n            handshake_with_protocols(&req, &protocols)\n                .unwrap()\n                .finish()\n                .headers()\n                .get(&header::SEC_WEBSOCKET_PROTOCOL)\n        );\n    }\n}\n"
  },
  {
    "path": "actix-web-actors/tests/test_ws.rs",
    "content": "use actix::prelude::*;\nuse actix_http::ws::Codec;\nuse actix_web::{web, App, HttpRequest};\nuse actix_web_actors::ws;\nuse bytes::Bytes;\nuse futures_util::{SinkExt as _, StreamExt as _};\n\nstruct Ws;\n\nimpl Actor for Ws {\n    type Context = ws::WebsocketContext<Self>;\n}\n\nimpl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Ws {\n    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {\n        match msg {\n            Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),\n            Ok(ws::Message::Text(text)) => ctx.text(text),\n            Ok(ws::Message::Binary(bin)) => ctx.binary(bin),\n            Ok(ws::Message::Close(reason)) => ctx.close(reason),\n            _ => ctx.close(Some(ws::CloseCode::Error.into())),\n        }\n    }\n}\n\nconst MAX_FRAME_SIZE: usize = 10_000;\nconst DEFAULT_FRAME_SIZE: usize = 10;\n\nasync fn common_test_code(mut srv: actix_test::TestServer, frame_size: usize) {\n    // client service\n    let mut framed = srv.ws().await.unwrap();\n\n    framed.send(ws::Message::Text(\"text\".into())).await.unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Text(Bytes::from_static(b\"text\")));\n\n    let bytes = Bytes::from(vec![0; frame_size]);\n    framed\n        .send(ws::Message::Binary(bytes.clone()))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Binary(bytes));\n\n    framed.send(ws::Message::Ping(\"text\".into())).await.unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Pong(Bytes::copy_from_slice(b\"text\")));\n\n    framed\n        .send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));\n}\n\n#[actix_rt::test]\nasync fn simple_builder() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream).start()\n            },\n        ))\n    });\n\n    common_test_code(srv, DEFAULT_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn builder_with_frame_size() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .frame_size(MAX_FRAME_SIZE)\n                    .start()\n            },\n        ))\n    });\n\n    common_test_code(srv, MAX_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn builder_with_frame_size_exceeded() {\n    const MAX_FRAME_SIZE: usize = 64;\n\n    let mut srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .frame_size(MAX_FRAME_SIZE)\n                    .start()\n            },\n        ))\n    });\n\n    // client service\n    let mut framed = srv.ws().await.unwrap();\n\n    // create a request with a frame size larger than expected\n    let bytes = Bytes::from(vec![0; MAX_FRAME_SIZE + 1]);\n    framed.send(ws::Message::Binary(bytes)).await.unwrap();\n\n    let frame = framed.next().await.unwrap().unwrap();\n    let close_reason = match frame {\n        ws::Frame::Close(Some(reason)) => reason,\n        _ => panic!(\"close frame expected\"),\n    };\n    assert_eq!(close_reason.code, ws::CloseCode::Error);\n}\n\n#[actix_rt::test]\nasync fn builder_with_codec() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .codec(Codec::new())\n                    .start()\n            },\n        ))\n    });\n\n    common_test_code(srv, DEFAULT_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn builder_with_protocols() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .protocols(&[\"A\", \"B\"])\n                    .start()\n            },\n        ))\n    });\n\n    common_test_code(srv, DEFAULT_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn builder_with_codec_and_frame_size() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .codec(Codec::new())\n                    .frame_size(MAX_FRAME_SIZE)\n                    .start()\n            },\n        ))\n    });\n\n    common_test_code(srv, DEFAULT_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn builder_full() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move {\n                ws::WsResponseBuilder::new(Ws, &req, stream)\n                    .frame_size(MAX_FRAME_SIZE)\n                    .codec(Codec::new())\n                    .protocols(&[\"A\", \"B\"])\n                    .start()\n            },\n        ))\n    });\n\n    common_test_code(srv, MAX_FRAME_SIZE).await;\n}\n\n#[actix_rt::test]\nasync fn simple_start() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(\n            |req: HttpRequest, stream: web::Payload| async move { ws::start(Ws, &req, stream) },\n        ))\n    });\n\n    common_test_code(srv, DEFAULT_FRAME_SIZE).await;\n}\n"
  },
  {
    "path": "actix-web-codegen/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n\n## 4.3.0\n\n- Add `#[scope]` macro.\n- Add `compat-routing-macros-force-pub` crate feature which, on-by-default, which when disabled causes handlers to inherit their attached function's visibility.\n- Prevent inclusion of default `actix-router` features.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 4.2.2\n\n- Fix regression when declaring `wrap` attribute using an expression.\n\n## 4.2.1\n\n- Update `syn` dependency to `2`.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 4.2.0\n\n- Add support for custom methods with the `#[route]` macro. [#2969]\n\n[#2969]: https://github.com/actix/actix-web/pull/2969\n\n## 4.1.0\n\n- Add `#[routes]` macro to support multiple paths for one handler. [#2718]\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n[#2718]: https://github.com/actix/actix-web/pull/2718\n\n## 4.0.1\n\n- Fix support for guard paths in route handler macros. [#2771]\n- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.\n\n[#2771]: https://github.com/actix/actix-web/pull/2771\n\n## 4.0.0\n\n- Version aligned with `actix-web` and will remain in sync going forward.\n- No significant changes since `0.5.0`.\n\n## 0.5.0\n\n- No significant changes since `0.5.0-rc.2`.\n\n## 0.5.0-rc.2\n\n- No significant changes since `0.5.0-rc.1`.\n\n## 0.5.0-rc.1\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 0.5.0-beta.6\n\n- No significant changes since `0.5.0-beta.5`.\n\n## 0.5.0-beta.5\n\n- Improve error recovery potential when macro input is invalid. [#2410]\n- Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]\n- Minimum supported Rust version (MSRV) is now 1.52.\n\n[#2410]: https://github.com/actix/actix-web/pull/2410\n[#2409]: https://github.com/actix/actix-web/pull/2409\n\n## 0.5.0-beta.4\n\n- In routing macros, paths are now validated at compile time. [#2350]\n- Minimum supported Rust version (MSRV) is now 1.51.\n\n[#2350]: https://github.com/actix/actix-web/pull/2350\n\n## 0.5.0-beta.3\n\n- No notable changes.\n\n## 0.5.0-beta.2\n\n- Preserve doc comments when using route macros. [#2022]\n- Add `name` attribute to `route` macro. [#1934]\n\n[#2022]: https://github.com/actix/actix-web/pull/2022\n[#1934]: https://github.com/actix/actix-web/pull/1934\n\n## 0.5.0-beta.1\n\n- Use new call signature for `System::new`.\n\n## 0.4.0\n\n- Added compile success and failure testing. [#1677]\n- Add `route` macro for supporting multiple HTTP methods guards. [#1674]\n\n[#1677]: https://github.com/actix/actix-web/pull/1677\n[#1674]: https://github.com/actix/actix-web/pull/1674\n\n## 0.3.0\n\n- No significant changes from `0.3.0-beta.1`.\n\n## 0.3.0-beta.1\n\n- Add main entry-point macro that uses re-exported runtime. [#1559]\n\n[#1559]: https://github.com/actix/actix-web/pull/1559\n\n## 0.2.2\n\n- Add resource middleware on actix-web-codegen [#1467]\n\n[#1467]: https://github.com/actix/actix-web/pull/1467\n\n## 0.2.1\n\n- Add `#[allow(missing_docs)]` attribute to generated structs [#1368]\n- Allow the handler function to be named as `config` [#1290]\n\n[#1368]: https://github.com/actix/actix-web/issues/1368\n[#1290]: https://github.com/actix/actix-web/issues/1290\n\n## 0.2.0\n\n- Generate code for actix-web 2.0\n\n## 0.1.3\n\n- Bump up `syn` & `quote` to 1.0\n- Provide better error message\n\n## 0.1.2\n\n- Add macros for head, options, trace, connect and patch http methods\n\n## 0.1.1\n\n- Add syn \"extra-traits\" feature\n\n## 0.1.0\n\n- Release\n\n## 0.1.0-beta.1\n\n- Gen code for actix-web 1.0.0-beta.1\n\n## 0.1.0-alpha.6\n\n- Gen code for actix-web 1.0.0-alpha.6\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "actix-web-codegen/Cargo.toml",
    "content": "[package]\nname = \"actix-web-codegen\"\nversion = \"4.3.0\"\ndescription = \"Routing and runtime macros for Actix Web\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\", \"Rob Ede <robjtede@icloud.com>\"]\nhomepage.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[lib]\nproc-macro = true\n\n[features]\ndefault = [\"compat-routing-macros-force-pub\"]\ncompat-routing-macros-force-pub = []\n\n[dependencies]\nactix-router = { version = \"0.5\", default-features = false }\nproc-macro2 = \"1\"\nquote = \"1\"\nsyn = { version = \"2\", features = [\"full\", \"extra-traits\"] }\n\n[dev-dependencies]\nactix-macros = \"0.2.4\"\nactix-rt = \"2.2\"\nactix-test = \"0.1\"\nactix-utils = \"3\"\nactix-web = \"4\"\n\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nrustversion-msrv = \"0.100\"\ntrybuild = \"1\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "actix-web-codegen/README.md",
    "content": "# `actix-web-codegen`\n\n> Routing and runtime macros for Actix Web.\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/actix-web-codegen?label=latest)](https://crates.io/crates/actix-web-codegen)\n[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=4.3.0)](https://docs.rs/actix-web-codegen/4.3.0)\n![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg)\n![License](https://img.shields.io/crates/l/actix-web-codegen.svg)\n<br />\n[![dependency status](https://deps.rs/crate/actix-web-codegen/4.3.0/status.svg)](https://deps.rs/crate/actix-web-codegen/4.3.0)\n[![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n## Compile Testing\n\nUses the [`trybuild`] crate. All compile fail tests should include a stderr file generated by `trybuild`. See the [workflow section](https://github.com/dtolnay/trybuild#workflow) of the trybuild docs for info on how to do this.\n\n[`trybuild`]: https://github.com/dtolnay/trybuild\n"
  },
  {
    "path": "actix-web-codegen/src/lib.rs",
    "content": "//! Routing and runtime macros for Actix Web.\n//!\n//! # Actix Web Re-exports\n//! Actix Web re-exports a version of this crate in it's entirety so you usually don't have to\n//! specify a dependency on this crate explicitly. Sometimes, however, updates are made to this\n//! crate before the actix-web dependency is updated. Therefore, code examples here will show\n//! explicit imports. Check the latest [actix-web attributes docs] to see which macros\n//! are re-exported.\n//!\n//! # Runtime Setup\n//! Used for setting up the actix async runtime. See [macro@main] macro docs.\n//!\n//! ```\n//! #[actix_web_codegen::main] // or `#[actix_web::main]` in Actix Web apps\n//! async fn main() {\n//!     async { println!(\"Hello world\"); }.await\n//! }\n//! ```\n//!\n//! # Single Method Handler\n//! There is a macro to set up a handler for each of the most common HTTP methods that also define\n//! additional guards and route-specific middleware.\n//!\n//! See docs for: [GET], [POST], [PATCH], [PUT], [DELETE], [HEAD], [CONNECT], [OPTIONS], [TRACE]\n//!\n//! ```\n//! # use actix_web::HttpResponse;\n//! # use actix_web_codegen::get;\n//! #[get(\"/test\")]\n//! async fn get_handler() -> HttpResponse {\n//!     HttpResponse::Ok().finish()\n//! }\n//! ```\n//!\n//! # Multiple Method Handlers\n//! Similar to the single method handler macro but takes one or more arguments for the HTTP methods\n//! it should respond to. See [macro@route] macro docs.\n//!\n//! ```\n//! # use actix_web::HttpResponse;\n//! # use actix_web_codegen::route;\n//! #[route(\"/test\", method = \"GET\", method = \"HEAD\")]\n//! async fn get_and_head_handler() -> HttpResponse {\n//!     HttpResponse::Ok().finish()\n//! }\n//! ```\n//!\n//! # Multiple Path Handlers\n//! Acts as a wrapper for multiple single method handler macros. It takes no arguments and\n//! delegates those to the macros for the individual methods. See [macro@routes] macro docs.\n//!\n//! ```\n//! # use actix_web::HttpResponse;\n//! # use actix_web_codegen::routes;\n//! #[routes]\n//! #[get(\"/test\")]\n//! #[get(\"/test2\")]\n//! #[delete(\"/test\")]\n//! async fn example() -> HttpResponse {\n//!     HttpResponse::Ok().finish()\n//! }\n//! ```\n//!\n//! [actix-web attributes docs]: https://docs.rs/actix-web/latest/actix_web/#attributes\n//! [GET]: macro@get\n//! [POST]: macro@post\n//! [PUT]: macro@put\n//! [HEAD]: macro@head\n//! [CONNECT]: macro@macro@connect\n//! [OPTIONS]: macro@options\n//! [TRACE]: macro@trace\n//! [PATCH]: macro@patch\n//! [DELETE]: macro@delete\n\n#![recursion_limit = \"512\"]\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\nuse proc_macro::TokenStream;\nuse quote::quote;\n\nmod route;\nmod scope;\n\n/// Creates resource handler, allowing multiple HTTP method guards.\n///\n/// # Syntax\n/// ```plain\n/// #[route(\"path\", method=\"HTTP_METHOD\"[, attributes])]\n/// ```\n///\n/// # Attributes\n/// - `\"path\"`: Raw literal string with path for which to register handler.\n/// - `name = \"resource_name\"`: Specifies resource name for the handler. If not set, the function\n///   name of handler is used.\n/// - `method = \"HTTP_METHOD\"`: Registers HTTP method to provide guard for. Upper-case string,\n///   \"GET\", \"POST\" for example.\n/// - `guard = \"function_name\"`: Registers function as guard using `actix_web::guard::fn_guard`.\n/// - `wrap = \"Middleware\"`: Registers a resource middleware.\n///\n/// # Notes\n/// Function name can be specified as any expression that is going to be accessible to the generate\n/// code, e.g `my_guard` or `my_module::my_guard`.\n///\n/// # Examples\n/// ```\n/// # use actix_web::HttpResponse;\n/// # use actix_web_codegen::route;\n/// #[route(\"/test\", method = \"GET\", method = \"HEAD\", method = \"CUSTOM\")]\n/// async fn example() -> HttpResponse {\n///     HttpResponse::Ok().finish()\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn route(args: TokenStream, input: TokenStream) -> TokenStream {\n    route::with_method(None, args, input)\n}\n\n/// Creates resource handler, allowing multiple HTTP methods and paths.\n///\n/// # Syntax\n/// ```plain\n/// #[routes]\n/// #[<method>(\"path\", ...)]\n/// #[<method>(\"path\", ...)]\n/// ...\n/// ```\n///\n/// # Attributes\n/// The `routes` macro itself has no parameters, but allows specifying the attribute macros for\n/// the multiple paths and/or methods, e.g. [`GET`](macro@get) and [`POST`](macro@post).\n///\n/// These helper attributes take the same parameters as the [single method handlers](crate#single-method-handler).\n///\n/// # Examples\n/// ```\n/// # use actix_web::HttpResponse;\n/// # use actix_web_codegen::routes;\n/// #[routes]\n/// #[get(\"/test\")]\n/// #[get(\"/test2\")]\n/// #[delete(\"/test\")]\n/// async fn example() -> HttpResponse {\n///     HttpResponse::Ok().finish()\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn routes(_: TokenStream, input: TokenStream) -> TokenStream {\n    route::with_methods(input)\n}\n\nmacro_rules! method_macro {\n    ($variant:ident, $method:ident) => {\n        #[doc = concat!(\"Creates route handler with `actix_web::guard::\", stringify!($variant), \"`.\")]\n        ///\n        /// # Syntax\n        /// ```plain\n        #[doc = concat!(\"#[\", stringify!($method), r#\"(\"path\"[, attributes])]\"#)]\n        /// ```\n        ///\n        /// # Attributes\n        /// - `\"path\"`: Raw literal string with path for which to register handler.\n        /// - `name = \"resource_name\"`: Specifies resource name for the handler. If not set, the\n        ///   function name of handler is used.\n        /// - `guard = \"function_name\"`: Registers function as guard using `actix_web::guard::fn_guard`.\n        /// - `wrap = \"Middleware\"`: Registers a resource middleware.\n        ///\n        /// # Notes\n        /// Function name can be specified as any expression that is going to be accessible to the\n        /// generate code, e.g `my_guard` or `my_module::my_guard`.\n        ///\n        /// # Examples\n        /// ```\n        /// # use actix_web::HttpResponse;\n        #[doc = concat!(\"# use actix_web_codegen::\", stringify!($method), \";\")]\n        #[doc = concat!(\"#[\", stringify!($method), r#\"(\"/\")]\"#)]\n        /// async fn example() -> HttpResponse {\n        ///     HttpResponse::Ok().finish()\n        /// }\n        /// ```\n        #[proc_macro_attribute]\n        pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {\n            route::with_method(Some(route::MethodType::$variant), args, input)\n        }\n    };\n}\n\nmethod_macro!(Get, get);\nmethod_macro!(Post, post);\nmethod_macro!(Put, put);\nmethod_macro!(Delete, delete);\nmethod_macro!(Head, head);\nmethod_macro!(Connect, connect);\nmethod_macro!(Options, options);\nmethod_macro!(Trace, trace);\nmethod_macro!(Patch, patch);\n\n/// Prepends a path prefix to all handlers using routing macros inside the attached module.\n///\n/// # Syntax\n///\n/// ```\n/// # use actix_web_codegen::scope;\n/// #[scope(\"/prefix\")]\n/// mod api {\n///     // ...\n/// }\n/// ```\n///\n/// # Arguments\n///\n/// - `\"/prefix\"` - Raw literal string to be prefixed onto contained handlers' paths.\n///\n/// # Example\n///\n/// ```\n/// # use actix_web_codegen::{scope, get};\n/// # use actix_web::Responder;\n/// #[scope(\"/api\")]\n/// mod api {\n///     # use super::*;\n///     #[get(\"/hello\")]\n///     pub async fn hello() -> impl Responder {\n///         // this has path /api/hello\n///         \"Hello, world!\"\n///     }\n/// }\n/// # fn main() {}\n/// ```\n#[proc_macro_attribute]\npub fn scope(args: TokenStream, input: TokenStream) -> TokenStream {\n    scope::with_scope(args, input)\n}\n\n/// Marks async main function as the Actix Web system entry-point.\n///\n/// Note that Actix Web also works under `#[tokio::main]` since version 4.0. However, this macro is\n/// still necessary for actor support (since actors use a `System`). Read more in the\n/// [`actix_web::rt`](https://docs.rs/actix-web/4/actix_web/rt) module docs.\n///\n/// # Examples\n/// ```\n/// #[actix_web::main]\n/// async fn main() {\n///     async { println!(\"Hello world\"); }.await\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main(_: TokenStream, item: TokenStream) -> TokenStream {\n    let mut output: TokenStream = (quote! {\n        #[::actix_web::rt::main(system = \"::actix_web::rt::System\")]\n    })\n    .into();\n\n    output.extend(item);\n    output\n}\n\n/// Marks async test functions to use the Actix Web system entry-point.\n///\n/// # Examples\n/// ```\n/// #[actix_web::test]\n/// async fn test() {\n///     assert_eq!(async { \"Hello world\" }.await, \"Hello world\");\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn test(_: TokenStream, item: TokenStream) -> TokenStream {\n    let mut output: TokenStream = (quote! {\n        #[::actix_web::rt::test(system = \"::actix_web::rt::System\")]\n    })\n    .into();\n\n    output.extend(item);\n    output\n}\n\n/// Converts the error to a token stream and appends it to the original input.\n///\n/// Returning the original input in addition to the error is good for IDEs which can gracefully\n/// recover and show more precise errors within the macro body.\n///\n/// See <https://github.com/rust-analyzer/rust-analyzer/issues/10468> for more info.\nfn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {\n    let compile_err = TokenStream::from(err.to_compile_error());\n    item.extend(compile_err);\n    item\n}\n"
  },
  {
    "path": "actix-web-codegen/src/route.rs",
    "content": "use std::collections::HashSet;\n\nuse actix_router::ResourceDef;\nuse proc_macro::TokenStream;\nuse proc_macro2::{Span, TokenStream as TokenStream2};\nuse quote::{quote, ToTokens, TokenStreamExt};\nuse syn::{punctuated::Punctuated, Ident, LitStr, Path, Token};\n\nuse crate::input_and_compile_error;\n\n#[derive(Debug)]\npub struct RouteArgs {\n    pub(crate) path: syn::LitStr,\n    pub(crate) options: Punctuated<syn::MetaNameValue, Token![,]>,\n}\n\nimpl syn::parse::Parse for RouteArgs {\n    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {\n        // path to match: \"/foo\"\n        let path = input.parse::<syn::LitStr>().map_err(|mut err| {\n            err.combine(syn::Error::new(\n                err.span(),\n                r#\"invalid service definition, expected #[<method>(\"<path>\")]\"#,\n            ));\n\n            err\n        })?;\n\n        // verify that path pattern is valid\n        let _ = ResourceDef::new(path.value());\n\n        // if there's no comma, assume that no options are provided\n        if !input.peek(Token![,]) {\n            return Ok(Self {\n                path,\n                options: Punctuated::new(),\n            });\n        }\n\n        // advance past comma separator\n        input.parse::<Token![,]>()?;\n\n        // if next char is a literal, assume that it is a string and show multi-path error\n        if input.cursor().literal().is_some() {\n            return Err(syn::Error::new(\n                Span::call_site(),\n                r#\"Multiple paths specified! There should be only one.\"#,\n            ));\n        }\n\n        // zero or more options: name = \"foo\"\n        let options = input.parse_terminated(syn::MetaNameValue::parse, Token![,])?;\n\n        Ok(Self { path, options })\n    }\n}\n\nmacro_rules! standard_method_type {\n    (\n        $($variant:ident, $upper:ident, $lower:ident,)+\n    ) => {\n        #[doc(hidden)]\n        #[derive(Debug, Clone, PartialEq, Eq, Hash)]\n        pub enum MethodType {\n            $(\n                $variant,\n            )+\n        }\n\n        impl MethodType {\n            fn as_str(&self) -> &'static str {\n                match self {\n                    $(Self::$variant => stringify!($variant),)+\n                }\n            }\n\n            fn parse(method: &str) -> Result<Self, String> {\n                match method {\n                    $(stringify!($upper) => Ok(Self::$variant),)+\n                    _ => Err(format!(\"HTTP method must be uppercase: `{}`\", method)),\n                }\n            }\n\n            pub(crate) fn from_path(method: &Path) -> Result<Self, ()> {\n                match () {\n                    $(_ if method.is_ident(stringify!($lower)) => Ok(Self::$variant),)+\n                    _ => Err(()),\n                }\n            }\n        }\n    };\n}\n\nstandard_method_type! {\n    Get,       GET,     get,\n    Post,      POST,    post,\n    Put,       PUT,     put,\n    Delete,    DELETE,  delete,\n    Head,      HEAD,    head,\n    Connect,   CONNECT, connect,\n    Options,   OPTIONS, options,\n    Trace,     TRACE,   trace,\n    Patch,     PATCH,   patch,\n}\n\nimpl TryFrom<&syn::LitStr> for MethodType {\n    type Error = syn::Error;\n\n    fn try_from(value: &syn::LitStr) -> Result<Self, Self::Error> {\n        Self::parse(value.value().as_str())\n            .map_err(|message| syn::Error::new_spanned(value, message))\n    }\n}\n\nimpl ToTokens for MethodType {\n    fn to_tokens(&self, stream: &mut TokenStream2) {\n        let ident = Ident::new(self.as_str(), Span::call_site());\n        stream.append(ident);\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\nenum MethodTypeExt {\n    Standard(MethodType),\n    Custom(LitStr),\n}\n\nimpl MethodTypeExt {\n    /// Returns a single method guard token stream.\n    fn to_tokens_single_guard(&self) -> TokenStream2 {\n        match self {\n            MethodTypeExt::Standard(method) => {\n                quote! {\n                    .guard(::actix_web::guard::#method())\n                }\n            }\n            MethodTypeExt::Custom(lit) => {\n                quote! {\n                    .guard(::actix_web::guard::Method(\n                        ::actix_web::http::Method::from_bytes(#lit.as_bytes()).unwrap()\n                    ))\n                }\n            }\n        }\n    }\n\n    /// Returns a multi-method guard chain token stream.\n    fn to_tokens_multi_guard(&self, or_chain: Vec<impl ToTokens>) -> TokenStream2 {\n        debug_assert!(\n            !or_chain.is_empty(),\n            \"empty or_chain passed to multi-guard constructor\"\n        );\n\n        match self {\n            MethodTypeExt::Standard(method) => {\n                quote! {\n                    .guard(\n                        ::actix_web::guard::Any(::actix_web::guard::#method())\n                            #(#or_chain)*\n                    )\n                }\n            }\n            MethodTypeExt::Custom(lit) => {\n                quote! {\n                    .guard(\n                        ::actix_web::guard::Any(\n                            ::actix_web::guard::Method(\n                                ::actix_web::http::Method::from_bytes(#lit.as_bytes()).unwrap()\n                            )\n                        )\n                        #(#or_chain)*\n                    )\n                }\n            }\n        }\n    }\n\n    /// Returns a token stream containing the `.or` chain to be passed in to\n    /// [`MethodTypeExt::to_tokens_multi_guard()`].\n    fn to_tokens_multi_guard_or_chain(&self) -> TokenStream2 {\n        match self {\n            MethodTypeExt::Standard(method_type) => {\n                quote! {\n                    .or(::actix_web::guard::#method_type())\n                }\n            }\n            MethodTypeExt::Custom(lit) => {\n                quote! {\n                    .or(\n                        ::actix_web::guard::Method(\n                            ::actix_web::http::Method::from_bytes(#lit.as_bytes()).unwrap()\n                        )\n                    )\n                }\n            }\n        }\n    }\n}\n\nimpl ToTokens for MethodTypeExt {\n    fn to_tokens(&self, stream: &mut TokenStream2) {\n        match self {\n            MethodTypeExt::Custom(lit_str) => {\n                let ident = Ident::new(lit_str.value().as_str(), Span::call_site());\n                stream.append(ident);\n            }\n            MethodTypeExt::Standard(method) => method.to_tokens(stream),\n        }\n    }\n}\n\nimpl TryFrom<&syn::LitStr> for MethodTypeExt {\n    type Error = syn::Error;\n\n    fn try_from(value: &syn::LitStr) -> Result<Self, Self::Error> {\n        match MethodType::try_from(value) {\n            Ok(method) => Ok(MethodTypeExt::Standard(method)),\n            Err(_) if value.value().chars().all(|c| c.is_ascii_uppercase()) => {\n                Ok(MethodTypeExt::Custom(value.clone()))\n            }\n            Err(err) => Err(err),\n        }\n    }\n}\n\nstruct Args {\n    path: syn::LitStr,\n    resource_name: Option<syn::LitStr>,\n    guards: Vec<Path>,\n    wrappers: Vec<syn::Expr>,\n    methods: HashSet<MethodTypeExt>,\n}\n\nimpl Args {\n    fn new(args: RouteArgs, method: Option<MethodType>) -> syn::Result<Self> {\n        let mut resource_name = None;\n        let mut guards = Vec::new();\n        let mut wrappers = Vec::new();\n        let mut methods = HashSet::new();\n\n        let is_route_macro = method.is_none();\n        if let Some(method) = method {\n            methods.insert(MethodTypeExt::Standard(method));\n        }\n\n        for nv in args.options {\n            if nv.path.is_ident(\"name\") {\n                if let syn::Expr::Lit(syn::ExprLit {\n                    lit: syn::Lit::Str(lit),\n                    ..\n                }) = nv.value\n                {\n                    resource_name = Some(lit);\n                } else {\n                    return Err(syn::Error::new_spanned(\n                        nv.value,\n                        \"Attribute name expects literal string\",\n                    ));\n                }\n            } else if nv.path.is_ident(\"guard\") {\n                if let syn::Expr::Lit(syn::ExprLit {\n                    lit: syn::Lit::Str(lit),\n                    ..\n                }) = nv.value\n                {\n                    guards.push(lit.parse::<Path>()?);\n                } else {\n                    return Err(syn::Error::new_spanned(\n                        nv.value,\n                        \"Attribute guard expects literal string\",\n                    ));\n                }\n            } else if nv.path.is_ident(\"wrap\") {\n                if let syn::Expr::Lit(syn::ExprLit {\n                    lit: syn::Lit::Str(lit),\n                    ..\n                }) = nv.value\n                {\n                    wrappers.push(lit.parse()?);\n                } else {\n                    return Err(syn::Error::new_spanned(\n                        nv.value,\n                        \"Attribute wrap expects type\",\n                    ));\n                }\n            } else if nv.path.is_ident(\"method\") {\n                if !is_route_macro {\n                    return Err(syn::Error::new_spanned(\n                        &nv,\n                        \"HTTP method forbidden here; to handle multiple methods, use `route` instead\",\n                    ));\n                } else if let syn::Expr::Lit(syn::ExprLit {\n                    lit: syn::Lit::Str(lit),\n                    ..\n                }) = nv.value.clone()\n                {\n                    if !methods.insert(MethodTypeExt::try_from(&lit)?) {\n                        return Err(syn::Error::new_spanned(\n                            nv.value,\n                            format!(\"HTTP method defined more than once: `{}`\", lit.value()),\n                        ));\n                    }\n                } else {\n                    return Err(syn::Error::new_spanned(\n                        nv.value,\n                        \"Attribute method expects literal string\",\n                    ));\n                }\n            } else {\n                return Err(syn::Error::new_spanned(\n                    nv.path,\n                    \"Unknown attribute key is specified; allowed: guard, method and wrap\",\n                ));\n            }\n        }\n\n        Ok(Args {\n            path: args.path,\n            resource_name,\n            guards,\n            wrappers,\n            methods,\n        })\n    }\n}\n\npub struct Route {\n    /// Name of the handler function being annotated.\n    name: syn::Ident,\n\n    /// Args passed to routing macro.\n    ///\n    /// When using `#[routes]`, this will contain args for each specific routing macro.\n    args: Vec<Args>,\n\n    /// AST of the handler function being annotated.\n    ast: syn::ItemFn,\n\n    /// The doc comment attributes to copy to generated struct, if any.\n    doc_attributes: Vec<syn::Attribute>,\n}\n\nimpl Route {\n    pub fn new(args: RouteArgs, ast: syn::ItemFn, method: Option<MethodType>) -> syn::Result<Self> {\n        let name = ast.sig.ident.clone();\n\n        // Try and pull out the doc comments so that we can reapply them to the generated struct.\n        // Note that multi line doc comments are converted to multiple doc attributes.\n        let doc_attributes = ast\n            .attrs\n            .iter()\n            .filter(|attr| attr.path().is_ident(\"doc\"))\n            .cloned()\n            .collect();\n\n        let args = Args::new(args, method)?;\n\n        if args.methods.is_empty() {\n            return Err(syn::Error::new(\n                Span::call_site(),\n                \"The #[route(..)] macro requires at least one `method` attribute\",\n            ));\n        }\n\n        if matches!(ast.sig.output, syn::ReturnType::Default) {\n            return Err(syn::Error::new_spanned(\n                ast,\n                \"Function has no return type. Cannot be used as handler\",\n            ));\n        }\n\n        Ok(Self {\n            name,\n            args: vec![args],\n            ast,\n            doc_attributes,\n        })\n    }\n\n    fn multiple(args: Vec<Args>, ast: syn::ItemFn) -> syn::Result<Self> {\n        let name = ast.sig.ident.clone();\n\n        // Try and pull out the doc comments so that we can reapply them to the generated struct.\n        // Note that multi line doc comments are converted to multiple doc attributes.\n        let doc_attributes = ast\n            .attrs\n            .iter()\n            .filter(|attr| attr.path().is_ident(\"doc\"))\n            .cloned()\n            .collect();\n\n        if matches!(ast.sig.output, syn::ReturnType::Default) {\n            return Err(syn::Error::new_spanned(\n                ast,\n                \"Function has no return type. Cannot be used as handler\",\n            ));\n        }\n\n        Ok(Self {\n            name,\n            args,\n            ast,\n            doc_attributes,\n        })\n    }\n}\n\nimpl ToTokens for Route {\n    fn to_tokens(&self, output: &mut TokenStream2) {\n        let Self {\n            name,\n            ast,\n            args,\n            doc_attributes,\n        } = self;\n\n        #[allow(unused_variables)] // used when force-pub feature is disabled\n        let vis = &ast.vis;\n\n        // TODO(breaking): remove this force-pub forwards-compatibility feature\n        #[cfg(feature = \"compat-routing-macros-force-pub\")]\n        let vis = syn::Visibility::Public(<Token![pub]>::default());\n\n        let registrations: TokenStream2 = args\n            .iter()\n            .map(|args| {\n                let Args {\n                    path,\n                    resource_name,\n                    guards,\n                    wrappers,\n                    methods,\n                } = args;\n\n                let resource_name = resource_name\n                    .as_ref()\n                    .map_or_else(|| name.to_string(), LitStr::value);\n\n                let method_guards = {\n                    debug_assert!(!methods.is_empty(), \"Args::methods should not be empty\");\n\n                    let mut others = methods.iter();\n                    let first = others.next().unwrap();\n\n                    if methods.len() > 1 {\n                        let other_method_guards = others\n                            .map(|method_ext| method_ext.to_tokens_multi_guard_or_chain())\n                            .collect();\n\n                        first.to_tokens_multi_guard(other_method_guards)\n                    } else {\n                        first.to_tokens_single_guard()\n                    }\n                };\n\n                quote! {\n                    let __resource = ::actix_web::Resource::new(#path)\n                        .name(#resource_name)\n                        #method_guards\n                        #(.guard(::actix_web::guard::fn_guard(#guards)))*\n                        #(.wrap(#wrappers))*\n                        .to(#name);\n                    ::actix_web::dev::HttpServiceFactory::register(__resource, __config);\n                }\n            })\n            .collect();\n\n        let stream = quote! {\n            #(#doc_attributes)*\n            #[allow(non_camel_case_types)]\n            #vis struct #name;\n\n            impl ::actix_web::dev::HttpServiceFactory for #name {\n                fn register(self, __config: &mut actix_web::dev::AppService) {\n                    #ast\n                    #registrations\n                }\n            }\n        };\n\n        output.extend(stream);\n    }\n}\n\npub(crate) fn with_method(\n    method: Option<MethodType>,\n    args: TokenStream,\n    input: TokenStream,\n) -> TokenStream {\n    let args = match syn::parse(args) {\n        Ok(args) => args,\n        // on parse error, make IDEs happy; see fn docs\n        Err(err) => return input_and_compile_error(input, err),\n    };\n\n    let ast = match syn::parse::<syn::ItemFn>(input.clone()) {\n        Ok(ast) => ast,\n        // on parse error, make IDEs happy; see fn docs\n        Err(err) => return input_and_compile_error(input, err),\n    };\n\n    match Route::new(args, ast, method) {\n        Ok(route) => route.into_token_stream().into(),\n        // on macro related error, make IDEs happy; see fn docs\n        Err(err) => input_and_compile_error(input, err),\n    }\n}\n\npub(crate) fn with_methods(input: TokenStream) -> TokenStream {\n    let mut ast = match syn::parse::<syn::ItemFn>(input.clone()) {\n        Ok(ast) => ast,\n        // on parse error, make IDEs happy; see fn docs\n        Err(err) => return input_and_compile_error(input, err),\n    };\n\n    let (methods, others) = ast\n        .attrs\n        .into_iter()\n        .map(|attr| match MethodType::from_path(attr.path()) {\n            Ok(method) => Ok((method, attr)),\n            Err(_) => Err(attr),\n        })\n        .partition::<Vec<_>, _>(Result::is_ok);\n\n    ast.attrs = others.into_iter().map(Result::unwrap_err).collect();\n\n    let methods = match methods\n        .into_iter()\n        .map(Result::unwrap)\n        .map(|(method, attr)| {\n            attr.parse_args()\n                .and_then(|args| Args::new(args, Some(method)))\n        })\n        .collect::<Result<Vec<_>, _>>()\n    {\n        Ok(methods) if methods.is_empty() => {\n            return input_and_compile_error(\n                input,\n                syn::Error::new(\n                    Span::call_site(),\n                    \"The #[routes] macro requires at least one `#[<method>(..)]` attribute.\",\n                ),\n            )\n        }\n        Ok(methods) => methods,\n        Err(err) => return input_and_compile_error(input, err),\n    };\n\n    match Route::multiple(methods, ast) {\n        Ok(route) => route.into_token_stream().into(),\n        // on macro related error, make IDEs happy; see fn docs\n        Err(err) => input_and_compile_error(input, err),\n    }\n}\n"
  },
  {
    "path": "actix-web-codegen/src/scope.rs",
    "content": "use proc_macro::TokenStream;\nuse proc_macro2::{Span, TokenStream as TokenStream2};\nuse quote::{quote, ToTokens as _};\n\nuse crate::{\n    input_and_compile_error,\n    route::{MethodType, RouteArgs},\n};\n\npub fn with_scope(args: TokenStream, input: TokenStream) -> TokenStream {\n    match with_scope_inner(args, input.clone()) {\n        Ok(stream) => stream,\n        Err(err) => input_and_compile_error(input, err),\n    }\n}\n\nfn with_scope_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {\n    if args.is_empty() {\n        return Err(syn::Error::new(\n            Span::call_site(),\n            \"missing arguments for scope macro, expected: #[scope(\\\"/prefix\\\")]\",\n        ));\n    }\n\n    let scope_prefix = syn::parse::<syn::LitStr>(args.clone()).map_err(|err| {\n        syn::Error::new(\n            err.span(),\n            \"argument to scope macro is not a string literal, expected: #[scope(\\\"/prefix\\\")]\",\n        )\n    })?;\n\n    let scope_prefix_value = scope_prefix.value();\n\n    if scope_prefix_value.ends_with('/') {\n        // trailing slashes cause non-obvious problems\n        // it's better to point them out to developers rather than\n\n        return Err(syn::Error::new(\n            scope_prefix.span(),\n            \"scopes should not have trailing slashes; see https://docs.rs/actix-web/4/actix_web/struct.Scope.html#avoid-trailing-slashes\",\n        ));\n    }\n\n    let mut module = syn::parse::<syn::ItemMod>(input).map_err(|err| {\n        syn::Error::new(err.span(), \"#[scope] macro must be attached to a module\")\n    })?;\n\n    // modify any routing macros (method or route[s]) attached to\n    // functions by prefixing them with this scope macro's argument\n    if let Some((_, items)) = &mut module.content {\n        for item in items {\n            if let syn::Item::Fn(fun) = item {\n                fun.attrs = fun\n                    .attrs\n                    .iter()\n                    .map(|attr| modify_attribute_with_scope(attr, &scope_prefix_value))\n                    .collect();\n            }\n        }\n    }\n\n    Ok(module.to_token_stream().into())\n}\n\n/// Checks if the attribute is a method type and has a route path, then modifies it.\nfn modify_attribute_with_scope(attr: &syn::Attribute, scope_path: &str) -> syn::Attribute {\n    match (attr.parse_args::<RouteArgs>(), attr.clone().meta) {\n        (Ok(route_args), syn::Meta::List(meta_list)) if has_allowed_methods_in_scope(attr) => {\n            let modified_path = format!(\"{}{}\", scope_path, route_args.path.value());\n\n            let options_tokens: Vec<TokenStream2> = route_args\n                .options\n                .iter()\n                .map(|option| {\n                    quote! { ,#option }\n                })\n                .collect();\n\n            let combined_options_tokens: TokenStream2 =\n                options_tokens\n                    .into_iter()\n                    .fold(TokenStream2::new(), |mut acc, ts| {\n                        acc.extend(std::iter::once(ts));\n                        acc\n                    });\n\n            syn::Attribute {\n                meta: syn::Meta::List(syn::MetaList {\n                    tokens: quote! { #modified_path #combined_options_tokens },\n                    ..meta_list.clone()\n                }),\n                ..attr.clone()\n            }\n        }\n        _ => attr.clone(),\n    }\n}\n\nfn has_allowed_methods_in_scope(attr: &syn::Attribute) -> bool {\n    MethodType::from_path(attr.path()).is_ok()\n        || attr.path().is_ident(\"route\")\n        || attr.path().is_ident(\"ROUTE\")\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/routes.rs",
    "content": "use std::future::Future;\n\nuse actix_utils::future::{ok, Ready};\nuse actix_web::{\n    dev::{Service, ServiceRequest, ServiceResponse, Transform},\n    http::{\n        self,\n        header::{HeaderName, HeaderValue},\n        StatusCode,\n    },\n    web, App, Error, HttpRequest, HttpResponse, Responder,\n};\nuse actix_web_codegen::{\n    connect, delete, get, head, options, patch, post, put, route, routes, trace,\n};\nuse futures_core::future::LocalBoxFuture;\n\n// Make sure that we can name function as 'config'\n#[get(\"/config\")]\nasync fn config() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[get(\"/test\")]\nasync fn test_handler() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[put(\"/test\")]\nasync fn put_test() -> impl Responder {\n    HttpResponse::Created()\n}\n\n#[patch(\"/test\")]\nasync fn patch_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[post(\"/test\")]\nasync fn post_test() -> impl Responder {\n    HttpResponse::NoContent()\n}\n\n#[head(\"/test\")]\nasync fn head_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[connect(\"/test\")]\nasync fn connect_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[options(\"/test\")]\nasync fn options_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[trace(\"/test\")]\nasync fn trace_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[get(\"/test\")]\nfn auto_async() -> impl Future<Output = Result<HttpResponse, actix_web::Error>> {\n    ok(HttpResponse::Ok().finish())\n}\n\n#[get(\"/test\")]\nfn auto_sync() -> impl Future<Output = Result<HttpResponse, actix_web::Error>> {\n    ok(HttpResponse::Ok().finish())\n}\n\n#[put(\"/test/{param}\")]\nasync fn put_param_test(_: web::Path<String>) -> impl Responder {\n    HttpResponse::Created()\n}\n\n#[delete(\"/test/{param}\")]\nasync fn delete_param_test(_: web::Path<String>) -> impl Responder {\n    HttpResponse::NoContent()\n}\n\n#[get(\"/test/{param}\")]\nasync fn get_param_test(_: web::Path<String>) -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[route(\"/hello\", method = \"HELLO\")]\nasync fn custom_route_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[route(\n    \"/multi\",\n    method = \"GET\",\n    method = \"POST\",\n    method = \"HEAD\",\n    method = \"HELLO\"\n)]\nasync fn route_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[routes]\n#[get(\"/routes/test\")]\n#[get(\"/routes/test2\")]\n#[post(\"/routes/test\")]\nasync fn routes_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n// routes overlap with the more specific route first, therefore accessible\n#[routes]\n#[get(\"/routes/overlap/test\")]\n#[get(\"/routes/overlap/{foo}\")]\nasync fn routes_overlapping_test(req: HttpRequest) -> impl Responder {\n    // foo is only populated when route is not /routes/overlap/test\n    match req.match_info().get(\"foo\") {\n        None => assert!(req.uri() == \"/routes/overlap/test\"),\n        Some(_) => assert!(req.uri() != \"/routes/overlap/test\"),\n    }\n\n    HttpResponse::Ok()\n}\n\n// routes overlap with the more specific route last, therefore inaccessible\n#[routes]\n#[get(\"/routes/overlap2/{foo}\")]\n#[get(\"/routes/overlap2/test\")]\nasync fn routes_overlapping_inaccessible_test(req: HttpRequest) -> impl Responder {\n    // foo is always populated even when path is /routes/overlap2/test\n    assert!(req.match_info().get(\"foo\").is_some());\n\n    HttpResponse::Ok()\n}\n\n#[get(\"/custom_resource_name\", name = \"custom\")]\nasync fn custom_resource_name_test(req: HttpRequest) -> impl Responder {\n    assert!(req.url_for_static(\"custom\").is_ok());\n    assert!(req.url_for_static(\"custom_resource_name_test\").is_err());\n    HttpResponse::Ok()\n}\n\nmod guard_module {\n    use actix_web::{guard::GuardContext, http::header};\n\n    pub fn guard(ctx: &GuardContext<'_>) -> bool {\n        ctx.header::<header::Accept>()\n            .map(|h| h.preference() == \"image/*\")\n            .unwrap_or(false)\n    }\n}\n\n#[get(\"/test/guard\", guard = \"guard_module::guard\")]\nasync fn guard_test() -> impl Responder {\n    HttpResponse::Ok()\n}\n\npub struct ChangeStatusCode;\n\nimpl<S, B> Transform<S, ServiceRequest> for ChangeStatusCode\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Transform = ChangeStatusCodeMiddleware<S>;\n    type InitError = ();\n    type Future = Ready<Result<Self::Transform, Self::InitError>>;\n\n    fn new_transform(&self, service: S) -> Self::Future {\n        ok(ChangeStatusCodeMiddleware { service })\n    }\n}\n\npub struct ChangeStatusCodeMiddleware<S> {\n    service: S,\n}\n\nimpl<S, B> Service<ServiceRequest> for ChangeStatusCodeMiddleware<S>\nwhere\n    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,\n    S::Future: 'static,\n    B: 'static,\n{\n    type Response = ServiceResponse<B>;\n    type Error = Error;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_web::dev::forward_ready!(service);\n\n    fn call(&self, req: ServiceRequest) -> Self::Future {\n        let fut = self.service.call(req);\n\n        Box::pin(async move {\n            let mut res = fut.await?;\n            let headers = res.headers_mut();\n            let header_name = HeaderName::from_lowercase(b\"custom-header\").unwrap();\n            let header_value = HeaderValue::from_str(\"hello\").unwrap();\n            headers.insert(header_name, header_value);\n            Ok(res)\n        })\n    }\n}\n\n#[get(\"/test/wrap\", wrap = \"ChangeStatusCode\")]\nasync fn get_wrap(_: web::Path<String>) -> impl Responder {\n    // panic!(\"actually never gets called because path failed to extract\");\n    HttpResponse::Ok()\n}\n\n/// Using expression, not just path to type, in wrap attribute.\n///\n/// Regression from <https://github.com/actix/actix-web/issues/3118>.\n#[route(\n    \"/catalog\",\n    method = \"GET\",\n    method = \"HEAD\",\n    wrap = \"actix_web::middleware::Compress::default()\"\n)]\nasync fn get_catalog() -> impl Responder {\n    HttpResponse::Ok().body(\"123123123\")\n}\n\n#[actix_rt::test]\nasync fn test_params() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(get_param_test)\n            .service(put_param_test)\n            .service(delete_param_test)\n    });\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test/it\"));\n    let response = request.send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::OK);\n\n    let request = srv.request(http::Method::PUT, srv.url(\"/test/it\"));\n    let response = request.send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::CREATED);\n\n    let request = srv.request(http::Method::DELETE, srv.url(\"/test/it\"));\n    let response = request.send().await.unwrap();\n    assert_eq!(response.status(), http::StatusCode::NO_CONTENT);\n}\n\n#[actix_rt::test]\nasync fn test_body() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(post_test)\n            .service(put_test)\n            .service(head_test)\n            .service(connect_test)\n            .service(options_test)\n            .service(trace_test)\n            .service(patch_test)\n            .service(test_handler)\n            .service(route_test)\n            .service(routes_overlapping_test)\n            .service(routes_overlapping_inaccessible_test)\n            .service(routes_test)\n            .service(custom_resource_name_test)\n            .service(guard_test)\n    });\n    let request = srv.request(http::Method::GET, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::HEAD, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::CONNECT, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::OPTIONS, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::TRACE, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::PATCH, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::PUT, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.status(), http::StatusCode::CREATED);\n\n    let request = srv.request(http::Method::POST, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.status(), http::StatusCode::NO_CONTENT);\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::POST, srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::HEAD, srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::PATCH, srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(!response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/test2\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::POST, srv.url(\"/routes/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/not-set\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_client_error());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/overlap/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/overlap/bar\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/overlap2/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/routes/overlap2/bar\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::GET, srv.url(\"/custom_resource_name\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv\n        .request(http::Method::GET, srv.url(\"/test/guard\"))\n        .insert_header((\"Accept\", \"image/*\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn test_auto_async() {\n    let srv = actix_test::start(|| App::new().service(auto_async));\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_web::test]\nasync fn test_wrap() {\n    let srv = actix_test::start(|| App::new().service(get_wrap));\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test/wrap\"));\n    let mut response = request.send().await.unwrap();\n    assert_eq!(response.status(), StatusCode::NOT_FOUND);\n    assert!(response.headers().contains_key(\"custom-header\"));\n    let body = response.body().await.unwrap();\n    let body = String::from_utf8(body.to_vec()).unwrap();\n    assert!(body.contains(\"wrong number of parameters\"));\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/scopes.rs",
    "content": "use actix_web::{guard::GuardContext, http, http::header, web, App, HttpResponse, Responder};\nuse actix_web_codegen::{delete, get, post, route, routes, scope};\n\npub fn image_guard(ctx: &GuardContext<'_>) -> bool {\n    ctx.header::<header::Accept>()\n        .map(|h| h.preference() == \"image/*\")\n        .unwrap_or(false)\n}\n\n#[scope(\"/test\")]\nmod scope_module {\n    // ensure that imports can be brought into the scope\n    use super::*;\n\n    #[get(\"/test/guard\", guard = \"image_guard\")]\n    pub async fn guard() -> impl Responder {\n        HttpResponse::Ok()\n    }\n\n    #[get(\"/test\")]\n    pub async fn test() -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    #[get(\"/twice-test/{value}\")]\n    pub async fn twice(value: web::Path<String>) -> impl actix_web::Responder {\n        let int_value: i32 = value.parse().unwrap_or(0);\n        let doubled = int_value * 2;\n        HttpResponse::Ok().body(format!(\"Twice value: {}\", doubled))\n    }\n\n    #[post(\"/test\")]\n    pub async fn post() -> impl Responder {\n        HttpResponse::Ok().body(\"post works\")\n    }\n\n    #[delete(\"/test\")]\n    pub async fn delete() -> impl Responder {\n        \"delete works\"\n    }\n\n    #[route(\"/test\", method = \"PUT\", method = \"PATCH\", method = \"CUSTOM\")]\n    pub async fn multiple_shared_path() -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    #[routes]\n    #[head(\"/test1\")]\n    #[connect(\"/test2\")]\n    #[options(\"/test3\")]\n    #[trace(\"/test4\")]\n    pub async fn multiple_separate_paths() -> impl Responder {\n        HttpResponse::Ok().finish()\n    }\n\n    // test calling this from other mod scope with scope attribute...\n    pub fn mod_common(message: String) -> impl actix_web::Responder {\n        HttpResponse::Ok().body(message)\n    }\n}\n\n/// Scope doc string to check in cargo expand.\n#[scope(\"/v1\")]\nmod mod_scope_v1 {\n    use super::*;\n\n    /// Route doc string to check in cargo expand.\n    #[get(\"/test\")]\n    pub async fn test() -> impl Responder {\n        scope_module::mod_common(\"version1 works\".to_string())\n    }\n}\n\n#[scope(\"/v2\")]\nmod mod_scope_v2 {\n    use super::*;\n\n    // check to make sure non-function tokens in the scope block are preserved...\n    enum TestEnum {\n        Works,\n    }\n\n    #[get(\"/test\")]\n    pub async fn test() -> impl Responder {\n        // make sure this type still exists...\n        let test_enum = TestEnum::Works;\n\n        match test_enum {\n            TestEnum::Works => scope_module::mod_common(\"version2 works\".to_string()),\n        }\n    }\n}\n\n#[actix_rt::test]\nasync fn scope_get_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::test));\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn scope_get_param_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::twice));\n\n    let request = srv.request(http::Method::GET, srv.url(\"/test/twice-test/4\"));\n    let mut response = request.send().await.unwrap();\n    let body = response.body().await.unwrap();\n    let body_str = String::from_utf8(body.to_vec()).unwrap();\n    assert_eq!(body_str, \"Twice value: 8\");\n}\n\n#[actix_rt::test]\nasync fn scope_post_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::post));\n\n    let request = srv.request(http::Method::POST, srv.url(\"/test/test\"));\n    let mut response = request.send().await.unwrap();\n    let body = response.body().await.unwrap();\n    let body_str = String::from_utf8(body.to_vec()).unwrap();\n    assert_eq!(body_str, \"post works\");\n}\n\n#[actix_rt::test]\nasync fn multiple_shared_path_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::multiple_shared_path));\n\n    let request = srv.request(http::Method::PUT, srv.url(\"/test/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::PATCH, srv.url(\"/test/test\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn multiple_multi_path_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::multiple_separate_paths));\n\n    let request = srv.request(http::Method::HEAD, srv.url(\"/test/test1\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::CONNECT, srv.url(\"/test/test2\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::OPTIONS, srv.url(\"/test/test3\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(http::Method::TRACE, srv.url(\"/test/test4\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn scope_delete_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::delete));\n\n    let request = srv.request(http::Method::DELETE, srv.url(\"/test/test\"));\n    let mut response = request.send().await.unwrap();\n    let body = response.body().await.unwrap();\n    let body_str = String::from_utf8(body.to_vec()).unwrap();\n    assert_eq!(body_str, \"delete works\");\n}\n\n#[actix_rt::test]\nasync fn scope_get_with_guard_async() {\n    let srv = actix_test::start(|| App::new().service(scope_module::guard));\n\n    let request = srv\n        .request(http::Method::GET, srv.url(\"/test/test/guard\"))\n        .insert_header((\"Accept\", \"image/*\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn scope_v1_v2_async() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(mod_scope_v1::test)\n            .service(mod_scope_v2::test)\n    });\n\n    let request = srv.request(http::Method::GET, srv.url(\"/v1/test\"));\n    let mut response = request.send().await.unwrap();\n    let body = response.body().await.unwrap();\n    let body_str = String::from_utf8(body.to_vec()).unwrap();\n    assert_eq!(body_str, \"version1 works\");\n\n    let request = srv.request(http::Method::GET, srv.url(\"/v2/test\"));\n    let mut response = request.send().await.unwrap();\n    let body = response.body().await.unwrap();\n    let body_str = String::from_utf8(body.to_vec()).unwrap();\n    assert_eq!(body_str, \"version2 works\");\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/docstring-ok.rs",
    "content": "use actix_web::{Responder, HttpResponse, App};\nuse actix_web_codegen::*;\n\n/// doc comments shouldn't break anything\n#[get(\"/\")]\nasync fn index() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[actix_web::main]\nasync fn main() {\n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.get(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-custom-lowercase.rs",
    "content": "use actix_web_codegen::*;\nuse actix_web::http::Method;\nuse std::str::FromStr;\n\n#[route(\"/\", method = \"hello\")]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.request(Method::from_str(\"hello\").unwrap(), srv.url(\"/\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-custom-lowercase.stderr",
    "content": "error: HTTP method must be uppercase: `hello`\n --> tests/trybuild/route-custom-lowercase.rs:5:23\n  |\n5 | #[route(\"/\", method = \"hello\")]\n  |                       ^^^^^^^\n\nerror[E0277]: the trait bound `fn() -> impl std::future::Future<Output = String> {index}: HttpServiceFactory` is not satisfied\n  --> tests/trybuild/route-custom-lowercase.rs:14:55\n   |\n14 |     let srv = actix_test::start(|| App::new().service(index));\n   |                                               ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for fn item `fn() -> impl std::future::Future<Output = String> {index}`\n   |                                               |\n   |                                               required by a bound introduced by this call\n   |\n   = help: the following other types implement trait `HttpServiceFactory`:\n             (A, B)\n             (A, B, C)\n             (A, B, C, D)\n             (A, B, C, D, E)\n             (A, B, C, D, E, F)\n             (A, B, C, D, E, F, G)\n             (A, B, C, D, E, F, G, H)\n             (A, B, C, D, E, F, G, H, I)\n           and $N others\nnote: required by a bound in `App::<T>::service`\n  --> $WORKSPACE/actix-web/src/app.rs\n   |\n   |     pub fn service<F>(mut self, factory: F) -> Self\n   |            ------- required by a bound in this associated function\n   |     where\n   |         F: HttpServiceFactory + 'static,\n   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `App::<T>::service`\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-custom-method.rs",
    "content": "use std::str::FromStr;\n\nuse actix_web::http::Method;\nuse actix_web_codegen::route;\n\n#[route(\"/single\", method = \"CUSTOM\")]\nasync fn index() -> String {\n    \"Hello Single!\".to_owned()\n}\n\n#[route(\"/multi\", method = \"GET\", method = \"CUSTOM\")]\nasync fn custom() -> String {\n    \"Hello Multi!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index).service(custom));\n\n    let request = srv.request(Method::GET, srv.url(\"/\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_client_error());\n\n    let request = srv.request(Method::from_str(\"CUSTOM\").unwrap(), srv.url(\"/single\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(Method::GET, srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.request(Method::from_str(\"CUSTOM\").unwrap(), srv.url(\"/multi\"));\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-duplicate-method-fail.rs",
    "content": "use actix_web_codegen::*;\n\n#[route(\"/\", method=\"GET\", method=\"GET\")]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.get(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-duplicate-method-fail.stderr",
    "content": "error: HTTP method defined more than once: `GET`\n --> tests/trybuild/route-duplicate-method-fail.rs:3:35\n  |\n3 | #[route(\"/\", method=\"GET\", method=\"GET\")]\n  |                                   ^^^^^\n\nerror[E0277]: the trait bound `fn() -> impl std::future::Future<Output = String> {index}: HttpServiceFactory` is not satisfied\n  --> tests/trybuild/route-duplicate-method-fail.rs:12:55\n   |\n12 |     let srv = actix_test::start(|| App::new().service(index));\n   |                                               ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for fn item `fn() -> impl std::future::Future<Output = String> {index}`\n   |                                               |\n   |                                               required by a bound introduced by this call\n   |\n   = help: the following other types implement trait `HttpServiceFactory`:\n             (A, B)\n             (A, B, C)\n             (A, B, C, D)\n             (A, B, C, D, E)\n             (A, B, C, D, E, F)\n             (A, B, C, D, E, F, G)\n             (A, B, C, D, E, F, G, H)\n             (A, B, C, D, E, F, G, H, I)\n           and $N others\nnote: required by a bound in `App::<T>::service`\n  --> $WORKSPACE/actix-web/src/app.rs\n   |\n   |     pub fn service<F>(mut self, factory: F) -> Self\n   |            ------- required by a bound in this associated function\n   |     where\n   |         F: HttpServiceFactory + 'static,\n   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `App::<T>::service`\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-malformed-path-fail.rs",
    "content": "use actix_web_codegen::get;\n\n#[get(\"/{\")]\nasync fn zero() -> &'static str {\n    \"malformed resource def\"\n}\n\n#[get(\"/{foo\")]\nasync fn one() -> &'static str {\n    \"malformed resource def\"\n}\n\n#[get(\"/{}\")]\nasync fn two() -> &'static str {\n    \"malformed resource def\"\n}\n\n#[get(\"/*\")]\nasync fn three() -> &'static str {\n    \"malformed resource def\"\n}\n\n#[get(\"/{tail:\\\\d+}*\")]\nasync fn four() -> &'static str {\n    \"malformed resource def\"\n}\n\n#[get(\"/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}\")]\nasync fn five() -> &'static str {\n    \"malformed resource def\"\n}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-malformed-path-fail.stderr",
    "content": "error: custom attribute panicked\n --> $DIR/route-malformed-path-fail.rs:3:1\n  |\n3 | #[get(\"/{\")]\n  | ^^^^^^^^^^^^\n  |\n  = help: message: pattern \"{\" contains malformed dynamic segment\n\nerror: custom attribute panicked\n --> $DIR/route-malformed-path-fail.rs:8:1\n  |\n8 | #[get(\"/{foo\")]\n  | ^^^^^^^^^^^^^^^\n  |\n  = help: message: pattern \"{foo\" contains malformed dynamic segment\n\nerror: custom attribute panicked\n  --> $DIR/route-malformed-path-fail.rs:13:1\n   |\n13 | #[get(\"/{}\")]\n   | ^^^^^^^^^^^^^\n   |\n   = help: message: Wrong path pattern: \"/{}\" empty capture group names are not allowed\n\nerror: custom attribute panicked\n  --> $DIR/route-malformed-path-fail.rs:23:1\n   |\n23 | #[get(\"/{tail:\\\\d+}*\")]\n   | ^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = help: message: custom regex is not supported for tail match\n\nerror: custom attribute panicked\n  --> $DIR/route-malformed-path-fail.rs:28:1\n   |\n28 | #[get(\"/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}\")]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = help: message: Only 16 dynamic segments are allowed, provided: 17\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-missing-method-fail.rs",
    "content": "use actix_web_codegen::*;\n\n#[route(\"/\")]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n    \n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.get(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-missing-method-fail.stderr",
    "content": "error: The #[route(..)] macro requires at least one `method` attribute\n --> tests/trybuild/route-missing-method-fail.rs:3:1\n  |\n3 | #[route(\"/\")]\n  | ^^^^^^^^^^^^^\n  |\n  = note: this error originates in the attribute macro `route` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `fn() -> impl std::future::Future<Output = String> {index}: HttpServiceFactory` is not satisfied\n  --> tests/trybuild/route-missing-method-fail.rs:12:55\n   |\n12 |     let srv = actix_test::start(|| App::new().service(index));\n   |                                               ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for fn item `fn() -> impl std::future::Future<Output = String> {index}`\n   |                                               |\n   |                                               required by a bound introduced by this call\n   |\n   = help: the following other types implement trait `HttpServiceFactory`:\n             (A, B)\n             (A, B, C)\n             (A, B, C, D)\n             (A, B, C, D, E)\n             (A, B, C, D, E, F)\n             (A, B, C, D, E, F, G)\n             (A, B, C, D, E, F, G, H)\n             (A, B, C, D, E, F, G, H, I)\n           and $N others\nnote: required by a bound in `App::<T>::service`\n  --> $WORKSPACE/actix-web/src/app.rs\n   |\n   |     pub fn service<F>(mut self, factory: F) -> Self\n   |            ------- required by a bound in this associated function\n   |     where\n   |         F: HttpServiceFactory + 'static,\n   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `App::<T>::service`\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/route-ok.rs",
    "content": "use actix_web_codegen::*;\n\n#[route(\"/\", method=\"GET\", method=\"HEAD\")]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.get(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/routes-missing-args-fail.rs",
    "content": "use actix_web_codegen::*;\n\n#[routes]\n#[get]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/routes-missing-args-fail.stderr",
    "content": "error: unexpected end of input, expected string literal\n --> tests/trybuild/routes-missing-args-fail.rs:4:1\n  |\n4 | #[get]\n  | ^^^^^^\n  |\n  = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: invalid service definition, expected #[<method>(\"<path>\")]\n --> tests/trybuild/routes-missing-args-fail.rs:4:1\n  |\n4 | #[get]\n  | ^^^^^^\n  |\n  = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: expected attribute arguments in parentheses: #[get(...)]\n --> tests/trybuild/routes-missing-args-fail.rs:4:3\n  |\n4 | #[get]\n  |   ^^^\n\nerror[E0277]: the trait bound `fn() -> impl std::future::Future<Output = String> {index}: HttpServiceFactory` is not satisfied\n  --> tests/trybuild/routes-missing-args-fail.rs:13:55\n   |\n13 |     let srv = actix_test::start(|| App::new().service(index));\n   |                                               ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for fn item `fn() -> impl std::future::Future<Output = String> {index}`\n   |                                               |\n   |                                               required by a bound introduced by this call\n   |\n   = help: the following other types implement trait `HttpServiceFactory`:\n             (A, B)\n             (A, B, C)\n             (A, B, C, D)\n             (A, B, C, D, E)\n             (A, B, C, D, E, F)\n             (A, B, C, D, E, F, G)\n             (A, B, C, D, E, F, G, H)\n             (A, B, C, D, E, F, G, H, I)\n           and $N others\nnote: required by a bound in `App::<T>::service`\n  --> $WORKSPACE/actix-web/src/app.rs\n   |\n   |     pub fn service<F>(mut self, factory: F) -> Self\n   |            ------- required by a bound in this associated function\n   |     where\n   |         F: HttpServiceFactory + 'static,\n   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `App::<T>::service`\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/routes-missing-method-fail.rs",
    "content": "use actix_web_codegen::*;\n\n#[routes]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/routes-missing-method-fail.stderr",
    "content": "error: The #[routes] macro requires at least one `#[<method>(..)]` attribute.\n --> tests/trybuild/routes-missing-method-fail.rs:3:1\n  |\n3 | #[routes]\n  | ^^^^^^^^^\n  |\n  = note: this error originates in the attribute macro `routes` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `fn() -> impl std::future::Future<Output = String> {index}: HttpServiceFactory` is not satisfied\n  --> tests/trybuild/routes-missing-method-fail.rs:12:55\n   |\n12 |     let srv = actix_test::start(|| App::new().service(index));\n   |                                               ------- ^^^^^ the trait `HttpServiceFactory` is not implemented for fn item `fn() -> impl std::future::Future<Output = String> {index}`\n   |                                               |\n   |                                               required by a bound introduced by this call\n   |\n   = help: the following other types implement trait `HttpServiceFactory`:\n             (A, B)\n             (A, B, C)\n             (A, B, C, D)\n             (A, B, C, D, E)\n             (A, B, C, D, E, F)\n             (A, B, C, D, E, F, G)\n             (A, B, C, D, E, F, G, H)\n             (A, B, C, D, E, F, G, H, I)\n           and $N others\nnote: required by a bound in `App::<T>::service`\n  --> $WORKSPACE/actix-web/src/app.rs\n   |\n   |     pub fn service<F>(mut self, factory: F) -> Self\n   |            ------- required by a bound in this associated function\n   |     where\n   |         F: HttpServiceFactory + 'static,\n   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `App::<T>::service`\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/routes-ok.rs",
    "content": "use actix_web_codegen::*;\n\n#[routes]\n#[get(\"/\")]\n#[post(\"/\")]\nasync fn index() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[actix_web::main]\nasync fn main() {\n    use actix_web::App;\n\n    let srv = actix_test::start(|| App::new().service(index));\n\n    let request = srv.get(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    let request = srv.post(\"/\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-invalid-args.rs",
    "content": "use actix_web_codegen::scope;\n\nconst PATH: &str = \"/api\";\n\n#[scope(PATH)]\nmod api_const {}\n\n#[scope(true)]\nmod api_bool {}\n\n#[scope(123)]\nmod api_num {}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-invalid-args.stderr",
    "content": "error: argument to scope macro is not a string literal, expected: #[scope(\"/prefix\")]\n --> tests/trybuild/scope-invalid-args.rs:5:9\n  |\n5 | #[scope(PATH)]\n  |         ^^^^\n\nerror: argument to scope macro is not a string literal, expected: #[scope(\"/prefix\")]\n --> tests/trybuild/scope-invalid-args.rs:8:9\n  |\n8 | #[scope(true)]\n  |         ^^^^\n\nerror: argument to scope macro is not a string literal, expected: #[scope(\"/prefix\")]\n  --> tests/trybuild/scope-invalid-args.rs:11:9\n   |\n11 | #[scope(123)]\n   |         ^^^\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-missing-args.rs",
    "content": "use actix_web_codegen::scope;\n\n#[scope]\nmod api {}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-missing-args.stderr",
    "content": "error: missing arguments for scope macro, expected: #[scope(\"/prefix\")]\n --> tests/trybuild/scope-missing-args.rs:3:1\n  |\n3 | #[scope]\n  | ^^^^^^^^\n  |\n  = note: this error originates in the attribute macro `scope` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-on-handler.rs",
    "content": "use actix_web_codegen::scope;\n\n#[scope(\"/api\")]\nasync fn index() -> &'static str {\n    \"Hello World!\"\n}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-on-handler.stderr",
    "content": "error: #[scope] macro must be attached to a module\n --> tests/trybuild/scope-on-handler.rs:4:1\n  |\n4 | async fn index() -> &'static str {\n  | ^^^^^\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-trailing-slash.rs",
    "content": "use actix_web_codegen::scope;\n\n#[scope(\"/api/\")]\nmod api {}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/scope-trailing-slash.stderr",
    "content": "error: scopes should not have trailing slashes; see https://docs.rs/actix-web/4/actix_web/struct.Scope.html#avoid-trailing-slashes\n --> tests/trybuild/scope-trailing-slash.rs:3:9\n  |\n3 | #[scope(\"/api/\")]\n  |         ^^^^^^^\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/simple-fail.rs",
    "content": "use actix_web_codegen::*;\n\n#[get(\"/one\", other)]\nasync fn one() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[post(/two)]\nasync fn two() -> String {\n    \"Hello World!\".to_owned()\n}\n\nstatic PATCH_PATH: &str = \"/three\";\n\n#[patch(PATCH_PATH)]\nasync fn three() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[delete(\"/four\", \"/five\")]\nasync fn four() -> String {\n    \"Hello World!\".to_owned()\n}\n\n#[delete(\"/five\", method=\"GET\")]\nasync fn five() -> String {\n    \"Hello World!\".to_owned()\n}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/simple-fail.stderr",
    "content": "error: expected `=`\n --> $DIR/simple-fail.rs:3:1\n  |\n3 | #[get(\"/one\", other)]\n  | ^^^^^^^^^^^^^^^^^^^^^\n  |\n  = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: expected string literal\n --> $DIR/simple-fail.rs:8:8\n  |\n8 | #[post(/two)]\n  |        ^\n\nerror: invalid service definition, expected #[<method>(\"<path>\")]\n --> $DIR/simple-fail.rs:8:8\n  |\n8 | #[post(/two)]\n  |        ^\n\nerror: expected string literal\n  --> $DIR/simple-fail.rs:15:9\n   |\n15 | #[patch(PATCH_PATH)]\n   |         ^^^^^^^^^^\n\nerror: invalid service definition, expected #[<method>(\"<path>\")]\n  --> $DIR/simple-fail.rs:15:9\n   |\n15 | #[patch(PATCH_PATH)]\n   |         ^^^^^^^^^^\n\nerror: Multiple paths specified! There should be only one.\n  --> $DIR/simple-fail.rs:20:1\n   |\n20 | #[delete(\"/four\", \"/five\")]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `delete` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: HTTP method forbidden here; to handle multiple methods, use `route` instead\n  --> $DIR/simple-fail.rs:25:19\n   |\n25 | #[delete(\"/five\", method=\"GET\")]\n   |                   ^^^^^^^^^^^^\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/simple.rs",
    "content": "use actix_web::{Responder, HttpResponse, App};\nuse actix_web_codegen::*;\n\n#[get(\"/config\")]\nasync fn config() -> impl Responder {\n    HttpResponse::Ok()\n}\n\n#[actix_web::main]\nasync fn main() {\n    let srv = actix_test::start(|| App::new().service(config));\n\n    let request = srv.get(\"/config\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild/test-runtime.rs",
    "content": "#[actix_web::test]\nasync fn my_test() {\n    assert!(async { 1 }.await, 1);\n}\n\nfn main() {}\n"
  },
  {
    "path": "actix-web-codegen/tests/trybuild.rs",
    "content": "#[rustversion_msrv::msrv]\n#[test]\nfn compile_macros() {\n    let t = trybuild::TestCases::new();\n\n    t.pass(\"tests/trybuild/simple.rs\");\n    t.compile_fail(\"tests/trybuild/simple-fail.rs\");\n\n    t.pass(\"tests/trybuild/route-ok.rs\");\n    t.compile_fail(\"tests/trybuild/route-missing-method-fail.rs\");\n    t.compile_fail(\"tests/trybuild/route-duplicate-method-fail.rs\");\n    t.compile_fail(\"tests/trybuild/route-malformed-path-fail.rs\");\n\n    t.pass(\"tests/trybuild/route-custom-method.rs\");\n    t.compile_fail(\"tests/trybuild/route-custom-lowercase.rs\");\n\n    t.pass(\"tests/trybuild/routes-ok.rs\");\n    t.compile_fail(\"tests/trybuild/routes-missing-method-fail.rs\");\n    t.compile_fail(\"tests/trybuild/routes-missing-args-fail.rs\");\n\n    t.compile_fail(\"tests/trybuild/scope-on-handler.rs\");\n    t.compile_fail(\"tests/trybuild/scope-missing-args.rs\");\n    t.compile_fail(\"tests/trybuild/scope-invalid-args.rs\");\n    t.compile_fail(\"tests/trybuild/scope-trailing-slash.rs\");\n\n    t.pass(\"tests/trybuild/docstring-ok.rs\");\n\n    t.pass(\"tests/trybuild/test-runtime.rs\");\n}\n"
  },
  {
    "path": "awc/CHANGES.md",
    "content": "# Changes\n\n## Unreleased\n\n- Add camel-case header controls to `WebsocketsRequest` via `camel_case_headers()` and `set_camel_case_headers()`. [#3953]\n\n[#3953]: https://github.com/actix/actix-web/pull/3953\n\n## 3.8.2\n\n- Minimum supported Rust version (MSRV) is now 1.88.\n- Fix empty streaming request bodies being sent with chunked transfer encoding.\n\n## 3.8.1\n\n- Fix a bug where `GO_AWAY` errors did not stop connections from returning to the pool.\n\n## 3.8.0\n\n- Add `hickory-dns` crate feature (off-by-default).\n- The `trust-dns` crate feature now delegates DNS resolution to `hickory-dns`.\n\n## 3.7.0\n\n- Update `brotli` dependency to `8`.\n\n## 3.6.0\n\n- Prevent panics on connection pool drop when Tokio runtime is shutdown early.\n- Do not send `Host` header on HTTP/2 requests, as it is not required, and some web servers may reject it.\n- Update `brotli` dependency to `7`.\n- Minimum supported Rust version (MSRV) is now 1.75.\n\n## 3.5.1\n\n- Fix WebSocket `Host` request header value when using a non-default port.\n\n## 3.5.0\n\n- Add `rustls-0_23`, `rustls-0_23-webpki-roots`, and `rustls-0_23-native-roots` crate features.\n- Add `awc::Connector::rustls_0_23()` constructor.\n- Fix `rustls-0_22-native-roots` root store lookup.\n- Update `brotli` dependency to `6`.\n- Minimum supported Rust version (MSRV) is now 1.72.\n\n## 3.4.0\n\n- Add `rustls-0_22-webpki-roots` and `rustls-0_22-native-roots` crate feature.\n- Add `awc::Connector::rustls_0_22()` method.\n\n## 3.3.0\n\n- Update `trust-dns-resolver` dependency to `0.23`.\n- Updated `zstd` dependency to `0.13`.\n\n## 3.2.0\n\n- Add `awc::Connector::rustls_021()` method for Rustls v0.21 support behind new `rustls-0_21` crate feature.\n- Add `rustls-0_20` crate feature, which the existing `rustls` feature now aliases.\n- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.\n\n## 3.1.1\n\n### Changed\n\n- `client::Connect` is now public to allow tunneling connection with `client::Connector`.\n\n## 3.1.0\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.\n\n## 3.0.1\n\n### Changed\n\n- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.\n\n### Fixed\n\n- Fixed handling of redirection requests that begin with `//`. [#2840]\n\n[#2840]: https://github.com/actix/actix-web/pull/2840\n\n## 3.0.0\n\n### Dependencies\n\n- Updated `actix-*` to Tokio v1-based versions. [#1813]\n- Updated `bytes` to `1.0`. [#1813]\n- Updated `cookie` to `0.16`. [#2555]\n- Updated `rand` to `0.8`.\n- Updated `rustls` to `0.20`. [#2414]\n- Updated `tokio` to `1`.\n\n### Added\n\n- `trust-dns` crate feature to enable `trust-dns-resolver` as client DNS resolver; disabled by default. [#1969]\n- `cookies` crate feature; enabled by default. [#2619]\n- `compress-brotli` crate feature; enabled by default. [#2250]\n- `compress-gzip` crate feature; enabled by default. [#2250]\n- `compress-zstd` crate feature; enabled by default. [#2250]\n- `client::Connector::handshake_timeout()` for customizing TLS connection handshake timeout. [#2081]\n- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]\n- `client::ConnectionIo` trait alias [#2081]\n- `Client::headers()` to get default mut reference of `HeaderMap` of client object. [#2114]\n- `ClientResponse::timeout()` for set the timeout of collecting response body. [#1931]\n- `ClientBuilder::local_address()` for binding to a local IP address for this client. [#2024]\n- `ClientRequest::insert_header()` method which allows using typed and untyped headers. [#1869]\n- `ClientRequest::append_header()` method which allows using typed and untyped headers. [#1869]\n- `ClientBuilder::add_default_header()` (and deprecate `ClientBuilder::header()`). [#2510]\n\n### Changed\n\n- `client::Connector` type now only has one generic type for `actix_service::Service`. [#2063]\n- `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type. [#1905]\n- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]\n- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]\n- Fix http/https encoding when enabling `compress` feature. [#2116]\n- Rename `TestResponse::{header => append_header, set => insert_header}`. These methods now take a `TryIntoHeaderPair`. [#2094]\n- `ClientBuilder::connector()` method now takes `Connector<T, U>` type. [#2008]\n- Basic auth now accepts blank passwords as an empty string instead of an `Option`. [#2050]\n- Relax default timeout for `Connector` to 5 seconds (up from 1 second). [#1905]\n- `*::send_json()` and `*::send_form()` methods now receive `impl Serialize`. [#2553]\n- `FrozenClientRequest::extra_header()` now uses receives an `impl TryIntoHeaderPair`. [#2553]\n- Rename `Connector::{ssl => openssl}()`. [#2503]\n- `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546]\n- Rename `MessageBody => ResponseBody` to avoid conflicts with `MessageBody` trait. [#2546]\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n### Fixed\n\n- Send headers along with redirected requests. [#2310]\n- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]\n- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553]\n- `impl Future` for `ResponseBody` no longer requires the body type be `Unpin`. [#2546]\n- `impl Future` for `JsonBody` no longer requires the body type be `Unpin`. [#2546]\n- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546]\n\n### Removed\n\n- `compress` crate feature. [#2250]\n- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]\n- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]\n- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]\n- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]\n- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]\n- `ClientBuilder::default` function [#2008]\n\n### Security\n\n- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].\n\n[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html\n[#1813]: https://github.com/actix/actix-web/pull/1813\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1931]: https://github.com/actix/actix-web/pull/1931\n[#1969]: https://github.com/actix/actix-web/pull/1969\n[#1969]: https://github.com/actix/actix-web/pull/1969\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#2008]: https://github.com/actix/actix-web/pull/2008\n[#2024]: https://github.com/actix/actix-web/pull/2024\n[#2050]: https://github.com/actix/actix-web/pull/2050\n[#2063]: https://github.com/actix/actix-web/pull/2063\n[#2081]: https://github.com/actix/actix-web/pull/2081\n[#2081]: https://github.com/actix/actix-web/pull/2081\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2114]: https://github.com/actix/actix-web/pull/2114\n[#2116]: https://github.com/actix/actix-web/pull/2116\n[#2148]: https://github.com/actix/actix-web/pull/2148\n[#2250]: https://github.com/actix/actix-web/pull/2250\n[#2310]: https://github.com/actix/actix-web/pull/2310\n[#2414]: https://github.com/actix/actix-web/pull/2414\n[#2425]: https://github.com/actix/actix-web/pull/2425\n[#2474]: https://github.com/actix/actix-web/pull/2474\n[#2503]: https://github.com/actix/actix-web/pull/2503\n[#2510]: https://github.com/actix/actix-web/pull/2510\n[#2546]: https://github.com/actix/actix-web/pull/2546\n[#2553]: https://github.com/actix/actix-web/pull/2553\n[#2555]: https://github.com/actix/actix-web/pull/2555\n\n<details>\n<summary>3.0.0 Pre-Releases</summary>\n\n## 3.0.0-beta.21\n\n- No significant changes since `3.0.0-beta.20`.\n\n## 3.0.0-beta.20\n\n- No significant changes since `3.0.0-beta.19`.\n\n## 3.0.0-beta.19\n\n- No significant changes since `3.0.0-beta.18`.\n\n## 3.0.0-beta.18\n\n- Minimum supported Rust version (MSRV) is now 1.54.\n\n## 3.0.0-beta.17\n\n### Changed\n\n- Update `cookie` dependency (re-exported) to `0.16`. [#2555]\n\n### Security\n\n- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].\n\n[#2555]: https://github.com/actix/actix-web/pull/2555\n[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html\n\n## 3.0.0-beta.16\n\n- `*::send_json` and `*::send_form` methods now receive `impl Serialize`. [#2553]\n- `FrozenClientRequest::extra_header` now uses receives an `impl TryIntoHeaderPair`. [#2553]\n- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553]\n\n[#2553]: https://github.com/actix/actix-web/pull/2553\n\n## 3.0.0-beta.15\n\n- Rename `Connector::{ssl => openssl}`. [#2503]\n- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]\n- `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546]\n- Rename `MessageBody => ResponseBody` to avoid conflicts with `MessageBody` trait. [#2546]\n- `impl Future` for `ResponseBody` no longer requires the body type be `Unpin`. [#2546]\n- `impl Future` for `JsonBody` no longer requires the body type be `Unpin`. [#2546]\n- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546]\n\n[#2503]: https://github.com/actix/actix-web/pull/2503\n[#2546]: https://github.com/actix/actix-web/pull/2546\n\n## 3.0.0-beta.14\n\n- Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510]\n\n[#2510]: https://github.com/actix/actix-web/pull/2510\n\n## 3.0.0-beta.13\n\n- No significant changes since `3.0.0-beta.12`.\n\n## 3.0.0-beta.12\n\n- Update `actix-tls` to `3.0.0-rc.1`. [#2474]\n\n[#2474]: https://github.com/actix/actix-web/pull/2474\n\n## 3.0.0-beta.11\n\n- No significant changes from `3.0.0-beta.10`.\n\n## 3.0.0-beta.10\n\n- No significant changes from `3.0.0-beta.9`.\n\n## 3.0.0-beta.9\n\n- Updated rustls to v0.20. [#2414]\n\n[#2414]: https://github.com/actix/actix-web/pull/2414\n\n## 3.0.0-beta.8\n\n### Changed\n\n- Send headers within the redirect requests. [#2310]\n\n[#2310]: https://github.com/actix/actix-web/pull/2310\n\n## 3.0.0-beta.7\n\n### Changed\n\n- Change compression algorithm features flags. [#2250]\n\n[#2250]: https://github.com/actix/actix-web/pull/2250\n\n## 3.0.0-beta.6\n\n- No significant changes since 3.0.0-beta.5.\n\n## 3.0.0-beta.5\n\n### Removed\n\n- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]\n\n[#2148]: https://github.com/actix/actix-web/pull/2148\n\n## 3.0.0-beta.4\n\n### Added\n\n- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]\n\n### Changed\n\n- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]\n- Fix http/https encoding when enabling `compress` feature. [#2116]\n- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `TryIntoHeaderPair` tuples. [#2094]\n\n[#2081]: https://github.com/actix/actix-web/pull/2081\n[#2094]: https://github.com/actix/actix-web/pull/2094\n[#2114]: https://github.com/actix/actix-web/pull/2114\n[#2116]: https://github.com/actix/actix-web/pull/2116\n\n## 3.0.0-beta.3\n\n### Added\n\n- `ClientResponse::timeout` for set the timeout of collecting response body. [#1931]\n- `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024]\n\n### Changed\n\n- Feature `cookies` is now optional and enabled by default. [#1981]\n- `ClientBuilder::connector` method would take `actix_http::client::Connector<T, U>` type. [#2008]\n- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050]\n\n### Removed\n\n- `ClientBuilder::default` function [#2008]\n\n[#1931]: https://github.com/actix/actix-web/pull/1931\n[#1981]: https://github.com/actix/actix-web/pull/1981\n[#2008]: https://github.com/actix/actix-web/pull/2008\n[#2024]: https://github.com/actix/actix-web/pull/2024\n[#2050]: https://github.com/actix/actix-web/pull/2050\n\n## 3.0.0-beta.2\n\n### Added\n\n- `ClientRequest::insert_header` method which allows using typed headers. [#1869]\n- `ClientRequest::append_header` method which allows using typed headers. [#1869]\n- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]\n\n### Changed\n\n- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]\n\n### Removed\n\n- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]\n- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]\n- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]\n- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]\n\n[#1869]: https://github.com/actix/actix-web/pull/1869\n[#1905]: https://github.com/actix/actix-web/pull/1905\n[#1969]: https://github.com/actix/actix-web/pull/1969\n\n## 3.0.0-beta.1\n\n### Changed\n\n- Update `rand` to `0.8`\n- Update `bytes` to `1.0`. [#1813]\n- Update `rust-tls` to `0.19`. [#1813]\n\n[#1813]: https://github.com/actix/actix-web/pull/1813\n\n</details>\n\n## 2.0.3\n\n### Fixed\n\n- Ensure `actix-http` dependency uses same `serde_urlencoded`.\n\n## 2.0.2\n\n### Changed\n\n- Upgrade `serde_urlencoded` to `0.7`. [#1773]\n\n[#1773]: https://github.com/actix/actix-web/pull/1773\n\n## 2.0.1\n\n### Changed\n\n- Upgrade `base64` to `0.13`. [#1744]\n- Deprecate `ClientRequest::{if_some, if_true}`. [#1760]\n\n### Fixed\n\n- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature is enabled [#1737]\n\n[#1737]: https://github.com/actix/actix-web/pull/1737\n[#1760]: https://github.com/actix/actix-web/pull/1760\n[#1744]: https://github.com/actix/actix-web/pull/1744\n\n## 2.0.0\n\n### Changed\n\n- `Client::build` was renamed to `Client::builder`.\n\n## 2.0.0-beta.4\n\n### Changed\n\n- Update actix-codec & actix-tls dependencies.\n\n## 2.0.0-beta.3\n\n### Changed\n\n- Update `rustls` to 0.18\n\n## 2.0.0-beta.2\n\n### Changed\n\n- Update `actix-http` dependency to 2.0.0-beta.2\n\n## 2.0.0-beta.1\n\n### Changed\n\n- Update `actix-http` dependency to 2.0.0-beta.1\n\n## 2.0.0-alpha.2\n\n### Changed\n\n- Implement `std::error::Error` for our custom errors [#1422]\n- Bump minimum supported Rust version to 1.40\n- Update `base64` dependency to 0.12\n\n[#1422]: https://github.com/actix/actix-web/pull/1422\n\n## 2.0.0-alpha.1\n\n- Update `actix-http` dependency to 2.0.0-alpha.2\n- Update `rustls` dependency to 0.17\n- ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration\n- ClientBuilder allowing to set max_http_version to limit HTTP version to be used\n\n## 1.0.1\n\n- Fix compilation with default features off\n\n## 1.0.0\n\n- Release\n\n## 1.0.0-alpha.3\n\n- Migrate to `std::future`\n\n## 0.2.8\n\n- Add support for setting query from Serialize type for client request.\n\n## 0.2.7\n\n### Added\n\n- Remaining getter methods for `ClientRequest`'s private `head` field #1101\n\n## 0.2.6\n\n### Added\n\n- Export frozen request related types.\n\n## 0.2.5\n\n### Added\n\n- Add `FrozenClientRequest` to support retries for sending HTTP requests\n\n### Changed\n\n- Ensure that the `Host` header is set when initiating a WebSocket client connection.\n\n## 0.2.4\n\n### Changed\n\n- Update percent-encoding to \"2.1\"\n\n- Update serde_urlencoded to \"0.6.1\"\n\n## 0.2.3\n\n### Added\n\n- Add `rustls` support\n\n## 0.2.2\n\n### Changed\n\n- Always append a colon after username in basic auth\n\n- Upgrade `rand` dependency version to 0.7\n\n## 0.2.1\n\n### Added\n\n- Add license files\n\n## 0.2.0\n\n### Added\n\n- Allow to send headers in `Camel-Case` form.\n\n### Changed\n\n- Upgrade actix-http dependency.\n\n## 0.1.1\n\n### Added\n\n- Allow to specify server address for http and ws requests.\n\n### Changed\n\n- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref\n\n## 0.1.0\n\n- No changes\n\n## 0.1.0-alpha.6\n\n### Changed\n\n- Do not set default headers for websocket request\n\n## 0.1.0-alpha.5\n\n### Changed\n\n- Do not set any default headers\n\n### Added\n\n- Add Debug impl for BoxedSocket\n\n## 0.1.0-alpha.4\n\n### Changed\n\n- Update actix-http dependency\n\n## 0.1.0-alpha.3\n\n### Added\n\n- Export `MessageBody` type\n\n- `ClientResponse::json()` - Loads and parse `application/json` encoded body\n\n### Changed\n\n- `ClientRequest::json()` accepts reference instead of object.\n\n- `ClientResponse::body()` does not consume response object.\n\n- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()`\n\n## 0.1.0-alpha.2\n\n### Added\n\n- Per request and session wide request timeout.\n\n- Session wide headers.\n\n- Session wide basic and bearer auth.\n\n- Re-export `actix_http::client::Connector`.\n\n### Changed\n\n- Allow to override request's uri\n\n- Export `ws` sub-module with websockets related types\n\n## 0.1.0-alpha.1\n\n- Initial impl\n"
  },
  {
    "path": "awc/Cargo.toml",
    "content": "[package]\nname = \"awc\"\nversion = \"3.8.2\"\nauthors = [\"Nikolay Kim <fafhrd91@gmail.com>\"]\ndescription = \"Async HTTP and WebSocket client library\"\nkeywords = [\"actix\", \"http\", \"framework\", \"async\", \"web\"]\ncategories = [\n    \"network-programming\",\n    \"asynchronous\",\n    \"web-programming::http-client\",\n    \"web-programming::websocket\",\n]\nhomepage = \"https://actix.rs\"\nrepository = \"https://github.com/actix/actix-web\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\n\n[package.metadata.docs.rs]\nfeatures = [\n    \"cookies\",\n    \"openssl\",\n    \"rustls-0_20\",\n    \"rustls-0_21\",\n    \"rustls-0_22-webpki-roots\",\n    \"rustls-0_23-webpki-roots\",\n    \"compress-brotli\",\n    \"compress-gzip\",\n    \"compress-zstd\",\n]\n\n[package.metadata.cargo_check_external_types]\nallowed_external_types = [\n    \"actix_codec::*\",\n    \"actix_http::*\",\n    \"actix_rt::*\",\n    \"actix_service::*\",\n    \"actix_tls::*\",\n    \"bytes::*\",\n    \"cookie::*\",\n    \"cookie\",\n    \"futures_core::*\",\n    \"h2::*\",\n    \"http::*\",\n    \"openssl::*\",\n    \"rustls::*\",\n    \"serde_json::*\",\n    \"serde_urlencoded::*\",\n    \"serde::*\",\n    \"tokio::*\",\n]\n\n[features]\ndefault = [\"compress-brotli\", \"compress-gzip\", \"compress-zstd\", \"cookies\"]\n\n# TLS via OpenSSL\nopenssl = [\"tls-openssl\", \"actix-tls/openssl\"]\n\n# TLS via Rustls v0.20\nrustls = [\"rustls-0_20\"]\n# TLS via Rustls v0.20\nrustls-0_20 = [\"tls-rustls-0_20\", \"actix-tls/rustls-0_20\"]\n# TLS via Rustls v0.21\nrustls-0_21 = [\"tls-rustls-0_21\", \"actix-tls/rustls-0_21\"]\n# TLS via Rustls v0.22 (WebPKI roots)\nrustls-0_22-webpki-roots = [\"tls-rustls-0_22\", \"actix-tls/rustls-0_22-webpki-roots\"]\n# TLS via Rustls v0.22 (Native roots)\nrustls-0_22-native-roots = [\"tls-rustls-0_22\", \"actix-tls/rustls-0_22-native-roots\"]\n# TLS via Rustls v0.23\nrustls-0_23 = [\"tls-rustls-0_23\", \"actix-tls/rustls-0_23\"]\n# TLS via Rustls v0.23 (WebPKI roots)\nrustls-0_23-webpki-roots = [\"rustls-0_23\", \"actix-tls/rustls-0_23-webpki-roots\"]\n# TLS via Rustls v0.23 (Native roots)\nrustls-0_23-native-roots = [\"rustls-0_23\", \"actix-tls/rustls-0_23-native-roots\"]\n\n# Brotli algorithm content-encoding support\ncompress-brotli = [\"actix-http/compress-brotli\", \"__compress\"]\n# Gzip and deflate algorithms content-encoding support\ncompress-gzip = [\"actix-http/compress-gzip\", \"__compress\"]\n# Zstd algorithm content-encoding support\ncompress-zstd = [\"actix-http/compress-zstd\", \"__compress\"]\n\n# Cookie parsing and cookie jar\ncookies = [\"dep:cookie\"]\n\n# Use `hickory-dns-resolver` crate as DNS resolver\nhickory-dns = [\"dep:hickory-resolver\"]\n# Use `trust-dns-resolver` crate as DNS resolver (deprecated, use `hickory-dns`)\ntrust-dns = [\"hickory-dns\"]\n\n# Internal (PRIVATE!) features used to aid testing and checking feature status.\n# Don't rely on these whatsoever. They may disappear at anytime.\n__compress = []\n\n# Enable dangerous feature for testing and local network usage:\n# - HTTP/2 over TCP(No Tls).\n# DO NOT enable this over any internet use case.\ndangerous-h2c = []\n\n[dependencies]\nactix-codec = \"0.5\"\nactix-http = { version = \"3.12.0\", features = [\"http2\", \"ws\"] }\nactix-rt = { version = \"2.1\", default-features = false }\nactix-service = \"2\"\nactix-tls = { version = \"3.4\", features = [\"connect\", \"uri\"] }\nactix-utils = \"3\"\n\nbase64 = \"0.22\"\nbytes = \"1\"\ncfg-if = \"1\"\nderive_more = { version = \"2\", features = [\"display\", \"error\", \"from\"] }\nfutures-core = { version = \"0.3.17\", default-features = false, features = [\"alloc\"] }\nfutures-util = { version = \"0.3.17\", default-features = false, features = [\"alloc\", \"sink\"] }\nh2 = \"0.3.27\"\nhttp = \"0.2.7\"\nitoa = \"1\"\nlog = \"0.4\"\nmime = \"0.3\"\npercent-encoding = \"2.1\"\npin-project-lite = \"0.2\"\nrand = \"0.9\"\nserde = \"1.0\"\nserde_json = \"1.0\"\nserde_urlencoded = \"0.7\"\ntokio = { version = \"1.38.2\", features = [\"sync\"] }\n\ncookie = { version = \"0.16\", features = [\"percent-encode\"], optional = true }\n\ntls-openssl = { package = \"openssl\", version = \"0.10.55\", optional = true }\ntls-rustls-0_20 = { package = \"rustls\", version = \"0.20\", optional = true, features = [\"dangerous_configuration\"] }\ntls-rustls-0_21 = { package = \"rustls\", version = \"0.21\", optional = true, features = [\"dangerous_configuration\"] }\ntls-rustls-0_22 = { package = \"rustls\", version = \"0.22\", optional = true }\ntls-rustls-0_23 = { package = \"rustls\", version = \"0.23\", optional = true, default-features = false }\n\nhickory-resolver = { version = \"0.25\", optional = true, features = [\"system-config\", \"tokio\"] }\n\n[dev-dependencies]\nactix-http = { version = \"3.12\", features = [\"openssl\"] }\nactix-http-test = { version = \"3\", features = [\"openssl\"] }\nactix-server = \"2\"\nactix-test = { version = \"0.1\", features = [\"openssl\", \"rustls-0_23\"] }\nactix-tls = { version = \"3.4\", features = [\"openssl\", \"rustls-0_23\"] }\nactix-utils = \"3\"\nactix-web = { version = \"4.13\", features = [\"openssl\"] }\n\nbrotli = \"8\"\nconst-str = \"0.5\" # TODO(MSRV 1.77): update to 0.6\nenv_logger = \"0.11\"\nflate2 = \"1.0.13\"\nfutures-util = { version = \"0.3.17\", default-features = false }\nstatic_assertions = \"1.1\"\nrcgen = \"0.13\"\nrustls-pki-types = \"1.13.1\"\ntokio = { version = \"1.38.2\", features = [\"rt-multi-thread\", \"macros\"] }\nzstd = \"0.13\"\ntls-rustls-0_23 = { package = \"rustls\", version = \"0.23\" } # add rustls 0.23 with default features to make aws_lc_rs work in tests\n\n[[example]]\nname = \"client\"\nrequired-features = [\"rustls-0_23-webpki-roots\"]\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "awc/README.md",
    "content": "# `awc` (Actix Web Client)\n\n> Async HTTP and WebSocket client library.\n\n<!-- prettier-ignore-start -->\n\n[![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc)\n[![Documentation](https://docs.rs/awc/badge.svg?version=3.8.2)](https://docs.rs/awc/3.8.2)\n![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc)\n[![Dependency Status](https://deps.rs/crate/awc/3.8.2/status.svg)](https://deps.rs/crate/awc/3.8.2)\n[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)\n\n<!-- prettier-ignore-end -->\n\n## Examples\n\n[Example project using TLS-enabled client →](https://github.com/actix/examples/tree/main/https-tls/awc-https)\n\nBasic usage:\n\n```rust\nuse actix_rt::System;\nuse awc::Client;\n\nfn main() {\n    System::new().block_on(async {\n        let client = Client::default();\n\n        let res = client\n            .get(\"http://www.rust-lang.org\")    // <- Create request builder\n            .insert_header((\"User-Agent\", \"Actix-web\"))\n            .send()                             // <- Send http request\n            .await;\n\n        println!(\"Response: {:?}\", res);        // <- server http response\n    });\n}\n```\n"
  },
  {
    "path": "awc/examples/client.rs",
    "content": "//! Demonstrates construction and usage of a TLS-capable HTTP client.\n\nextern crate tls_rustls_0_23 as rustls;\n\nuse std::{error::Error as StdError, sync::Arc};\n\nuse actix_tls::connect::rustls_0_23::webpki_roots_cert_store;\nuse rustls::ClientConfig;\n\n#[actix_rt::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    env_logger::init_from_env(env_logger::Env::new().default_filter_or(\"info\"));\n\n    let mut config = ClientConfig::builder()\n        .with_root_certificates(webpki_roots_cert_store())\n        .with_no_client_auth();\n\n    let protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n    config.alpn_protocols = protos;\n\n    // construct request builder with TLS support\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().rustls_0_23(Arc::new(config)))\n        .finish();\n\n    // configure request\n    let request = client\n        .get(\"https://www.rust-lang.org/\")\n        .append_header((\"User-Agent\", \"awc/3.0\"));\n\n    println!(\"Request: {request:?}\");\n\n    let mut response = request.send().await?;\n\n    // server response head\n    println!(\"Response: {response:?}\");\n\n    // read response body\n    let body = response.body().await?;\n    println!(\"Downloaded: {:?} bytes\", body.len());\n\n    Ok(())\n}\n"
  },
  {
    "path": "awc/src/any_body.rs",
    "content": "use std::{\n    fmt, mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::body::{BodySize, BoxBody, MessageBody};\nuse bytes::Bytes;\nuse pin_project_lite::pin_project;\n\npin_project! {\n    /// Represents various types of HTTP message body.\n    #[derive(Clone)]\n    #[project = AnyBodyProj]\n    pub enum AnyBody<B = BoxBody> {\n        /// Empty response. `Content-Length` header is not set.\n        None,\n\n        /// Complete, in-memory response body.\n        Bytes { body: Bytes },\n\n        /// Generic / Other message body.\n        Body { #[pin] body: B },\n    }\n}\n\nimpl AnyBody {\n    /// Constructs a \"body\" representing an empty response.\n    pub fn none() -> Self {\n        Self::None\n    }\n\n    /// Constructs a new, 0-length body.\n    pub fn empty() -> Self {\n        Self::Bytes { body: Bytes::new() }\n    }\n\n    /// Create boxed body from generic message body.\n    pub fn new_boxed<B>(body: B) -> Self\n    where\n        B: MessageBody + 'static,\n    {\n        Self::Body { body: body.boxed() }\n    }\n\n    /// Constructs new `AnyBody` instance from a slice of bytes by copying it.\n    ///\n    /// If your bytes container is owned, it may be cheaper to use a `From` impl.\n    pub fn copy_from_slice(s: &[u8]) -> Self {\n        Self::Bytes {\n            body: Bytes::copy_from_slice(s),\n        }\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"4.0.0\", note = \"Renamed to `copy_from_slice`.\")]\n    pub fn from_slice(s: &[u8]) -> Self {\n        Self::Bytes {\n            body: Bytes::copy_from_slice(s),\n        }\n    }\n}\n\nimpl<B> AnyBody<B> {\n    /// Create body from generic message body.\n    pub fn new(body: B) -> Self {\n        Self::Body { body }\n    }\n}\n\nimpl<B> AnyBody<B>\nwhere\n    B: MessageBody + 'static,\n{\n    /// Converts a [`MessageBody`] type into the best possible representation.\n    ///\n    /// Checks size for `None` and tries to convert to `Bytes`. Otherwise, uses the `Body` variant.\n    pub fn from_message_body(body: B) -> Self\n    where\n        B: MessageBody,\n    {\n        if matches!(body.size(), BodySize::None) {\n            return Self::None;\n        }\n\n        match body.try_into_bytes() {\n            Ok(body) => Self::Bytes { body },\n            Err(body) => Self::new(body),\n        }\n    }\n\n    pub fn into_boxed(self) -> AnyBody {\n        match self {\n            Self::None => AnyBody::None,\n            Self::Bytes { body } => AnyBody::Bytes { body },\n            Self::Body { body } => AnyBody::new_boxed(body),\n        }\n    }\n}\n\nimpl<B> MessageBody for AnyBody<B>\nwhere\n    B: MessageBody,\n{\n    type Error = crate::BoxError;\n\n    fn size(&self) -> BodySize {\n        match self {\n            AnyBody::None => BodySize::None,\n            AnyBody::Bytes { ref body } => BodySize::Sized(body.len() as u64),\n            AnyBody::Body { ref body } => body.size(),\n        }\n    }\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n        match self.project() {\n            AnyBodyProj::None => Poll::Ready(None),\n            AnyBodyProj::Bytes { body } => {\n                let len = body.len();\n                if len == 0 {\n                    Poll::Ready(None)\n                } else {\n                    Poll::Ready(Some(Ok(mem::take(body))))\n                }\n            }\n\n            AnyBodyProj::Body { body } => body.poll_next(cx).map_err(|err| err.into()),\n        }\n    }\n}\n\nimpl PartialEq for AnyBody {\n    fn eq(&self, other: &AnyBody) -> bool {\n        match self {\n            AnyBody::None => matches!(*other, AnyBody::None),\n            AnyBody::Bytes { body } => match other {\n                AnyBody::Bytes { body: b2 } => body == b2,\n                _ => false,\n            },\n            AnyBody::Body { .. } => false,\n        }\n    }\n}\n\nimpl<S: fmt::Debug> fmt::Debug for AnyBody<S> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            AnyBody::None => write!(f, \"AnyBody::None\"),\n            AnyBody::Bytes { ref body } => write!(f, \"AnyBody::Bytes({:?})\", body),\n            AnyBody::Body { ref body } => write!(f, \"AnyBody::Message({:?})\", body),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::marker::PhantomPinned;\n\n    use static_assertions::{assert_impl_all, assert_not_impl_any};\n\n    use super::*;\n\n    #[allow(dead_code)]\n    struct PinType(PhantomPinned);\n\n    impl MessageBody for PinType {\n        type Error = crate::BoxError;\n\n        fn size(&self) -> BodySize {\n            unimplemented!()\n        }\n\n        fn poll_next(\n            self: Pin<&mut Self>,\n            _cx: &mut Context<'_>,\n        ) -> Poll<Option<Result<Bytes, Self::Error>>> {\n            unimplemented!()\n        }\n    }\n\n    assert_impl_all!(AnyBody<()>: Send, Sync, Unpin, fmt::Debug, MessageBody);\n    assert_impl_all!(AnyBody<AnyBody<()>>: Send, Sync, Unpin, fmt::Debug, MessageBody);\n    assert_impl_all!(AnyBody<Bytes>: Send, Sync, Unpin, fmt::Debug, MessageBody);\n    assert_impl_all!(AnyBody: Unpin, fmt::Debug, MessageBody);\n    assert_impl_all!(AnyBody<PinType>: Send, Sync, MessageBody);\n\n    assert_not_impl_any!(AnyBody: Send, Sync);\n    assert_not_impl_any!(AnyBody<PinType>: Unpin);\n}\n"
  },
  {
    "path": "awc/src/builder.rs",
    "content": "use std::{fmt, net::IpAddr, rc::Rc, time::Duration};\n\nuse actix_http::{\n    error::HttpError,\n    header::{self, HeaderMap, HeaderName, TryIntoHeaderPair},\n    Uri,\n};\nuse actix_rt::net::{ActixStream, TcpStream};\nuse actix_service::{boxed, Service};\nuse base64::prelude::*;\n\nuse crate::{\n    client::{\n        ClientConfig, ConnectInfo, Connector, ConnectorService, TcpConnectError, TcpConnection,\n    },\n    connect::DefaultConnector,\n    error::SendRequestError,\n    middleware::{NestTransform, Redirect, Transform},\n    Client, ConnectRequest, ConnectResponse,\n};\n\n/// An HTTP Client builder\n///\n/// This type can be used to construct an instance of `Client` through a\n/// builder-like pattern.\npub struct ClientBuilder<S = (), M = ()> {\n    max_http_version: Option<http::Version>,\n    stream_window_size: Option<u32>,\n    conn_window_size: Option<u32>,\n    fundamental_headers: bool,\n    default_headers: HeaderMap,\n    timeout: Option<Duration>,\n    connector: Connector<S>,\n    middleware: M,\n    local_address: Option<IpAddr>,\n    max_redirects: u8,\n}\n\nimpl ClientBuilder {\n    /// Create a new ClientBuilder with default settings\n    ///\n    /// Note: If the `rustls-0_23` feature is enabled and neither `rustls-0_23-native-roots` nor\n    /// `rustls-0_23-webpki-roots` are enabled, this ClientBuilder will build without TLS. In order\n    /// to enable TLS in this scenario, a custom `Connector` _must_ be added to the builder before\n    /// finishing construction.\n    #[allow(clippy::new_ret_no_self)]\n    pub fn new() -> ClientBuilder<\n        impl Service<\n                ConnectInfo<Uri>,\n                Response = TcpConnection<Uri, TcpStream>,\n                Error = TcpConnectError,\n            > + Clone,\n        (),\n    > {\n        ClientBuilder {\n            max_http_version: None,\n            stream_window_size: None,\n            conn_window_size: None,\n            fundamental_headers: true,\n            default_headers: HeaderMap::new(),\n            timeout: Some(Duration::from_secs(5)),\n            connector: Connector::new(),\n            middleware: (),\n            local_address: None,\n            max_redirects: 10,\n        }\n    }\n}\n\nimpl<S, Io, M> ClientBuilder<S, M>\nwhere\n    S: Service<ConnectInfo<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>\n        + Clone\n        + 'static,\n    Io: ActixStream + fmt::Debug + 'static,\n{\n    /// Use custom connector service.\n    pub fn connector<S1, Io1>(self, connector: Connector<S1>) -> ClientBuilder<S1, M>\n    where\n        S1: Service<ConnectInfo<Uri>, Response = TcpConnection<Uri, Io1>, Error = TcpConnectError>\n            + Clone\n            + 'static,\n        Io1: ActixStream + fmt::Debug + 'static,\n    {\n        ClientBuilder {\n            middleware: self.middleware,\n            fundamental_headers: self.fundamental_headers,\n            default_headers: self.default_headers,\n            timeout: self.timeout,\n            local_address: self.local_address,\n            connector,\n            max_http_version: self.max_http_version,\n            stream_window_size: self.stream_window_size,\n            conn_window_size: self.conn_window_size,\n            max_redirects: self.max_redirects,\n        }\n    }\n\n    /// Set request timeout\n    ///\n    /// Request timeout is the total time before a response must be received.\n    /// Default value is 5 seconds.\n    pub fn timeout(mut self, timeout: Duration) -> Self {\n        self.timeout = Some(timeout);\n        self\n    }\n\n    /// Disable request timeout.\n    pub fn disable_timeout(mut self) -> Self {\n        self.timeout = None;\n        self\n    }\n\n    /// Set local IP Address the connector would use for establishing connection.\n    pub fn local_address(mut self, addr: IpAddr) -> Self {\n        self.local_address = Some(addr);\n        self\n    }\n\n    /// Maximum supported HTTP major version.\n    ///\n    /// Supported versions are HTTP/1.1 and HTTP/2.\n    pub fn max_http_version(mut self, val: http::Version) -> Self {\n        self.max_http_version = Some(val);\n        self\n    }\n\n    /// Do not follow redirects.\n    ///\n    /// Redirects are allowed by default.\n    pub fn disable_redirects(mut self) -> Self {\n        self.max_redirects = 0;\n        self\n    }\n\n    /// Set max number of redirects.\n    ///\n    /// Max redirects is set to 10 by default.\n    pub fn max_redirects(mut self, num: u8) -> Self {\n        self.max_redirects = num;\n        self\n    }\n\n    /// Indicates the initial window size (in octets) for\n    /// HTTP2 stream-level flow control for received data.\n    ///\n    /// The default value is 65,535 and is good for APIs, but not for big objects.\n    pub fn initial_window_size(mut self, size: u32) -> Self {\n        self.stream_window_size = Some(size);\n        self\n    }\n\n    /// Indicates the initial window size (in octets) for\n    /// HTTP2 connection-level flow control for received data.\n    ///\n    /// The default value is 65,535 and is good for APIs, but not for big objects.\n    pub fn initial_connection_window_size(mut self, size: u32) -> Self {\n        self.conn_window_size = Some(size);\n        self\n    }\n\n    /// Do not add fundamental default request headers.\n    ///\n    /// By default `Date` and `User-Agent` headers are set.\n    pub fn no_default_headers(mut self) -> Self {\n        self.fundamental_headers = false;\n        self\n    }\n\n    /// Add default header.\n    ///\n    /// Headers added by this method get added to every request unless overridden by other methods.\n    ///\n    /// # Panics\n    /// Panics if header name or value is invalid.\n    pub fn add_default_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => self.default_headers.append(key, value),\n            Err(err) => panic!(\"Header error: {:?}\", err.into()),\n        }\n\n        self\n    }\n\n    #[doc(hidden)]\n    #[deprecated(since = \"3.0.0\", note = \"Prefer `add_default_header((key, value))`.\")]\n    pub fn header<K, V>(mut self, key: K, value: V) -> Self\n    where\n        HeaderName: TryFrom<K>,\n        <HeaderName as TryFrom<K>>::Error: fmt::Debug + Into<HttpError>,\n        V: header::TryIntoHeaderValue,\n        V::Error: fmt::Debug,\n    {\n        match HeaderName::try_from(key) {\n            Ok(key) => match value.try_into_value() {\n                Ok(value) => {\n                    self.default_headers.append(key, value);\n                }\n                Err(err) => log::error!(\"Header value error: {:?}\", err),\n            },\n            Err(err) => log::error!(\"Header name error: {:?}\", err),\n        }\n        self\n    }\n\n    /// Set client wide HTTP basic authorization header\n    pub fn basic_auth<N>(self, username: N, password: Option<&str>) -> Self\n    where\n        N: fmt::Display,\n    {\n        let auth = match password {\n            Some(password) => format!(\"{}:{}\", username, password),\n            None => format!(\"{}:\", username),\n        };\n        self.add_default_header((\n            header::AUTHORIZATION,\n            format!(\"Basic {}\", BASE64_STANDARD.encode(auth)),\n        ))\n    }\n\n    /// Set client wide HTTP bearer authentication header\n    pub fn bearer_auth<T>(self, token: T) -> Self\n    where\n        T: fmt::Display,\n    {\n        self.add_default_header((header::AUTHORIZATION, format!(\"Bearer {}\", token)))\n    }\n\n    /// Registers middleware, in the form of a middleware component (type), that runs during inbound\n    /// and/or outbound processing in the request life-cycle (request -> response),\n    /// modifying request/response as necessary, across all requests managed by the `Client`.\n    pub fn wrap<S1, M1>(self, mw: M1) -> ClientBuilder<S, NestTransform<M, M1, S1, ConnectRequest>>\n    where\n        M: Transform<S1, ConnectRequest>,\n        M1: Transform<M::Transform, ConnectRequest>,\n    {\n        ClientBuilder {\n            middleware: NestTransform::new(self.middleware, mw),\n            fundamental_headers: self.fundamental_headers,\n            max_http_version: self.max_http_version,\n            stream_window_size: self.stream_window_size,\n            conn_window_size: self.conn_window_size,\n            default_headers: self.default_headers,\n            timeout: self.timeout,\n            connector: self.connector,\n            local_address: self.local_address,\n            max_redirects: self.max_redirects,\n        }\n    }\n\n    /// Finish build process and create `Client` instance.\n    pub fn finish(self) -> Client\n    where\n        M: Transform<DefaultConnector<ConnectorService<S, Io>>, ConnectRequest> + 'static,\n        M::Transform: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,\n    {\n        let max_redirects = self.max_redirects;\n\n        if max_redirects > 0 {\n            self.wrap(Redirect::new().max_redirect_times(max_redirects))\n                ._finish()\n        } else {\n            self._finish()\n        }\n    }\n\n    fn _finish(self) -> Client\n    where\n        M: Transform<DefaultConnector<ConnectorService<S, Io>>, ConnectRequest> + 'static,\n        M::Transform: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,\n    {\n        let mut connector = self.connector;\n\n        if let Some(val) = self.max_http_version {\n            connector = connector.max_http_version(val);\n        };\n        if let Some(val) = self.conn_window_size {\n            connector = connector.initial_connection_window_size(val)\n        };\n        if let Some(val) = self.stream_window_size {\n            connector = connector.initial_window_size(val)\n        };\n        if let Some(val) = self.local_address {\n            connector = connector.local_address(val);\n        }\n\n        let connector = DefaultConnector::new(connector.finish());\n        let connector = boxed::rc_service(self.middleware.new_transform(connector));\n\n        Client(ClientConfig {\n            default_headers: Rc::new(self.default_headers),\n            timeout: self.timeout,\n            connector,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn client_basic_auth() {\n        let client = ClientBuilder::new().basic_auth(\"username\", Some(\"password\"));\n        assert_eq!(\n            client\n                .default_headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\"\n        );\n\n        let client = ClientBuilder::new().basic_auth(\"username\", None);\n        assert_eq!(\n            client\n                .default_headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6\"\n        );\n    }\n\n    #[test]\n    fn client_bearer_auth() {\n        let client = ClientBuilder::new().bearer_auth(\"someS3cr3tAutht0k3n\");\n        assert_eq!(\n            client\n                .default_headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Bearer someS3cr3tAutht0k3n\"\n        );\n    }\n}\n"
  },
  {
    "path": "awc/src/client/config.rs",
    "content": "use std::{net::IpAddr, time::Duration};\n\nconst DEFAULT_H2_CONN_WINDOW: u32 = 1024 * 1024 * 2; // 2MB\nconst DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB\n\n/// Connector configuration\n#[derive(Clone)]\npub(crate) struct ConnectorConfig {\n    pub(crate) timeout: Duration,\n    pub(crate) handshake_timeout: Duration,\n    pub(crate) conn_lifetime: Duration,\n    pub(crate) conn_keep_alive: Duration,\n    pub(crate) disconnect_timeout: Option<Duration>,\n    pub(crate) limit: usize,\n    pub(crate) conn_window_size: u32,\n    pub(crate) stream_window_size: u32,\n    pub(crate) local_address: Option<IpAddr>,\n}\n\nimpl Default for ConnectorConfig {\n    fn default() -> Self {\n        Self {\n            timeout: Duration::from_secs(5),\n            handshake_timeout: Duration::from_secs(5),\n            conn_lifetime: Duration::from_secs(75),\n            conn_keep_alive: Duration::from_secs(15),\n            disconnect_timeout: Some(Duration::from_millis(3000)),\n            limit: 100,\n            conn_window_size: DEFAULT_H2_CONN_WINDOW,\n            stream_window_size: DEFAULT_H2_STREAM_WINDOW,\n            local_address: None,\n        }\n    }\n}\n\nimpl ConnectorConfig {\n    pub(crate) fn no_disconnect_timeout(&self) -> Self {\n        let mut res = self.clone();\n        res.disconnect_timeout = None;\n        res\n    }\n}\n"
  },
  {
    "path": "awc/src/client/connection.rs",
    "content": "use std::{\n    io,\n    ops::{Deref, DerefMut},\n    pin::Pin,\n    task::{Context, Poll},\n    time,\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};\nuse actix_http::{body::MessageBody, h1::ClientCodec, Payload, RequestHeadType, ResponseHead};\nuse actix_rt::task::JoinHandle;\nuse bytes::Bytes;\nuse futures_core::future::LocalBoxFuture;\nuse h2::client::SendRequest;\n\nuse super::{error::SendRequestError, h1proto, h2proto, pool::Acquired};\nuse crate::BoxError;\n\n/// Trait alias for types impl [tokio::io::AsyncRead] and [tokio::io::AsyncWrite].\npub trait ConnectionIo: AsyncRead + AsyncWrite + Unpin + 'static {}\n\nimpl<T: AsyncRead + AsyncWrite + Unpin + 'static> ConnectionIo for T {}\n\n/// HTTP client connection\npub struct H1Connection<Io: ConnectionIo> {\n    io: Option<Io>,\n    created: time::Instant,\n    acquired: Acquired<Io>,\n}\n\nimpl<Io: ConnectionIo> H1Connection<Io> {\n    /// close or release the connection to pool based on flag input\n    pub(super) fn on_release(&mut self, keep_alive: bool) {\n        if keep_alive {\n            self.release();\n        } else {\n            self.close();\n        }\n    }\n\n    /// Close connection\n    fn close(&mut self) {\n        let io = self.io.take().unwrap();\n        self.acquired.close(ConnectionInnerType::H1(io));\n    }\n\n    /// Release this connection to the connection pool\n    fn release(&mut self) {\n        let io = self.io.take().unwrap();\n        self.acquired\n            .release(ConnectionInnerType::H1(io), self.created);\n    }\n\n    fn io_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Io> {\n        Pin::new(self.get_mut().io.as_mut().unwrap())\n    }\n}\n\nimpl<Io: ConnectionIo> AsyncRead for H1Connection<Io> {\n    fn poll_read(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        self.io_pin_mut().poll_read(cx, buf)\n    }\n}\n\nimpl<Io: ConnectionIo> AsyncWrite for H1Connection<Io> {\n    fn poll_write(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        self.io_pin_mut().poll_write(cx, buf)\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        self.io_pin_mut().poll_flush(cx)\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {\n        self.io_pin_mut().poll_shutdown(cx)\n    }\n\n    fn poll_write_vectored(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        bufs: &[io::IoSlice<'_>],\n    ) -> Poll<io::Result<usize>> {\n        self.io_pin_mut().poll_write_vectored(cx, bufs)\n    }\n\n    fn is_write_vectored(&self) -> bool {\n        self.io.as_ref().unwrap().is_write_vectored()\n    }\n}\n\n/// HTTP2 client connection\npub struct H2Connection<Io: ConnectionIo> {\n    io: Option<H2ConnectionInner>,\n    created: time::Instant,\n    acquired: Acquired<Io>,\n}\n\nimpl<Io: ConnectionIo> Deref for H2Connection<Io> {\n    type Target = SendRequest<Bytes>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.io.as_ref().unwrap().sender\n    }\n}\n\nimpl<Io: ConnectionIo> DerefMut for H2Connection<Io> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.io.as_mut().unwrap().sender\n    }\n}\n\nimpl<Io: ConnectionIo> H2Connection<Io> {\n    /// close or release the connection to pool based on flag input\n    pub(super) fn on_release(&mut self, close: bool) {\n        if close {\n            self.close();\n        } else {\n            self.release();\n        }\n    }\n\n    /// Close connection\n    fn close(&mut self) {\n        let io = self.io.take().unwrap();\n        self.acquired.close(ConnectionInnerType::H2(io));\n    }\n\n    /// Release this connection to the connection pool\n    fn release(&mut self) {\n        let io = self.io.take().unwrap();\n        self.acquired\n            .release(ConnectionInnerType::H2(io), self.created);\n    }\n}\n\n/// `H2ConnectionInner` has two parts: `SendRequest` and `Connection`.\n///\n/// `Connection` is spawned as an async task on runtime and `H2ConnectionInner` holds a handle\n/// for this task. Therefore, it can wake up and quit the task when SendRequest is dropped.\npub(super) struct H2ConnectionInner {\n    handle: JoinHandle<()>,\n    sender: SendRequest<Bytes>,\n}\n\nimpl H2ConnectionInner {\n    pub(super) fn new<Io: ConnectionIo>(\n        sender: SendRequest<Bytes>,\n        connection: h2::client::Connection<Io>,\n    ) -> Self {\n        let handle = actix_rt::spawn(async move {\n            let _ = connection.await;\n        });\n\n        Self { handle, sender }\n    }\n}\n\n/// Cancel spawned connection task on drop.\nimpl Drop for H2ConnectionInner {\n    fn drop(&mut self) {\n        // TODO: this can end up sending extraneous requests; see if there is a better way to handle\n        if self\n            .sender\n            .send_request(http::Request::new(()), true)\n            .is_err()\n        {\n            self.handle.abort();\n        }\n    }\n}\n\n/// Unified connection type cover HTTP/1 Plain/TLS and HTTP/2 protocols.\n#[allow(dead_code)]\npub enum Connection<A, B = Box<dyn ConnectionIo>>\nwhere\n    A: ConnectionIo,\n    B: ConnectionIo,\n{\n    Tcp(ConnectionType<A>),\n    Tls(ConnectionType<B>),\n}\n\n/// Unified connection type cover Http1/2 protocols\npub enum ConnectionType<Io: ConnectionIo> {\n    H1(H1Connection<Io>),\n    H2(H2Connection<Io>),\n}\n\n/// Helper type for storing connection types in pool.\npub(super) enum ConnectionInnerType<Io> {\n    H1(Io),\n    H2(H2ConnectionInner),\n}\n\nimpl<Io: ConnectionIo> ConnectionType<Io> {\n    pub(super) fn from_pool(\n        inner: ConnectionInnerType<Io>,\n        created: time::Instant,\n        acquired: Acquired<Io>,\n    ) -> Self {\n        match inner {\n            ConnectionInnerType::H1(io) => Self::from_h1(io, created, acquired),\n            ConnectionInnerType::H2(io) => Self::from_h2(io, created, acquired),\n        }\n    }\n\n    pub(super) fn from_h1(io: Io, created: time::Instant, acquired: Acquired<Io>) -> Self {\n        Self::H1(H1Connection {\n            io: Some(io),\n            created,\n            acquired,\n        })\n    }\n\n    pub(super) fn from_h2(\n        io: H2ConnectionInner,\n        created: time::Instant,\n        acquired: Acquired<Io>,\n    ) -> Self {\n        Self::H2(H2Connection {\n            io: Some(io),\n            created,\n            acquired,\n        })\n    }\n}\n\nimpl<A, B> Connection<A, B>\nwhere\n    A: ConnectionIo,\n    B: ConnectionIo,\n{\n    /// Send a request through connection.\n    pub fn send_request<RB, H>(\n        self,\n        head: H,\n        body: RB,\n    ) -> LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>>\n    where\n        H: Into<RequestHeadType> + 'static,\n        RB: MessageBody + 'static,\n        RB::Error: Into<BoxError>,\n    {\n        Box::pin(async move {\n            match self {\n                Connection::Tcp(ConnectionType::H1(conn)) => {\n                    h1proto::send_request(conn, head.into(), body).await\n                }\n                Connection::Tls(ConnectionType::H1(conn)) => {\n                    h1proto::send_request(conn, head.into(), body).await\n                }\n                Connection::Tls(ConnectionType::H2(conn)) => {\n                    h2proto::send_request(conn, head.into(), body).await\n                }\n                _ => {\n                    unreachable!(\"Plain TCP connection can be used only with HTTP/1.1 protocol\")\n                }\n            }\n        })\n    }\n\n    /// Send request, returns Response and Framed tunnel.\n    pub fn open_tunnel<H: Into<RequestHeadType> + 'static>(\n        self,\n        head: H,\n    ) -> LocalBoxFuture<\n        'static,\n        Result<(ResponseHead, Framed<Connection<A, B>, ClientCodec>), SendRequestError>,\n    > {\n        Box::pin(async move {\n            match self {\n                Connection::Tcp(ConnectionType::H1(ref _conn)) => {\n                    let (head, framed) = h1proto::open_tunnel(self, head.into()).await?;\n                    Ok((head, framed))\n                }\n                Connection::Tls(ConnectionType::H1(ref _conn)) => {\n                    let (head, framed) = h1proto::open_tunnel(self, head.into()).await?;\n                    Ok((head, framed))\n                }\n                Connection::Tls(ConnectionType::H2(mut conn)) => {\n                    conn.release();\n                    Err(SendRequestError::TunnelNotSupported)\n                }\n                Connection::Tcp(ConnectionType::H2(_)) => {\n                    unreachable!(\"Plain Tcp connection can be used only in Http1 protocol\")\n                }\n            }\n        })\n    }\n}\n\nimpl<A, B> AsyncRead for Connection<A, B>\nwhere\n    A: ConnectionIo,\n    B: ConnectionIo,\n{\n    fn poll_read(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        match self.get_mut() {\n            Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),\n            Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),\n            _ => unreachable!(\"H2Connection can not impl AsyncRead trait\"),\n        }\n    }\n}\n\nconst H2_UNREACHABLE_WRITE: &str = \"H2Connection can not impl AsyncWrite trait\";\n\nimpl<A, B> AsyncWrite for Connection<A, B>\nwhere\n    A: ConnectionIo,\n    B: ConnectionIo,\n{\n    fn poll_write(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        match self.get_mut() {\n            Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),\n            Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),\n            _ => unreachable!(\"{}\", H2_UNREACHABLE_WRITE),\n        }\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        match self.get_mut() {\n            Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx),\n            Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx),\n            _ => unreachable!(\"{}\", H2_UNREACHABLE_WRITE),\n        }\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        match self.get_mut() {\n            Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),\n            Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),\n            _ => unreachable!(\"{}\", H2_UNREACHABLE_WRITE),\n        }\n    }\n\n    fn poll_write_vectored(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        bufs: &[io::IoSlice<'_>],\n    ) -> Poll<io::Result<usize>> {\n        match self.get_mut() {\n            Connection::Tcp(ConnectionType::H1(conn)) => {\n                Pin::new(conn).poll_write_vectored(cx, bufs)\n            }\n            Connection::Tls(ConnectionType::H1(conn)) => {\n                Pin::new(conn).poll_write_vectored(cx, bufs)\n            }\n            _ => unreachable!(\"{}\", H2_UNREACHABLE_WRITE),\n        }\n    }\n\n    fn is_write_vectored(&self) -> bool {\n        match *self {\n            Connection::Tcp(ConnectionType::H1(ref conn)) => conn.is_write_vectored(),\n            Connection::Tls(ConnectionType::H1(ref conn)) => conn.is_write_vectored(),\n            _ => unreachable!(\"{}\", H2_UNREACHABLE_WRITE),\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use std::{\n        future::Future,\n        net,\n        time::{Duration, Instant},\n    };\n\n    use actix_rt::{\n        net::TcpStream,\n        time::{interval, Interval},\n    };\n\n    use super::*;\n\n    #[actix_rt::test]\n    async fn test_h2_connection_drop() {\n        env_logger::try_init().ok();\n\n        let addr = \"127.0.0.1:0\".parse::<net::SocketAddr>().unwrap();\n        let listener = net::TcpListener::bind(addr).unwrap();\n        let local = listener.local_addr().unwrap();\n\n        std::thread::spawn(move || while listener.accept().is_ok() {});\n\n        let tcp = TcpStream::connect(local).await.unwrap();\n        let (sender, connection) = h2::client::handshake(tcp).await.unwrap();\n        let conn = H2ConnectionInner::new(sender.clone(), connection);\n\n        assert!(sender.clone().ready().await.is_ok());\n        assert!(h2::client::SendRequest::clone(&conn.sender)\n            .ready()\n            .await\n            .is_ok());\n\n        drop(conn);\n\n        struct DropCheck {\n            sender: h2::client::SendRequest<Bytes>,\n            interval: Interval,\n            start_from: Instant,\n        }\n\n        impl Future for DropCheck {\n            type Output = ();\n\n            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n                let this = self.get_mut();\n                match futures_core::ready!(this.sender.poll_ready(cx)) {\n                    Ok(()) => {\n                        if this.start_from.elapsed() > Duration::from_secs(10) {\n                            panic!(\"connection should be gone and can not be ready\");\n                        } else {\n                            match this.interval.poll_tick(cx) {\n                                Poll::Ready(_) => {\n                                    // prevents spurious test hang\n                                    this.interval.reset();\n\n                                    Poll::Pending\n                                }\n                                Poll::Pending => Poll::Pending,\n                            }\n                        }\n                    }\n                    Err(_) => Poll::Ready(()),\n                }\n            }\n        }\n\n        DropCheck {\n            sender,\n            interval: interval(Duration::from_millis(100)),\n            start_from: Instant::now(),\n        }\n        .await;\n    }\n}\n"
  },
  {
    "path": "awc/src/client/connector.rs",
    "content": "use std::{\n    fmt,\n    future::Future,\n    net::IpAddr,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n    time::Duration,\n};\n\nuse actix_http::Protocol;\nuse actix_rt::{\n    net::{ActixStream, TcpStream},\n    time::{sleep, Sleep},\n};\nuse actix_service::Service;\nuse actix_tls::connect::{\n    ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection,\n    Connector as TcpConnector, Resolver,\n};\nuse futures_core::{future::LocalBoxFuture, ready};\nuse http::Uri;\nuse pin_project_lite::pin_project;\n\nuse super::{\n    config::ConnectorConfig,\n    connection::{Connection, ConnectionIo},\n    error::ConnectError,\n    pool::ConnectionPool,\n    Connect,\n};\n\nenum OurTlsConnector {\n    #[allow(dead_code)] // only dead when no TLS feature is enabled\n    None,\n\n    #[cfg(feature = \"openssl\")]\n    Openssl(actix_tls::connect::openssl::reexports::SslConnector),\n\n    /// Provided because building the OpenSSL context on newer versions can be very slow.\n    /// This prevents unnecessary calls to `.build()` while constructing the client connector.\n    #[cfg(feature = \"openssl\")]\n    #[allow(dead_code)] // false positive; used in build_tls\n    OpensslBuilder(actix_tls::connect::openssl::reexports::SslConnectorBuilder),\n\n    #[cfg(feature = \"rustls-0_20\")]\n    #[allow(dead_code)] // false positive; used in build_tls\n    Rustls020(std::sync::Arc<actix_tls::connect::rustls_0_20::reexports::ClientConfig>),\n\n    #[cfg(feature = \"rustls-0_21\")]\n    #[allow(dead_code)] // false positive; used in build_tls\n    Rustls021(std::sync::Arc<actix_tls::connect::rustls_0_21::reexports::ClientConfig>),\n\n    #[cfg(any(\n        feature = \"rustls-0_22-webpki-roots\",\n        feature = \"rustls-0_22-native-roots\",\n    ))]\n    #[allow(dead_code)] // false positive; used in build_tls\n    Rustls022(std::sync::Arc<actix_tls::connect::rustls_0_22::reexports::ClientConfig>),\n\n    #[cfg(feature = \"rustls-0_23\")]\n    #[allow(dead_code)] // false positive; used in build_tls\n    Rustls023(std::sync::Arc<actix_tls::connect::rustls_0_23::reexports::ClientConfig>),\n}\n\n/// Manages HTTP client network connectivity.\n///\n/// The `Connector` type uses a builder-like combinator pattern for service construction that\n/// finishes by calling the `.finish()` method.\n///\n/// ```no_run\n/// use std::time::Duration;\n///\n/// let connector = awc::Connector::new()\n///      .timeout(Duration::from_secs(5))\n///      .finish();\n/// ```\npub struct Connector<T> {\n    connector: T,\n    config: ConnectorConfig,\n\n    #[allow(dead_code)] // only dead when no TLS feature is enabled\n    tls: OurTlsConnector,\n}\n\nimpl Connector<()> {\n    /// Create a new connector with default TLS settings\n    ///\n    /// # Panics\n    ///\n    /// - When the `rustls-0_23-webpki-roots` or `rustls-0_23-native-roots` features are enabled\n    ///   and no default crypto provider has been loaded, this method will panic.\n    /// - When the `rustls-0_23-native-roots` or `rustls-0_22-native-roots` features are enabled\n    ///   and the runtime system has no native root certificates, this method will panic.\n    #[allow(clippy::new_ret_no_self, clippy::let_unit_value)]\n    pub fn new() -> Connector<\n        impl Service<\n                ConnectInfo<Uri>,\n                Response = TcpConnection<Uri, TcpStream>,\n                Error = actix_tls::connect::ConnectError,\n            > + Clone,\n    > {\n        Connector {\n            connector: TcpConnector::new(resolver::resolver()).service(),\n            config: ConnectorConfig::default(),\n            tls: Self::build_tls(vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()]),\n        }\n    }\n\n    cfg_if::cfg_if! {\n        if #[cfg(any(feature = \"rustls-0_23-webpki-roots\", feature = \"rustls-0_23-native-roots\"))] {\n            /// Build TLS connector with Rustls v0.23, based on supplied ALPN protocols.\n            ///\n            /// Note that if other TLS crate features are enabled, Rustls v0.23 will be used.\n            fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {\n                use actix_tls::connect::rustls_0_23::{self, reexports::ClientConfig};\n\n                cfg_if::cfg_if! {\n                    if #[cfg(feature = \"rustls-0_23-webpki-roots\")] {\n                        let certs = rustls_0_23::webpki_roots_cert_store();\n                    } else if #[cfg(feature = \"rustls-0_23-native-roots\")] {\n                        let certs = rustls_0_23::native_roots_cert_store().expect(\"Failed to find native root certificates\");\n                    }\n                }\n\n                let mut config = ClientConfig::builder()\n                    .with_root_certificates(certs)\n                    .with_no_client_auth();\n\n                config.alpn_protocols = protocols;\n\n                OurTlsConnector::Rustls023(std::sync::Arc::new(config))\n            }\n        } else if #[cfg(any(feature = \"rustls-0_22-webpki-roots\", feature = \"rustls-0_22-native-roots\"))] {\n            /// Build TLS connector with Rustls v0.22, based on supplied ALPN protocols.\n            fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {\n                use actix_tls::connect::rustls_0_22::{self, reexports::ClientConfig};\n\n                cfg_if::cfg_if! {\n                    if #[cfg(feature = \"rustls-0_22-webpki-roots\")] {\n                        let certs = rustls_0_22::webpki_roots_cert_store();\n                    } else if #[cfg(feature = \"rustls-0_22-native-roots\")] {\n                        let certs = rustls_0_22::native_roots_cert_store().expect(\"Failed to find native root certificates\");\n                    }\n                }\n\n                let mut config = ClientConfig::builder()\n                    .with_root_certificates(certs)\n                    .with_no_client_auth();\n\n                config.alpn_protocols = protocols;\n\n                OurTlsConnector::Rustls022(std::sync::Arc::new(config))\n            }\n        } else if #[cfg(feature = \"rustls-0_21\")] {\n            /// Build TLS connector with Rustls v0.21, based on supplied ALPN protocols.\n            fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {\n                use actix_tls::connect::rustls_0_21::{reexports::ClientConfig, webpki_roots_cert_store};\n\n                let mut config = ClientConfig::builder()\n                    .with_safe_defaults()\n                    .with_root_certificates(webpki_roots_cert_store())\n                    .with_no_client_auth();\n\n                config.alpn_protocols = protocols;\n\n                OurTlsConnector::Rustls021(std::sync::Arc::new(config))\n            }\n        } else if #[cfg(feature = \"rustls-0_20\")] {\n            /// Build TLS connector with Rustls v0.20, based on supplied ALPN protocols.\n            fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {\n                use actix_tls::connect::rustls_0_20::{reexports::ClientConfig, webpki_roots_cert_store};\n\n                let mut config = ClientConfig::builder()\n                    .with_safe_defaults()\n                    .with_root_certificates(webpki_roots_cert_store())\n                    .with_no_client_auth();\n\n                config.alpn_protocols = protocols;\n\n                OurTlsConnector::Rustls020(std::sync::Arc::new(config))\n            }\n        } else if #[cfg(feature = \"openssl\")] {\n            /// Build TLS connector with OpenSSL, based on supplied ALPN protocols.\n            fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {\n                use actix_tls::connect::openssl::reexports::{SslConnector, SslMethod};\n                use bytes::{BufMut, BytesMut};\n\n                let mut alpn = BytesMut::with_capacity(20);\n                for proto in &protocols {\n                    alpn.put_u8(proto.len() as u8);\n                    alpn.put(proto.as_slice());\n                }\n\n                let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();\n                if let Err(err) = ssl.set_alpn_protos(&alpn) {\n                    log::error!(\"Can not set ALPN protocol: {err:?}\");\n                }\n\n                OurTlsConnector::OpensslBuilder(ssl)\n            }\n        } else {\n            /// Provides an empty TLS connector when no TLS feature is enabled, or when only the\n            /// `rustls-0_23` crate feature is enabled.\n            fn build_tls(_: Vec<Vec<u8>>) -> OurTlsConnector {\n                OurTlsConnector::None\n            }\n        }\n    }\n}\n\nimpl<S> Connector<S> {\n    /// Sets custom connector.\n    pub fn connector<S1, Io1>(self, connector: S1) -> Connector<S1>\n    where\n        Io1: ActixStream + fmt::Debug + 'static,\n        S1: Service<ConnectInfo<Uri>, Response = TcpConnection<Uri, Io1>, Error = TcpConnectError>\n            + Clone,\n    {\n        Connector {\n            connector,\n            config: self.config,\n            tls: self.tls,\n        }\n    }\n}\n\nimpl<S, IO> Connector<S>\nwhere\n    // Note:\n    // Input Io type is bound to ActixStream trait but internally in client module they\n    // are bound to ConnectionIo trait alias. And latter is the trait exposed to public\n    // in the form of Box<dyn ConnectionIo> type.\n    //\n    // This remap is to hide ActixStream's trait methods. They are not meant to be called\n    // from user code.\n    IO: ActixStream + fmt::Debug + 'static,\n    S: Service<ConnectInfo<Uri>, Response = TcpConnection<Uri, IO>, Error = TcpConnectError>\n        + Clone\n        + 'static,\n{\n    /// Sets TCP connection timeout.\n    ///\n    /// This is the max time allowed to connect to remote host, including DNS name resolution.\n    ///\n    /// By default, the timeout is 5 seconds.\n    pub fn timeout(mut self, timeout: Duration) -> Self {\n        self.config.timeout = timeout;\n        self\n    }\n\n    /// Sets TLS handshake timeout.\n    ///\n    /// This is the max time allowed to perform the TLS handshake with remote host after TCP\n    /// connection is established.\n    ///\n    /// By default, the timeout is 5 seconds.\n    pub fn handshake_timeout(mut self, timeout: Duration) -> Self {\n        self.config.handshake_timeout = timeout;\n        self\n    }\n\n    /// Sets custom OpenSSL `SslConnector` instance.\n    #[cfg(feature = \"openssl\")]\n    pub fn openssl(\n        mut self,\n        connector: actix_tls::connect::openssl::reexports::SslConnector,\n    ) -> Self {\n        self.tls = OurTlsConnector::Openssl(connector);\n        self\n    }\n\n    /// See docs for [`Connector::openssl`].\n    #[doc(hidden)]\n    #[cfg(feature = \"openssl\")]\n    #[deprecated(since = \"3.0.0\", note = \"Renamed to `Connector::openssl`.\")]\n    pub fn ssl(mut self, connector: actix_tls::connect::openssl::reexports::SslConnector) -> Self {\n        self.tls = OurTlsConnector::Openssl(connector);\n        self\n    }\n\n    /// Sets custom Rustls v0.20 `ClientConfig` instance.\n    #[cfg(feature = \"rustls-0_20\")]\n    pub fn rustls(\n        mut self,\n        connector: std::sync::Arc<actix_tls::connect::rustls_0_20::reexports::ClientConfig>,\n    ) -> Self {\n        self.tls = OurTlsConnector::Rustls020(connector);\n        self\n    }\n\n    /// Sets custom Rustls v0.21 `ClientConfig` instance.\n    #[cfg(feature = \"rustls-0_21\")]\n    pub fn rustls_021(\n        mut self,\n        connector: std::sync::Arc<actix_tls::connect::rustls_0_21::reexports::ClientConfig>,\n    ) -> Self {\n        self.tls = OurTlsConnector::Rustls021(connector);\n        self\n    }\n\n    /// Sets custom Rustls v0.22 `ClientConfig` instance.\n    #[cfg(any(\n        feature = \"rustls-0_22-webpki-roots\",\n        feature = \"rustls-0_22-native-roots\",\n    ))]\n    pub fn rustls_0_22(\n        mut self,\n        connector: std::sync::Arc<actix_tls::connect::rustls_0_22::reexports::ClientConfig>,\n    ) -> Self {\n        self.tls = OurTlsConnector::Rustls022(connector);\n        self\n    }\n\n    /// Sets custom Rustls v0.23 `ClientConfig` instance.\n    ///\n    /// In order to enable ALPN, set the `.alpn_protocols` field on the ClientConfig to the\n    /// following:\n    ///\n    /// ```no_run\n    /// vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()]\n    /// # ;\n    /// ```\n    #[cfg(feature = \"rustls-0_23\")]\n    pub fn rustls_0_23(\n        mut self,\n        connector: std::sync::Arc<actix_tls::connect::rustls_0_23::reexports::ClientConfig>,\n    ) -> Self {\n        self.tls = OurTlsConnector::Rustls023(connector);\n        self\n    }\n\n    /// Sets maximum supported HTTP major version.\n    ///\n    /// Supported versions are HTTP/1.1 and HTTP/2.\n    pub fn max_http_version(mut self, val: http::Version) -> Self {\n        let versions = match val {\n            http::Version::HTTP_11 => vec![b\"http/1.1\".to_vec()],\n            http::Version::HTTP_2 => vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()],\n            _ => {\n                unimplemented!(\"actix-http client only supports versions http/1.1 & http/2\")\n            }\n        };\n        self.tls = Connector::build_tls(versions);\n        self\n    }\n\n    /// Sets the initial window size (in bytes) for HTTP/2 stream-level flow control for received\n    /// data.\n    ///\n    /// The default value is 65,535 and is good for APIs, but not for big objects.\n    pub fn initial_window_size(mut self, size: u32) -> Self {\n        self.config.stream_window_size = size;\n        self\n    }\n\n    /// Sets the initial window size (in bytes) for HTTP/2 connection-level flow control for\n    /// received data.\n    ///\n    /// The default value is 65,535 and is good for APIs, but not for big objects.\n    pub fn initial_connection_window_size(mut self, size: u32) -> Self {\n        self.config.conn_window_size = size;\n        self\n    }\n\n    /// Set total number of simultaneous connections per type of scheme.\n    ///\n    /// If limit is 0, the connector has no limit.\n    ///\n    /// The default limit size is 100.\n    pub fn limit(mut self, limit: usize) -> Self {\n        if limit == 0 {\n            self.config.limit = u32::MAX as usize;\n        } else {\n            self.config.limit = limit;\n        }\n\n        self\n    }\n\n    /// Set keep-alive period for opened connection.\n    ///\n    /// Keep-alive period is the period between connection usage. If\n    /// the delay between repeated usages of the same connection\n    /// exceeds this period, the connection is closed.\n    /// Default keep-alive period is 15 seconds.\n    pub fn conn_keep_alive(mut self, dur: Duration) -> Self {\n        self.config.conn_keep_alive = dur;\n        self\n    }\n\n    /// Set max lifetime period for connection.\n    ///\n    /// Connection lifetime is max lifetime of any opened connection\n    /// until it is closed regardless of keep-alive period.\n    /// Default lifetime period is 75 seconds.\n    pub fn conn_lifetime(mut self, dur: Duration) -> Self {\n        self.config.conn_lifetime = dur;\n        self\n    }\n\n    /// Set server connection disconnect timeout in milliseconds.\n    ///\n    /// Defines a timeout for disconnect connection. If a disconnect procedure does not complete\n    /// within this time, the socket get dropped. This timeout affects only secure connections.\n    ///\n    /// To disable timeout set value to 0.\n    ///\n    /// By default disconnect timeout is set to 3000 milliseconds.\n    pub fn disconnect_timeout(mut self, dur: Duration) -> Self {\n        self.config.disconnect_timeout = Some(dur);\n        self\n    }\n\n    /// Set local IP Address the connector would use for establishing connection.\n    pub fn local_address(mut self, addr: IpAddr) -> Self {\n        self.config.local_address = Some(addr);\n        self\n    }\n\n    /// Finish configuration process and create connector service.\n    ///\n    /// The `Connector` builder always concludes by calling `finish()` last in its combinator chain.\n    pub fn finish(self) -> ConnectorService<S, IO> {\n        let local_address = self.config.local_address;\n        let timeout = self.config.timeout;\n\n        let tcp_service_inner =\n            TcpConnectorInnerService::new(self.connector, timeout, local_address);\n\n        #[allow(clippy::redundant_clone)]\n        let tcp_service = TcpConnectorService {\n            service: tcp_service_inner.clone(),\n        };\n\n        let tls = match self.tls {\n            #[cfg(feature = \"openssl\")]\n            OurTlsConnector::OpensslBuilder(builder) => OurTlsConnector::Openssl(builder.build()),\n            tls => tls,\n        };\n\n        let tls_service = match tls {\n            OurTlsConnector::None => {\n                #[cfg(not(feature = \"dangerous-h2c\"))]\n                {\n                    None\n                }\n\n                #[cfg(feature = \"dangerous-h2c\")]\n                {\n                    use std::io;\n\n                    use actix_tls::connect::Connection;\n                    use actix_utils::future::{ready, Ready};\n\n                    #[allow(non_local_definitions)]\n                    impl IntoConnectionIo for TcpConnection<Uri, Box<dyn ConnectionIo>> {\n                        fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                            let io = self.into_parts().0;\n                            (io, Protocol::Http2)\n                        }\n                    }\n\n                    /// With the `dangerous-h2c` feature enabled, this connector uses a no-op TLS\n                    /// connection service that passes through plain TCP as a TLS connection.\n                    ///\n                    /// The protocol version of this fake TLS connection is set to be HTTP/2.\n                    #[derive(Clone)]\n                    struct NoOpTlsConnectorService;\n\n                    impl<R, IO> Service<Connection<R, IO>> for NoOpTlsConnectorService\n                    where\n                        IO: ActixStream + 'static,\n                    {\n                        type Response = Connection<R, Box<dyn ConnectionIo>>;\n                        type Error = io::Error;\n                        type Future = Ready<Result<Self::Response, Self::Error>>;\n\n                        actix_service::always_ready!();\n\n                        fn call(&self, connection: Connection<R, IO>) -> Self::Future {\n                            let (io, connection) = connection.replace_io(());\n                            let (_, connection) = connection.replace_io(Box::new(io) as _);\n\n                            ready(Ok(connection))\n                        }\n                    }\n\n                    let handshake_timeout = self.config.handshake_timeout;\n\n                    let tls_service = TlsConnectorService {\n                        tcp_service: tcp_service_inner,\n                        tls_service: NoOpTlsConnectorService,\n                        timeout: handshake_timeout,\n                    };\n\n                    Some(actix_service::boxed::rc_service(tls_service))\n                }\n            }\n\n            #[cfg(feature = \"openssl\")]\n            OurTlsConnector::Openssl(tls) => {\n                const H2: &[u8] = b\"h2\";\n\n                use actix_tls::connect::openssl::{reexports::AsyncSslStream, TlsConnector};\n\n                #[allow(non_local_definitions)]\n                impl<IO: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, AsyncSslStream<IO>> {\n                    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                        let sock = self.into_parts().0;\n                        let h2 = sock\n                            .ssl()\n                            .selected_alpn_protocol()\n                            .is_some_and(|protos| protos.windows(2).any(|w| w == H2));\n\n                        if h2 {\n                            (Box::new(sock), Protocol::Http2)\n                        } else {\n                            (Box::new(sock), Protocol::Http1)\n                        }\n                    }\n                }\n\n                let handshake_timeout = self.config.handshake_timeout;\n\n                let tls_service = TlsConnectorService {\n                    tcp_service: tcp_service_inner,\n                    tls_service: TlsConnector::service(tls),\n                    timeout: handshake_timeout,\n                };\n\n                Some(actix_service::boxed::rc_service(tls_service))\n            }\n\n            #[cfg(feature = \"openssl\")]\n            OurTlsConnector::OpensslBuilder(_) => {\n                unreachable!(\"OpenSSL builder is built before this match.\");\n            }\n\n            #[cfg(feature = \"rustls-0_20\")]\n            OurTlsConnector::Rustls020(tls) => {\n                const H2: &[u8] = b\"h2\";\n\n                use actix_tls::connect::rustls_0_20::{reexports::AsyncTlsStream, TlsConnector};\n\n                #[allow(non_local_definitions)]\n                impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, AsyncTlsStream<Io>> {\n                    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                        let sock = self.into_parts().0;\n                        let h2 = sock\n                            .get_ref()\n                            .1\n                            .alpn_protocol()\n                            .is_some_and(|protos| protos.windows(2).any(|w| w == H2));\n\n                        if h2 {\n                            (Box::new(sock), Protocol::Http2)\n                        } else {\n                            (Box::new(sock), Protocol::Http1)\n                        }\n                    }\n                }\n\n                let handshake_timeout = self.config.handshake_timeout;\n\n                let tls_service = TlsConnectorService {\n                    tcp_service: tcp_service_inner,\n                    tls_service: TlsConnector::service(tls),\n                    timeout: handshake_timeout,\n                };\n\n                Some(actix_service::boxed::rc_service(tls_service))\n            }\n\n            #[cfg(feature = \"rustls-0_21\")]\n            OurTlsConnector::Rustls021(tls) => {\n                const H2: &[u8] = b\"h2\";\n\n                use actix_tls::connect::rustls_0_21::{reexports::AsyncTlsStream, TlsConnector};\n\n                #[allow(non_local_definitions)]\n                impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, AsyncTlsStream<Io>> {\n                    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                        let sock = self.into_parts().0;\n                        let h2 = sock\n                            .get_ref()\n                            .1\n                            .alpn_protocol()\n                            .is_some_and(|protos| protos.windows(2).any(|w| w == H2));\n\n                        if h2 {\n                            (Box::new(sock), Protocol::Http2)\n                        } else {\n                            (Box::new(sock), Protocol::Http1)\n                        }\n                    }\n                }\n\n                let handshake_timeout = self.config.handshake_timeout;\n\n                let tls_service = TlsConnectorService {\n                    tcp_service: tcp_service_inner,\n                    tls_service: TlsConnector::service(tls),\n                    timeout: handshake_timeout,\n                };\n\n                Some(actix_service::boxed::rc_service(tls_service))\n            }\n\n            #[cfg(any(\n                feature = \"rustls-0_22-webpki-roots\",\n                feature = \"rustls-0_22-native-roots\",\n            ))]\n            OurTlsConnector::Rustls022(tls) => {\n                const H2: &[u8] = b\"h2\";\n\n                use actix_tls::connect::rustls_0_22::{reexports::AsyncTlsStream, TlsConnector};\n\n                #[allow(non_local_definitions)]\n                impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, AsyncTlsStream<Io>> {\n                    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                        let sock = self.into_parts().0;\n                        let h2 = sock\n                            .get_ref()\n                            .1\n                            .alpn_protocol()\n                            .is_some_and(|protos| protos.windows(2).any(|w| w == H2));\n\n                        if h2 {\n                            (Box::new(sock), Protocol::Http2)\n                        } else {\n                            (Box::new(sock), Protocol::Http1)\n                        }\n                    }\n                }\n\n                let handshake_timeout = self.config.handshake_timeout;\n\n                let tls_service = TlsConnectorService {\n                    tcp_service: tcp_service_inner,\n                    tls_service: TlsConnector::service(tls),\n                    timeout: handshake_timeout,\n                };\n\n                Some(actix_service::boxed::rc_service(tls_service))\n            }\n\n            #[cfg(feature = \"rustls-0_23\")]\n            OurTlsConnector::Rustls023(tls) => {\n                const H2: &[u8] = b\"h2\";\n\n                use actix_tls::connect::rustls_0_23::{reexports::AsyncTlsStream, TlsConnector};\n\n                #[allow(non_local_definitions)]\n                impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, AsyncTlsStream<Io>> {\n                    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {\n                        let sock = self.into_parts().0;\n                        let h2 = sock\n                            .get_ref()\n                            .1\n                            .alpn_protocol()\n                            .is_some_and(|protos| protos.windows(2).any(|w| w == H2));\n\n                        if h2 {\n                            (Box::new(sock), Protocol::Http2)\n                        } else {\n                            (Box::new(sock), Protocol::Http1)\n                        }\n                    }\n                }\n\n                let handshake_timeout = self.config.handshake_timeout;\n\n                let tls_service = TlsConnectorService {\n                    tcp_service: tcp_service_inner,\n                    tls_service: TlsConnector::service(tls),\n                    timeout: handshake_timeout,\n                };\n\n                Some(actix_service::boxed::rc_service(tls_service))\n            }\n        };\n\n        let tcp_config = self.config.no_disconnect_timeout();\n\n        let tcp_pool = ConnectionPool::new(tcp_service, tcp_config);\n\n        let tls_config = self.config;\n        let tls_pool =\n            tls_service.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));\n\n        ConnectorServicePriv { tcp_pool, tls_pool }\n    }\n}\n\n/// tcp service for map `TcpConnection<Uri, Io>` type to `(Io, Protocol)`\n#[derive(Clone)]\npub struct TcpConnectorService<S: Clone> {\n    service: S,\n}\n\nimpl<S, Io> Service<Connect> for TcpConnectorService<S>\nwhere\n    S: Service<Connect, Response = TcpConnection<Uri, Io>, Error = ConnectError> + Clone + 'static,\n{\n    type Response = (Io, Protocol);\n    type Error = ConnectError;\n    type Future = TcpConnectorFuture<S::Future>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: Connect) -> Self::Future {\n        TcpConnectorFuture {\n            fut: self.service.call(req),\n        }\n    }\n}\n\npin_project! {\n    #[project = TcpConnectorFutureProj]\n    pub struct TcpConnectorFuture<Fut> {\n        #[pin]\n        fut: Fut,\n    }\n}\n\nimpl<Fut, Io> Future for TcpConnectorFuture<Fut>\nwhere\n    Fut: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,\n{\n    type Output = Result<(Io, Protocol), ConnectError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        self.project()\n            .fut\n            .poll(cx)\n            .map_ok(|res| (res.into_parts().0, Protocol::Http1))\n    }\n}\n\n/// service for establish tcp connection and do client tls handshake.\n/// operation is canceled when timeout limit reached.\n#[cfg(any(\n    feature = \"dangerous-h2c\",\n    feature = \"openssl\",\n    feature = \"rustls-0_20\",\n    feature = \"rustls-0_21\",\n    feature = \"rustls-0_22-webpki-roots\",\n    feature = \"rustls-0_22-native-roots\",\n    feature = \"rustls-0_23\",\n    feature = \"rustls-0_23-webpki-roots\",\n    feature = \"rustls-0_23-native-roots\"\n))]\nstruct TlsConnectorService<Tcp, Tls> {\n    /// TCP connection is canceled on `TcpConnectorInnerService`'s timeout setting.\n    tcp_service: Tcp,\n\n    /// TLS connection is canceled on `TlsConnectorService`'s timeout setting.\n    tls_service: Tls,\n\n    timeout: Duration,\n}\n\n#[cfg(any(\n    feature = \"dangerous-h2c\",\n    feature = \"openssl\",\n    feature = \"rustls-0_20\",\n    feature = \"rustls-0_21\",\n    feature = \"rustls-0_22-webpki-roots\",\n    feature = \"rustls-0_22-native-roots\",\n    feature = \"rustls-0_23\",\n))]\nimpl<Tcp, Tls, IO> Service<Connect> for TlsConnectorService<Tcp, Tls>\nwhere\n    Tcp:\n        Service<Connect, Response = TcpConnection<Uri, IO>, Error = ConnectError> + Clone + 'static,\n    Tls: Service<TcpConnection<Uri, IO>, Error = std::io::Error> + Clone + 'static,\n    Tls::Response: IntoConnectionIo,\n    IO: ConnectionIo,\n{\n    type Response = (Box<dyn ConnectionIo>, Protocol);\n    type Error = ConnectError;\n    type Future = TlsConnectorFuture<Tls, Tcp::Future, Tls::Future>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        ready!(self.tcp_service.poll_ready(cx))?;\n        ready!(self.tls_service.poll_ready(cx))?;\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&self, req: Connect) -> Self::Future {\n        let fut = self.tcp_service.call(req);\n        let tls_service = self.tls_service.clone();\n        let timeout = self.timeout;\n\n        TlsConnectorFuture::TcpConnect {\n            fut,\n            tls_service: Some(tls_service),\n            timeout,\n        }\n    }\n}\n\npin_project! {\n    #[project = TlsConnectorProj]\n    #[allow(clippy::large_enum_variant)]\n    enum TlsConnectorFuture<S, Fut1, Fut2> {\n        TcpConnect {\n            #[pin]\n            fut: Fut1,\n            tls_service: Option<S>,\n            timeout: Duration,\n        },\n        TlsConnect {\n            #[pin]\n            fut: Fut2,\n            #[pin]\n            timeout: Sleep,\n        },\n    }\n\n}\n/// helper trait for generic over different TlsStream types between tls crates.\ntrait IntoConnectionIo {\n    fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol);\n}\n\nimpl<S, Io, Fut1, Fut2, Res> Future for TlsConnectorFuture<S, Fut1, Fut2>\nwhere\n    S: Service<TcpConnection<Uri, Io>, Response = Res, Error = std::io::Error, Future = Fut2>,\n    S::Response: IntoConnectionIo,\n    Fut1: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,\n    Fut2: Future<Output = Result<S::Response, S::Error>>,\n    Io: ConnectionIo,\n{\n    type Output = Result<(Box<dyn ConnectionIo>, Protocol), ConnectError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.as_mut().project() {\n            TlsConnectorProj::TcpConnect {\n                fut,\n                tls_service,\n                timeout,\n            } => {\n                let res = ready!(fut.poll(cx))?;\n                let fut = tls_service\n                    .take()\n                    .expect(\"TlsConnectorFuture polled after complete\")\n                    .call(res);\n                let timeout = sleep(*timeout);\n                self.set(TlsConnectorFuture::TlsConnect { fut, timeout });\n                self.poll(cx)\n            }\n            TlsConnectorProj::TlsConnect { fut, timeout } => match fut.poll(cx)? {\n                Poll::Ready(res) => Poll::Ready(Ok(res.into_connection_io())),\n                Poll::Pending => timeout.poll(cx).map(|_| Err(ConnectError::Timeout)),\n            },\n        }\n    }\n}\n\n/// service for establish tcp connection.\n/// operation is canceled when timeout limit reached.\n#[derive(Clone)]\npub struct TcpConnectorInnerService<S: Clone> {\n    service: S,\n    timeout: Duration,\n    local_address: Option<std::net::IpAddr>,\n}\n\nimpl<S: Clone> TcpConnectorInnerService<S> {\n    fn new(service: S, timeout: Duration, local_address: Option<std::net::IpAddr>) -> Self {\n        Self {\n            service,\n            timeout,\n            local_address,\n        }\n    }\n}\n\nimpl<S, Io> Service<Connect> for TcpConnectorInnerService<S>\nwhere\n    S: Service<ConnectInfo<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>\n        + Clone\n        + 'static,\n{\n    type Response = S::Response;\n    type Error = ConnectError;\n    type Future = TcpConnectorInnerFuture<S::Future>;\n\n    actix_service::forward_ready!(service);\n\n    fn call(&self, req: Connect) -> Self::Future {\n        let mut req = ConnectInfo::new(req.uri).set_addr(req.addr);\n\n        if let Some(local_addr) = self.local_address {\n            req = req.set_local_addr(local_addr);\n        }\n\n        TcpConnectorInnerFuture {\n            fut: self.service.call(req),\n            timeout: sleep(self.timeout),\n        }\n    }\n}\n\npin_project! {\n    #[project = TcpConnectorInnerFutureProj]\n    pub struct TcpConnectorInnerFuture<Fut> {\n        #[pin]\n        fut: Fut,\n        #[pin]\n        timeout: Sleep,\n    }\n}\n\nimpl<Fut, Io> Future for TcpConnectorInnerFuture<Fut>\nwhere\n    Fut: Future<Output = Result<TcpConnection<Uri, Io>, TcpConnectError>>,\n{\n    type Output = Result<TcpConnection<Uri, Io>, ConnectError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        match this.fut.poll(cx) {\n            Poll::Ready(res) => Poll::Ready(res.map_err(ConnectError::from)),\n            Poll::Pending => this.timeout.poll(cx).map(|_| Err(ConnectError::Timeout)),\n        }\n    }\n}\n\n/// Connector service for pooled Plain/Tls Tcp connections.\npub type ConnectorService<Svc, IO> = ConnectorServicePriv<\n    TcpConnectorService<TcpConnectorInnerService<Svc>>,\n    Rc<\n        dyn Service<\n            Connect,\n            Response = (Box<dyn ConnectionIo>, Protocol),\n            Error = ConnectError,\n            Future = LocalBoxFuture<\n                'static,\n                Result<(Box<dyn ConnectionIo>, Protocol), ConnectError>,\n            >,\n        >,\n    >,\n    IO,\n    Box<dyn ConnectionIo>,\n>;\n\npub struct ConnectorServicePriv<S1, S2, Io1, Io2>\nwhere\n    S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,\n    S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,\n    Io1: ConnectionIo,\n    Io2: ConnectionIo,\n{\n    tcp_pool: ConnectionPool<S1, Io1>,\n    tls_pool: Option<ConnectionPool<S2, Io2>>,\n}\n\nimpl<S1, S2, Io1, Io2> Service<Connect> for ConnectorServicePriv<S1, S2, Io1, Io2>\nwhere\n    S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,\n    S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,\n    Io1: ConnectionIo,\n    Io2: ConnectionIo,\n{\n    type Response = Connection<Io1, Io2>;\n    type Error = ConnectError;\n    type Future = ConnectorServiceFuture<S1, S2, Io1, Io2>;\n\n    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        ready!(self.tcp_pool.poll_ready(cx))?;\n        if let Some(ref tls_pool) = self.tls_pool {\n            ready!(tls_pool.poll_ready(cx))?;\n        }\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&self, req: Connect) -> Self::Future {\n        match req.uri.scheme_str() {\n            Some(\"https\") | Some(\"wss\") => match self.tls_pool {\n                None => ConnectorServiceFuture::SslIsNotSupported,\n                Some(ref pool) => ConnectorServiceFuture::Tls {\n                    fut: pool.call(req),\n                },\n            },\n            _ => ConnectorServiceFuture::Tcp {\n                fut: self.tcp_pool.call(req),\n            },\n        }\n    }\n}\n\npin_project! {\n    #[project = ConnectorServiceFutureProj]\n    pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>\n    where\n        S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,\n        S1: Clone,\n        S1: 'static,\n        S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,\n        S2: Clone,\n        S2: 'static,\n        Io1: ConnectionIo,\n        Io2: ConnectionIo,\n    {\n        Tcp {\n            #[pin]\n            fut: <ConnectionPool<S1, Io1> as Service<Connect>>::Future\n        },\n        Tls {\n            #[pin]\n            fut:  <ConnectionPool<S2, Io2> as Service<Connect>>::Future\n        },\n        SslIsNotSupported\n    }\n}\n\nimpl<S1, S2, Io1, Io2> Future for ConnectorServiceFuture<S1, S2, Io1, Io2>\nwhere\n    S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,\n    S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,\n    Io1: ConnectionIo,\n    Io2: ConnectionIo,\n{\n    type Output = Result<Connection<Io1, Io2>, ConnectError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.project() {\n            ConnectorServiceFutureProj::Tcp { fut } => fut.poll(cx).map_ok(Connection::Tcp),\n            ConnectorServiceFutureProj::Tls { fut } => fut.poll(cx).map_ok(Connection::Tls),\n            ConnectorServiceFutureProj::SslIsNotSupported => {\n                Poll::Ready(Err(ConnectError::SslIsNotSupported))\n            }\n        }\n    }\n}\n\n#[cfg(not(feature = \"hickory-dns\"))]\nmod resolver {\n    use super::*;\n\n    pub(super) fn resolver() -> Resolver {\n        Resolver::default()\n    }\n}\n\n#[cfg(feature = \"hickory-dns\")]\nmod resolver {\n    use std::{cell::OnceCell, net::SocketAddr};\n\n    use actix_tls::connect::Resolve;\n    use hickory_resolver::{\n        config::{ResolverConfig, ResolverOpts},\n        name_server::TokioConnectionProvider,\n        system_conf::read_system_conf,\n        TokioResolver,\n    };\n\n    use super::*;\n\n    pub(super) fn resolver() -> Resolver {\n        // new type for impl Resolve trait for TokioAsyncResolver.\n        struct HickoryDnsResolver(TokioResolver);\n\n        impl Resolve for HickoryDnsResolver {\n            fn lookup<'a>(\n                &'a self,\n                host: &'a str,\n                port: u16,\n            ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>>\n            {\n                Box::pin(async move {\n                    let res = self\n                        .0\n                        .lookup_ip(host)\n                        .await?\n                        .iter()\n                        .map(|ip| SocketAddr::new(ip, port))\n                        .collect();\n                    Ok(res)\n                })\n            }\n        }\n\n        // resolver struct is cached in thread local so new clients can reuse the existing instance\n        thread_local! {\n            static HICKORY_DNS_RESOLVER: OnceCell<Resolver> = const { OnceCell::new() };\n        }\n\n        // get from thread local or construct a new hickory dns resolver.\n        HICKORY_DNS_RESOLVER.with(|local| {\n            local\n                .get_or_init(|| {\n                    let (cfg, opts) = match read_system_conf() {\n                        Ok((cfg, opts)) => (cfg, opts),\n                        Err(err) => {\n                            log::error!(\"Hickory DNS can not load system config: {err}\");\n                            (ResolverConfig::default(), ResolverOpts::default())\n                        }\n                    };\n\n                    let resolver =\n                        TokioResolver::builder_with_config(cfg, TokioConnectionProvider::default())\n                            .with_options(opts)\n                            .build();\n\n                    Resolver::custom(HickoryDnsResolver(resolver))\n                })\n                .clone()\n        })\n    }\n}\n\n#[cfg(feature = \"dangerous-h2c\")]\n#[cfg(test)]\nmod tests {\n    use std::convert::Infallible;\n\n    use actix_http::{HttpService, Request, Response, Version};\n    use actix_http_test::test_server;\n    use actix_service::ServiceFactoryExt as _;\n\n    use super::*;\n    use crate::Client;\n\n    #[actix_rt::test]\n    async fn h2c_connector() {\n        let mut srv = test_server(|| {\n            HttpService::build()\n                .h2(|_req: Request| async { Ok::<_, Infallible>(Response::ok()) })\n                .tcp()\n                .map_err(|_| ())\n        })\n        .await;\n\n        let connector = Connector {\n            connector: TcpConnector::new(resolver::resolver()).service(),\n            config: ConnectorConfig::default(),\n            tls: OurTlsConnector::None,\n        };\n\n        let client = Client::builder().connector(connector).finish();\n\n        let request = client.get(srv.surl(\"/\")).send();\n        let response = request.await.unwrap();\n        assert!(response.status().is_success());\n        assert_eq!(response.version(), Version::HTTP_2);\n\n        srv.stop().await;\n    }\n}\n"
  },
  {
    "path": "awc/src/client/error.rs",
    "content": "use std::{fmt, io};\n\nuse actix_http::error::{HttpError, ParseError};\n#[cfg(feature = \"openssl\")]\nuse actix_tls::accept::openssl::reexports::Error as OpensslError;\nuse derive_more::{Display, From};\n\nuse crate::BoxError;\n\n/// A set of errors that can occur while connecting to an HTTP host\n#[derive(Debug, Display, From)]\n#[non_exhaustive]\npub enum ConnectError {\n    /// SSL feature is not enabled\n    #[display(\"SSL is not supported\")]\n    SslIsNotSupported,\n\n    /// SSL error\n    #[cfg(feature = \"openssl\")]\n    #[display(\"{}\", _0)]\n    SslError(OpensslError),\n\n    /// Failed to resolve the hostname\n    #[display(\"Failed resolving hostname: {}\", _0)]\n    Resolver(Box<dyn std::error::Error>),\n\n    /// No dns records\n    #[display(\"No DNS records found for the input\")]\n    NoRecords,\n\n    /// Http2 error\n    #[display(\"{}\", _0)]\n    H2(h2::Error),\n\n    /// Connecting took too long\n    #[display(\"Timeout while establishing connection\")]\n    Timeout,\n\n    /// Connector has been disconnected\n    #[display(\"Internal error: connector has been disconnected\")]\n    Disconnected,\n\n    /// Unresolved host name\n    #[display(\"Connector received `Connect` method with unresolved host\")]\n    Unresolved,\n\n    /// Connection io error\n    #[display(\"{}\", _0)]\n    Io(io::Error),\n}\n\nimpl std::error::Error for ConnectError {}\n\nimpl From<actix_tls::connect::ConnectError> for ConnectError {\n    fn from(err: actix_tls::connect::ConnectError) -> ConnectError {\n        match err {\n            actix_tls::connect::ConnectError::Resolver(err) => ConnectError::Resolver(err),\n            actix_tls::connect::ConnectError::NoRecords => ConnectError::NoRecords,\n            actix_tls::connect::ConnectError::InvalidInput => panic!(),\n            actix_tls::connect::ConnectError::Unresolved => ConnectError::Unresolved,\n            actix_tls::connect::ConnectError::Io(err) => ConnectError::Io(err),\n        }\n    }\n}\n\n#[derive(Debug, Display, From)]\n#[non_exhaustive]\npub enum InvalidUrl {\n    #[display(\"Missing URL scheme\")]\n    MissingScheme,\n\n    #[display(\"Unknown URL scheme\")]\n    UnknownScheme,\n\n    #[display(\"Missing host name\")]\n    MissingHost,\n\n    #[display(\"URL parse error: {}\", _0)]\n    HttpError(http::Error),\n}\n\nimpl std::error::Error for InvalidUrl {}\n\n/// A set of errors that can occur during request sending and response reading\n#[derive(Debug, Display, From)]\n#[non_exhaustive]\npub enum SendRequestError {\n    /// Invalid URL\n    #[display(\"Invalid URL: {}\", _0)]\n    Url(InvalidUrl),\n\n    /// Failed to connect to host\n    #[display(\"Failed to connect to host: {}\", _0)]\n    Connect(ConnectError),\n\n    /// Error sending request\n    Send(io::Error),\n\n    /// Error parsing response\n    Response(ParseError),\n\n    /// Http error\n    #[display(\"{}\", _0)]\n    Http(HttpError),\n\n    /// Http2 error\n    #[display(\"{}\", _0)]\n    H2(h2::Error),\n\n    /// Response took too long\n    #[display(\"Timeout while waiting for response\")]\n    Timeout,\n\n    /// Tunnels are not supported for HTTP/2 connection\n    #[display(\"Tunnels are not supported for http2 connection\")]\n    TunnelNotSupported,\n\n    /// Error sending request body\n    Body(BoxError),\n\n    /// Other errors that can occur after submitting a request.\n    #[display(\"{:?}: {}\", _1, _0)]\n    Custom(BoxError, Box<dyn fmt::Debug>),\n}\n\nimpl std::error::Error for SendRequestError {}\n\n/// A set of errors that can occur during freezing a request\n#[derive(Debug, Display, From)]\n#[non_exhaustive]\npub enum FreezeRequestError {\n    /// Invalid URL\n    #[display(\"Invalid URL: {}\", _0)]\n    Url(InvalidUrl),\n\n    /// HTTP error\n    #[display(\"{}\", _0)]\n    Http(HttpError),\n\n    /// Other errors that can occur after submitting a request.\n    #[display(\"{:?}: {}\", _1, _0)]\n    Custom(BoxError, Box<dyn fmt::Debug>),\n}\n\nimpl std::error::Error for FreezeRequestError {}\n\nimpl From<FreezeRequestError> for SendRequestError {\n    fn from(err: FreezeRequestError) -> Self {\n        match err {\n            FreezeRequestError::Url(err) => err.into(),\n            FreezeRequestError::Http(err) => err.into(),\n            FreezeRequestError::Custom(err, msg) => SendRequestError::Custom(err, msg),\n        }\n    }\n}\n"
  },
  {
    "path": "awc/src/client/h1proto.rs",
    "content": "use std::{\n    io::Write,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_codec::Framed;\nuse actix_http::{\n    body::{BodySize, MessageBody},\n    error::PayloadError,\n    h1,\n    header::{HeaderMap, TryIntoHeaderValue, EXPECT, HOST},\n    Payload, RequestHeadType, ResponseHead, StatusCode,\n};\nuse actix_utils::future::poll_fn;\nuse bytes::{buf::BufMut, Bytes, BytesMut};\nuse futures_core::{ready, Stream};\nuse futures_util::SinkExt as _;\nuse pin_project_lite::pin_project;\n\nuse super::{\n    connection::{ConnectionIo, H1Connection},\n    error::{ConnectError, SendRequestError},\n};\nuse crate::BoxError;\n\npub(crate) async fn send_request<Io, B>(\n    io: H1Connection<Io>,\n    mut head: RequestHeadType,\n    body: B,\n) -> Result<(ResponseHead, Payload), SendRequestError>\nwhere\n    Io: ConnectionIo,\n    B: MessageBody,\n    B::Error: Into<BoxError>,\n{\n    actix_rt::pin!(body);\n\n    let orig_length = body.size();\n    let mut length = orig_length;\n    let mut first_chunk = None;\n\n    // This avoids sending `Transfer-Encoding: chunked` for requests with an empty body stream.\n    // https://github.com/actix/actix-web/issues/2320\n    if matches!(orig_length, BodySize::Stream) {\n        enum Peek<E> {\n            Pending,\n            Item(Result<Bytes, E>),\n            Eof,\n        }\n\n        match poll_fn(|cx| match body.as_mut().poll_next(cx) {\n            Poll::Pending => Poll::Ready(Peek::Pending),\n            Poll::Ready(Some(res)) => Poll::Ready(Peek::Item(res)),\n            Poll::Ready(None) => Poll::Ready(Peek::Eof),\n        })\n        .await\n        {\n            Peek::Pending => {}\n            Peek::Eof => length = BodySize::Sized(0),\n            Peek::Item(Ok(chunk)) => first_chunk = Some(chunk),\n            Peek::Item(Err(err)) => return Err(SendRequestError::Body(err.into())),\n        }\n    }\n\n    // set request host header\n    if !head.as_ref().headers.contains_key(HOST)\n        && !head.extra_headers().iter().any(|h| h.contains_key(HOST))\n    {\n        if let Some(host) = head.as_ref().uri.host() {\n            let mut wrt = BytesMut::with_capacity(host.len() + 5).writer();\n\n            match head.as_ref().uri.port_u16() {\n                None | Some(80) | Some(443) => write!(wrt, \"{}\", host)?,\n                Some(port) => write!(wrt, \"{}:{}\", host, port)?,\n            };\n\n            match wrt.get_mut().split().freeze().try_into_value() {\n                Ok(value) => match head {\n                    RequestHeadType::Owned(ref mut head) => {\n                        head.headers.insert(HOST, value);\n                    }\n                    RequestHeadType::Rc(_, ref mut extra_headers) => {\n                        let headers = extra_headers.get_or_insert(HeaderMap::new());\n                        headers.insert(HOST, value);\n                    }\n                },\n                Err(err) => log::error!(\"Can not set HOST header {err}\"),\n            }\n        }\n    }\n\n    // create Framed and prepare sending request\n    let mut framed = Framed::new(io, h1::ClientCodec::default());\n\n    // Check EXPECT header and enable expect handle flag accordingly.\n    // See https://datatracker.ietf.org/doc/html/rfc7231#section-5.1.1\n    let is_expect = if head.as_ref().headers.contains_key(EXPECT) {\n        match orig_length {\n            BodySize::None | BodySize::Sized(0) => {\n                let keep_alive = framed.codec_ref().keep_alive();\n                framed.io_mut().on_release(keep_alive);\n\n                // TODO: use a new variant or a new type better describing error violate\n                // `Requirements for clients` session of above RFC\n                return Err(SendRequestError::Connect(ConnectError::Disconnected));\n            }\n            _ => true,\n        }\n    } else {\n        false\n    };\n\n    let mut pin_framed = Pin::new(&mut framed);\n\n    // special handle for EXPECT request.\n    let (do_send, mut res_head) = if is_expect {\n        pin_framed.send((head, length).into()).await?;\n\n        let head = poll_fn(|cx| pin_framed.as_mut().poll_next(cx))\n            .await\n            .ok_or(ConnectError::Disconnected)??;\n\n        // return response head in case status code is not continue\n        // and current head would be used as final response head.\n        (head.status == StatusCode::CONTINUE, Some(head))\n    } else {\n        pin_framed.feed((head, length).into()).await?;\n\n        (true, None)\n    };\n\n    if do_send {\n        // send request body\n        match length {\n            BodySize::None | BodySize::Sized(0) => {\n                poll_fn(|cx| pin_framed.as_mut().flush(cx)).await?;\n            }\n            _ => send_body(body.as_mut(), pin_framed.as_mut(), first_chunk).await?,\n        };\n\n        // read response and init read body\n        let head = poll_fn(|cx| pin_framed.as_mut().poll_next(cx))\n            .await\n            .ok_or(ConnectError::Disconnected)??;\n\n        res_head = Some(head);\n    }\n\n    let head = res_head.unwrap();\n\n    match pin_framed.codec_ref().message_type() {\n        h1::MessageType::None => {\n            let keep_alive = pin_framed.codec_ref().keep_alive();\n            pin_framed.io_mut().on_release(keep_alive);\n\n            Ok((head, Payload::None))\n        }\n        _ => Ok((\n            head,\n            Payload::Stream {\n                payload: Box::pin(PlStream::new(framed)),\n            },\n        )),\n    }\n}\n\npub(crate) async fn open_tunnel<Io>(\n    io: Io,\n    head: RequestHeadType,\n) -> Result<(ResponseHead, Framed<Io, h1::ClientCodec>), SendRequestError>\nwhere\n    Io: ConnectionIo,\n{\n    // create Framed and send request.\n    let mut framed = Framed::new(io, h1::ClientCodec::default());\n    framed.send((head, BodySize::None).into()).await?;\n\n    // read response head.\n    let head = poll_fn(|cx| Pin::new(&mut framed).poll_next(cx))\n        .await\n        .ok_or(ConnectError::Disconnected)??;\n\n    Ok((head, framed))\n}\n\n/// send request body to the peer\npub(crate) async fn send_body<Io, B>(\n    mut body: Pin<&mut B>,\n    mut framed: Pin<&mut Framed<Io, h1::ClientCodec>>,\n    first_chunk: Option<Bytes>,\n) -> Result<(), SendRequestError>\nwhere\n    Io: ConnectionIo,\n    B: MessageBody,\n    B::Error: Into<BoxError>,\n{\n    if let Some(chunk) = first_chunk {\n        framed.as_mut().write(h1::Message::Chunk(Some(chunk)))?;\n    }\n\n    let mut eof = false;\n    while !eof {\n        while !eof && !framed.as_ref().is_write_buf_full() {\n            match poll_fn(|cx| body.as_mut().poll_next(cx)).await {\n                Some(Ok(chunk)) => {\n                    framed.as_mut().write(h1::Message::Chunk(Some(chunk)))?;\n                }\n                Some(Err(err)) => return Err(SendRequestError::Body(err.into())),\n                None => {\n                    eof = true;\n                    framed.as_mut().write(h1::Message::Chunk(None))?;\n                }\n            }\n        }\n\n        if !framed.as_ref().is_write_buf_empty() {\n            poll_fn(|cx| match framed.as_mut().flush(cx) {\n                Poll::Ready(Ok(_)) => Poll::Ready(Ok(())),\n                Poll::Ready(Err(err)) => Poll::Ready(Err(err)),\n                Poll::Pending => {\n                    if !framed.as_ref().is_write_buf_full() {\n                        Poll::Ready(Ok(()))\n                    } else {\n                        Poll::Pending\n                    }\n                }\n            })\n            .await?;\n        }\n    }\n\n    framed.get_mut().flush().await?;\n    Ok(())\n}\n\npin_project! {\n    pub(crate) struct PlStream<Io: ConnectionIo> {\n        #[pin]\n        framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,\n    }\n}\n\nimpl<Io: ConnectionIo> PlStream<Io> {\n    fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {\n        let framed = framed.into_map_codec(|codec| codec.into_payload_codec());\n\n        PlStream { framed }\n    }\n}\n\nimpl<Io: ConnectionIo> Stream for PlStream<Io> {\n    type Item = Result<Bytes, PayloadError>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let mut this = self.project();\n\n        match ready!(this.framed.as_mut().next_item(cx)?) {\n            Some(Some(chunk)) => Poll::Ready(Some(Ok(chunk))),\n            Some(None) => {\n                let keep_alive = this.framed.codec_ref().keep_alive();\n                this.framed.io_mut().on_release(keep_alive);\n                Poll::Ready(None)\n            }\n            None => Poll::Ready(None),\n        }\n    }\n}\n"
  },
  {
    "path": "awc/src/client/h2proto.rs",
    "content": "use std::future::Future;\n\nuse actix_http::{\n    body::{BodySize, MessageBody},\n    header::HeaderMap,\n    Payload, RequestHeadType, ResponseHead,\n};\nuse actix_utils::future::poll_fn;\nuse bytes::Bytes;\nuse h2::{\n    client::{Builder, Connection, SendRequest},\n    SendStream,\n};\nuse http::{\n    header::{HeaderValue, CONNECTION, CONTENT_LENGTH, HOST, TRANSFER_ENCODING},\n    request::Request,\n    Method, Version,\n};\nuse log::trace;\n\nuse super::{\n    config::ConnectorConfig,\n    connection::{ConnectionIo, H2Connection},\n    error::SendRequestError,\n};\nuse crate::BoxError;\n\npub(crate) async fn send_request<Io, B>(\n    mut io: H2Connection<Io>,\n    head: RequestHeadType,\n    body: B,\n) -> Result<(ResponseHead, Payload), SendRequestError>\nwhere\n    Io: ConnectionIo,\n    B: MessageBody,\n    B::Error: Into<BoxError>,\n{\n    trace!(\"Sending client request: {:?} {:?}\", head, body.size());\n\n    let head_req = head.as_ref().method == Method::HEAD;\n    let length = body.size();\n    let eof = matches!(length, BodySize::None | BodySize::Sized(0));\n\n    let mut req = Request::new(());\n    *req.uri_mut() = head.as_ref().uri.clone();\n    *req.method_mut() = head.as_ref().method.clone();\n    *req.version_mut() = Version::HTTP_2;\n\n    let mut skip_len = true;\n    // let mut has_date = false;\n\n    // Content length\n    let _ = match length {\n        BodySize::None => None,\n\n        BodySize::Sized(0) => {\n            #[allow(clippy::declare_interior_mutable_const)]\n            const HV_ZERO: HeaderValue = HeaderValue::from_static(\"0\");\n            req.headers_mut().insert(CONTENT_LENGTH, HV_ZERO)\n        }\n\n        BodySize::Sized(len) => {\n            let mut buf = itoa::Buffer::new();\n\n            req.headers_mut().insert(\n                CONTENT_LENGTH,\n                HeaderValue::from_str(buf.format(len)).unwrap(),\n            )\n        }\n\n        BodySize::Stream => {\n            skip_len = false;\n            None\n        }\n    };\n\n    // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate.\n    let (head, extra_headers) = match head {\n        RequestHeadType::Owned(head) => (RequestHeadType::Owned(head), HeaderMap::new()),\n        RequestHeadType::Rc(head, extra_headers) => (\n            RequestHeadType::Rc(head, None),\n            extra_headers.unwrap_or_else(HeaderMap::new),\n        ),\n    };\n\n    // merging headers from head and extra headers.\n    let headers = head\n        .as_ref()\n        .headers\n        .iter()\n        .filter(|(name, _)| !extra_headers.contains_key(*name))\n        .chain(extra_headers.iter());\n\n    // copy headers\n    for (key, value) in headers {\n        match *key {\n            // TODO: consider skipping other headers according to:\n            //       https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2\n            // omit HTTP/1.x only headers\n            CONNECTION | TRANSFER_ENCODING | HOST => continue,\n            CONTENT_LENGTH if skip_len => continue,\n            // DATE => has_date = true,\n            _ => {}\n        }\n        req.headers_mut().append(key, value.clone());\n    }\n\n    let res = poll_fn(|cx| io.poll_ready(cx)).await;\n    if let Err(err) = res {\n        io.on_release(err.is_io() || err.is_go_away());\n        return Err(SendRequestError::from(err));\n    }\n\n    let resp = match io.send_request(req, eof) {\n        Ok((fut, send)) => {\n            io.on_release(false);\n\n            if !eof {\n                send_body(body, send).await?;\n            }\n            fut.await.map_err(SendRequestError::from)?\n        }\n        Err(err) => {\n            io.on_release(err.is_io() || err.is_go_away());\n            return Err(err.into());\n        }\n    };\n\n    let (parts, body) = resp.into_parts();\n    let payload = if head_req { Payload::None } else { body.into() };\n\n    let mut head = ResponseHead::new(parts.status);\n    head.version = parts.version;\n    head.headers = parts.headers.into();\n    Ok((head, payload))\n}\n\nasync fn send_body<B>(body: B, mut send: SendStream<Bytes>) -> Result<(), SendRequestError>\nwhere\n    B: MessageBody,\n    B::Error: Into<BoxError>,\n{\n    let mut buf = None;\n\n    actix_rt::pin!(body);\n\n    loop {\n        if buf.is_none() {\n            match poll_fn(|cx| body.as_mut().poll_next(cx)).await {\n                Some(Ok(b)) => {\n                    send.reserve_capacity(b.len());\n                    buf = Some(b);\n                }\n                Some(Err(err)) => return Err(SendRequestError::Body(err.into())),\n                None => {\n                    if let Err(err) = send.send_data(Bytes::new(), true) {\n                        return Err(err.into());\n                    }\n                    send.reserve_capacity(0);\n                    return Ok(());\n                }\n            }\n        }\n\n        match poll_fn(|cx| send.poll_capacity(cx)).await {\n            None => return Ok(()),\n            Some(Ok(cap)) => {\n                let b = buf.as_mut().unwrap();\n                let len = b.len();\n                let bytes = b.split_to(std::cmp::min(cap, len));\n\n                if let Err(err) = send.send_data(bytes, false) {\n                    return Err(err.into());\n                }\n                if !b.is_empty() {\n                    send.reserve_capacity(b.len());\n                } else {\n                    buf = None;\n                }\n                continue;\n            }\n            Some(Err(err)) => return Err(err.into()),\n        }\n    }\n}\n\npub(crate) fn handshake<Io: ConnectionIo>(\n    io: Io,\n    config: &ConnectorConfig,\n) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>> {\n    let mut builder = Builder::new();\n    builder\n        .initial_window_size(config.stream_window_size)\n        .initial_connection_window_size(config.conn_window_size)\n        .enable_push(false);\n    builder.handshake(io)\n}\n"
  },
  {
    "path": "awc/src/client/mod.rs",
    "content": "//! HTTP client.\n\nuse std::{rc::Rc, time::Duration};\n\nuse actix_http::{error::HttpError, header::HeaderMap, Method, RequestHead, Uri};\nuse actix_rt::net::TcpStream;\nuse actix_service::Service;\npub use actix_tls::connect::{\n    ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection,\n};\n\nuse crate::{ws, BoxConnectorService, ClientBuilder, ClientRequest};\n\nmod config;\nmod connection;\nmod connector;\nmod error;\nmod h1proto;\nmod h2proto;\nmod pool;\n\npub use self::{\n    connection::{Connection, ConnectionIo},\n    connector::{Connector, ConnectorService},\n    error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError},\n};\n\n#[derive(Clone)]\npub struct Connect {\n    pub uri: Uri,\n    pub addr: Option<std::net::SocketAddr>,\n}\n\n/// An asynchronous HTTP and WebSocket client.\n///\n/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU\n/// and memory usage.\n///\n/// # Examples\n/// ```\n/// use awc::Client;\n///\n/// #[actix_rt::main]\n/// async fn main() {\n///     let mut client = Client::default();\n///\n///     let res = client.get(\"http://www.rust-lang.org\")\n///         .insert_header((\"User-Agent\", \"my-app/1.2\"))\n///         .send()\n///         .await;\n///\n///      println!(\"Response: {:?}\", res);\n/// }\n/// ```\n#[derive(Clone)]\npub struct Client(pub(crate) ClientConfig);\n\n#[derive(Clone)]\npub(crate) struct ClientConfig {\n    pub(crate) connector: BoxConnectorService,\n    pub(crate) default_headers: Rc<HeaderMap>,\n    pub(crate) timeout: Option<Duration>,\n}\n\nimpl Default for Client {\n    fn default() -> Self {\n        ClientBuilder::new().finish()\n    }\n}\n\nimpl Client {\n    /// Constructs new client instance with default settings.\n    pub fn new() -> Client {\n        Client::default()\n    }\n\n    /// Constructs new `Client` builder.\n    ///\n    /// This function is equivalent of `ClientBuilder::new()`.\n    pub fn builder() -> ClientBuilder<\n        impl Service<\n                ConnectInfo<Uri>,\n                Response = TcpConnection<Uri, TcpStream>,\n                Error = TcpConnectError,\n            > + Clone,\n    > {\n        ClientBuilder::new()\n    }\n\n    /// Construct HTTP request.\n    pub fn request<U>(&self, method: Method, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        let mut req = ClientRequest::new(method, url, self.0.clone());\n\n        for header in self.0.default_headers.iter() {\n            req = req.append_header(header);\n        }\n\n        req\n    }\n\n    /// Create `ClientRequest` from `RequestHead`\n    ///\n    /// It is useful for proxy requests. This implementation\n    /// copies all headers and the method.\n    pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        let mut req = self.request(head.method.clone(), url);\n        for header in head.headers.iter() {\n            req = req.insert_header_if_none(header);\n        }\n        req\n    }\n\n    /// Construct HTTP *GET* request.\n    pub fn get<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::GET, url)\n    }\n\n    /// Construct HTTP *HEAD* request.\n    pub fn head<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::HEAD, url)\n    }\n\n    /// Construct HTTP *PUT* request.\n    pub fn put<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::PUT, url)\n    }\n\n    /// Construct HTTP *POST* request.\n    pub fn post<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::POST, url)\n    }\n\n    /// Construct HTTP *PATCH* request.\n    pub fn patch<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::PATCH, url)\n    }\n\n    /// Construct HTTP *DELETE* request.\n    pub fn delete<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::DELETE, url)\n    }\n\n    /// Construct HTTP *OPTIONS* request.\n    pub fn options<U>(&self, url: U) -> ClientRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        self.request(Method::OPTIONS, url)\n    }\n\n    /// Initialize a WebSocket connection.\n    /// Returns a WebSocket connection builder.\n    pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        let mut req = ws::WebsocketsRequest::new(url, self.0.clone());\n        for (key, value) in self.0.default_headers.iter() {\n            req.head.headers.insert(key.clone(), value.clone());\n        }\n        req\n    }\n\n    /// Get default HeaderMap of Client.\n    ///\n    /// Returns Some(&mut HeaderMap) when Client object is unique\n    /// (No other clone of client exists at the same time).\n    pub fn headers(&mut self) -> Option<&mut HeaderMap> {\n        Rc::get_mut(&mut self.0.default_headers)\n    }\n}\n"
  },
  {
    "path": "awc/src/client/pool.rs",
    "content": "//! Client connection pooling keyed on the authority part of the connection URI.\n\nuse std::{\n    cell::RefCell,\n    collections::{HashMap, VecDeque},\n    future::Future,\n    io,\n    ops::Deref,\n    pin::Pin,\n    rc::Rc,\n    sync::Arc,\n    task::{Context, Poll},\n    time::{Duration, Instant},\n};\n\nuse actix_codec::{AsyncRead, AsyncWrite, ReadBuf};\nuse actix_http::Protocol;\nuse actix_rt::time::{sleep, Sleep};\nuse actix_service::Service;\nuse futures_core::future::LocalBoxFuture;\nuse futures_util::FutureExt as _;\nuse http::uri::Authority;\nuse pin_project_lite::pin_project;\nuse tokio::sync::{OwnedSemaphorePermit, Semaphore};\n\nuse super::{\n    config::ConnectorConfig,\n    connection::{ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner},\n    error::ConnectError,\n    h2proto::handshake,\n    Connect,\n};\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub struct Key {\n    authority: Authority,\n}\n\nimpl From<Authority> for Key {\n    fn from(authority: Authority) -> Key {\n        Key { authority }\n    }\n}\n\n/// Connections pool to reuse I/O per [`Authority`].\n#[doc(hidden)]\npub struct ConnectionPool<S, Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    connector: S,\n    inner: ConnectionPoolInner<Io>,\n}\n\n/// Wrapper type for check the ref count of Rc.\npub struct ConnectionPoolInner<Io>(Rc<ConnectionPoolInnerPriv<Io>>)\nwhere\n    Io: AsyncWrite + Unpin + 'static;\n\nimpl<Io> ConnectionPoolInner<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    fn new(config: ConnectorConfig) -> Self {\n        let permits = Arc::new(Semaphore::new(config.limit));\n        let available = RefCell::new(HashMap::new());\n\n        Self(Rc::new(ConnectionPoolInnerPriv {\n            config,\n            available,\n            permits,\n        }))\n    }\n\n    /// Spawns a graceful shutdown task for the underlying I/O with a timeout.\n    fn close(&self, conn: ConnectionInnerType<Io>) {\n        if let Some(timeout) = self.config.disconnect_timeout {\n            if let ConnectionInnerType::H1(io) = conn {\n                if tokio::runtime::Handle::try_current().is_ok() {\n                    actix_rt::spawn(CloseConnection::new(io, timeout));\n                }\n            }\n        }\n    }\n}\n\nimpl<Io> Clone for ConnectionPoolInner<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    fn clone(&self) -> Self {\n        Self(Rc::clone(&self.0))\n    }\n}\n\nimpl<Io> Deref for ConnectionPoolInner<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    type Target = ConnectionPoolInnerPriv<Io>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<Io> Drop for ConnectionPoolInner<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    fn drop(&mut self) {\n        // When strong count is one it means the pool is dropped\n        // remove and drop all Io types.\n        if Rc::strong_count(&self.0) == 1 {\n            self.permits.close();\n            std::mem::take(&mut *self.available.borrow_mut())\n                .into_iter()\n                .for_each(|(_, conns)| {\n                    conns.into_iter().for_each(|pooled| self.close(pooled.conn))\n                });\n        }\n    }\n}\n\npub struct ConnectionPoolInnerPriv<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    config: ConnectorConfig,\n    available: RefCell<HashMap<Key, VecDeque<PooledConnection<Io>>>>,\n    permits: Arc<Semaphore>,\n}\n\nimpl<S, Io> ConnectionPool<S, Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    /// Construct a new connection pool.\n    ///\n    /// [`super::config::ConnectorConfig`]'s `limit` is used as the max permits allowed for\n    /// in-flight connections.\n    ///\n    /// The pool can only have equal to `limit` amount of requests spawning/using Io type\n    /// concurrently.\n    ///\n    /// Any requests beyond limit would be wait in fifo order and get notified in async manner\n    /// by [`tokio::sync::Semaphore`]\n    pub(crate) fn new(connector: S, config: ConnectorConfig) -> Self {\n        let inner = ConnectionPoolInner::new(config);\n\n        Self { connector, inner }\n    }\n}\n\nimpl<S, Io> Service<Connect> for ConnectionPool<S, Io>\nwhere\n    S: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + Clone + 'static,\n    Io: ConnectionIo,\n{\n    type Response = ConnectionType<Io>;\n    type Error = ConnectError;\n    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    actix_service::forward_ready!(connector);\n\n    fn call(&self, req: Connect) -> Self::Future {\n        let connector = self.connector.clone();\n        let inner = self.inner.clone();\n\n        Box::pin(async move {\n            let key = if let Some(authority) = req.uri.authority() {\n                authority.clone().into()\n            } else {\n                return Err(ConnectError::Unresolved);\n            };\n\n            // acquire an owned permit and carry it with connection\n            let permit = Arc::clone(&inner.permits)\n                .acquire_owned()\n                .await\n                .map_err(|_| {\n                    ConnectError::Io(io::Error::other(\n                        \"Failed to acquire semaphore on client connection pool\",\n                    ))\n                })?;\n\n            let conn = {\n                let mut conn = None;\n\n                // check if there is idle connection for given key.\n                let mut map = inner.available.borrow_mut();\n\n                if let Some(conns) = map.get_mut(&key) {\n                    let now = Instant::now();\n\n                    while let Some(mut c) = conns.pop_front() {\n                        let config = &inner.config;\n                        let idle_dur = now - c.used;\n                        let age = now - c.created;\n                        let conn_ineligible =\n                            idle_dur > config.conn_keep_alive || age > config.conn_lifetime;\n\n                        if conn_ineligible {\n                            // drop connections that are too old\n                            inner.close(c.conn);\n                        } else {\n                            // check if the connection is still usable\n                            if let ConnectionInnerType::H1(ref mut io) = c.conn {\n                                let check = ConnectionCheckFuture { io };\n                                match check.now_or_never().expect(\n                                    \"ConnectionCheckFuture must never yield with Poll::Pending.\",\n                                ) {\n                                    ConnectionState::Tainted => {\n                                        inner.close(c.conn);\n                                        continue;\n                                    }\n                                    ConnectionState::Skip => continue,\n                                    ConnectionState::Live => conn = Some(c),\n                                }\n                            } else {\n                                conn = Some(c);\n                            }\n\n                            break;\n                        }\n                    }\n                };\n\n                conn\n            };\n\n            // construct acquired. It's used to put Io type back to pool/ close the Io type.\n            // permit is carried with the whole lifecycle of Acquired.\n            let acquired = Acquired { key, inner, permit };\n\n            // match the connection and spawn new one if did not get anything.\n            match conn {\n                Some(conn) => Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired)),\n                None => {\n                    let (io, proto) = connector.call(req).await?;\n\n                    // NOTE: remove when http3 is added in support.\n                    assert!(proto != Protocol::Http3);\n\n                    if proto == Protocol::Http1 {\n                        Ok(ConnectionType::from_h1(io, Instant::now(), acquired))\n                    } else {\n                        let config = &acquired.inner.config;\n                        let (sender, connection) = handshake(io, config).await?;\n                        let inner = H2ConnectionInner::new(sender, connection);\n                        Ok(ConnectionType::from_h2(inner, Instant::now(), acquired))\n                    }\n                }\n            }\n        })\n    }\n}\n\n/// Type for check the connection and determine if it's usable.\nstruct ConnectionCheckFuture<'a, Io> {\n    io: &'a mut Io,\n}\n\nenum ConnectionState {\n    /// IO is pending and a new request would wake it.\n    Live,\n\n    /// IO unexpectedly has unread data and should be dropped.\n    Tainted,\n\n    /// IO should be skipped but not dropped.\n    Skip,\n}\n\nimpl<Io> Future for ConnectionCheckFuture<'_, Io>\nwhere\n    Io: AsyncRead + Unpin,\n{\n    type Output = ConnectionState;\n\n    // this future is only used to get access to Context.\n    // It should never return Poll::Pending.\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n        let mut buf = [0; 2];\n        let mut read_buf = ReadBuf::new(&mut buf);\n\n        let state = match Pin::new(&mut this.io).poll_read(cx, &mut read_buf) {\n            Poll::Ready(Ok(())) if !read_buf.filled().is_empty() => ConnectionState::Tainted,\n\n            Poll::Pending => ConnectionState::Live,\n            _ => ConnectionState::Skip,\n        };\n\n        Poll::Ready(state)\n    }\n}\n\nstruct PooledConnection<Io> {\n    conn: ConnectionInnerType<Io>,\n    used: Instant,\n    created: Instant,\n}\n\npin_project! {\n    #[project = CloseConnectionProj]\n    struct CloseConnection<Io> {\n        io: Io,\n        #[pin]\n        timeout: Sleep,\n    }\n}\n\nimpl<Io> CloseConnection<Io>\nwhere\n    Io: AsyncWrite + Unpin,\n{\n    fn new(io: Io, timeout: Duration) -> Self {\n        CloseConnection {\n            io,\n            timeout: sleep(timeout),\n        }\n    }\n}\n\nimpl<Io> Future for CloseConnection<Io>\nwhere\n    Io: AsyncWrite + Unpin,\n{\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {\n        let this = self.project();\n\n        match this.timeout.poll(cx) {\n            Poll::Ready(_) => Poll::Ready(()),\n            Poll::Pending => Pin::new(this.io).poll_shutdown(cx).map(|_| ()),\n        }\n    }\n}\n\npub struct Acquired<Io>\nwhere\n    Io: AsyncWrite + Unpin + 'static,\n{\n    /// authority key for identify connection.\n    key: Key,\n    /// handle to connection pool.\n    inner: ConnectionPoolInner<Io>,\n    /// permit for limit concurrent in-flight connection for a Client object.\n    permit: OwnedSemaphorePermit,\n}\n\nimpl<Io: ConnectionIo> Acquired<Io> {\n    /// Close the IO.\n    pub(super) fn close(&self, conn: ConnectionInnerType<Io>) {\n        self.inner.close(conn);\n    }\n\n    /// Release IO back into pool.\n    pub(super) fn release(&self, conn: ConnectionInnerType<Io>, created: Instant) {\n        let Acquired { key, inner, .. } = self;\n\n        inner\n            .available\n            .borrow_mut()\n            .entry(key.clone())\n            .or_insert_with(VecDeque::new)\n            .push_back(PooledConnection {\n                conn,\n                created,\n                used: Instant::now(),\n            });\n\n        let _ = &self.permit;\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use std::cell::Cell;\n\n    use http::Uri;\n\n    use super::*;\n\n    /// A stream type that always returns pending on async read.\n    ///\n    /// Mocks an idle TCP stream that is ready to be used for client connections.\n    struct TestStream(Rc<Cell<usize>>);\n\n    impl Drop for TestStream {\n        fn drop(&mut self) {\n            self.0.set(self.0.get() - 1);\n        }\n    }\n\n    impl AsyncRead for TestStream {\n        fn poll_read(\n            self: Pin<&mut Self>,\n            _: &mut Context<'_>,\n            _: &mut ReadBuf<'_>,\n        ) -> Poll<io::Result<()>> {\n            Poll::Pending\n        }\n    }\n\n    impl AsyncWrite for TestStream {\n        fn poll_write(\n            self: Pin<&mut Self>,\n            _: &mut Context<'_>,\n            _: &[u8],\n        ) -> Poll<io::Result<usize>> {\n            unimplemented!()\n        }\n\n        fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n            unimplemented!()\n        }\n\n        fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n            Poll::Ready(Ok(()))\n        }\n    }\n\n    #[derive(Clone)]\n    struct TestPoolConnector {\n        generated: Rc<Cell<usize>>,\n    }\n\n    impl Service<Connect> for TestPoolConnector {\n        type Response = (TestStream, Protocol);\n        type Error = ConnectError;\n        type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n        actix_service::always_ready!();\n\n        fn call(&self, _: Connect) -> Self::Future {\n            self.generated.set(self.generated.get() + 1);\n            let generated = self.generated.clone();\n            Box::pin(async { Ok((TestStream(generated), Protocol::Http1)) })\n        }\n    }\n\n    fn release<T>(conn: ConnectionType<T>)\n    where\n        T: AsyncRead + AsyncWrite + Unpin + 'static,\n    {\n        match conn {\n            ConnectionType::H1(mut conn) => conn.on_release(true),\n            ConnectionType::H2(mut conn) => conn.on_release(false),\n        }\n    }\n\n    #[actix_rt::test]\n    async fn test_pool_limit() {\n        let connector = TestPoolConnector {\n            generated: Rc::new(Cell::new(0)),\n        };\n\n        let config = ConnectorConfig {\n            limit: 1,\n            ..Default::default()\n        };\n\n        let pool = super::ConnectionPool::new(connector, config);\n\n        let req = Connect {\n            uri: Uri::from_static(\"http://localhost\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n\n        let waiting = Rc::new(Cell::new(true));\n\n        let waiting_clone = waiting.clone();\n        actix_rt::spawn(async move {\n            actix_rt::time::sleep(Duration::from_millis(100)).await;\n            waiting_clone.set(false);\n            drop(conn);\n        });\n\n        assert!(waiting.get());\n\n        let now = Instant::now();\n        let conn = pool.call(req).await.unwrap();\n\n        release(conn);\n        assert!(!waiting.get());\n        assert!(now.elapsed() >= Duration::from_millis(100));\n    }\n\n    #[actix_rt::test]\n    async fn test_pool_keep_alive() {\n        let generated = Rc::new(Cell::new(0));\n        let generated_clone = generated.clone();\n\n        let connector = TestPoolConnector { generated };\n\n        let config = ConnectorConfig {\n            conn_keep_alive: Duration::from_secs(1),\n            ..Default::default()\n        };\n\n        let pool = super::ConnectionPool::new(connector, config);\n\n        let req = Connect {\n            uri: Uri::from_static(\"http://localhost\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        actix_rt::time::sleep(Duration::from_millis(1500)).await;\n        actix_rt::task::yield_now().await;\n\n        let conn = pool.call(req).await.unwrap();\n        // Note: spawned recycle connection is not ran yet.\n        // This is tokio current thread runtime specific behavior.\n        assert_eq!(2, generated_clone.get());\n\n        // yield task so the old connection is properly dropped.\n        actix_rt::task::yield_now().await;\n        assert_eq!(1, generated_clone.get());\n\n        release(conn);\n    }\n\n    #[actix_rt::test]\n    async fn test_pool_lifetime() {\n        let generated = Rc::new(Cell::new(0));\n        let generated_clone = generated.clone();\n\n        let connector = TestPoolConnector { generated };\n\n        let config = ConnectorConfig {\n            conn_lifetime: Duration::from_secs(1),\n            ..Default::default()\n        };\n\n        let pool = super::ConnectionPool::new(connector, config);\n\n        let req = Connect {\n            uri: Uri::from_static(\"http://localhost\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        actix_rt::time::sleep(Duration::from_millis(1500)).await;\n        actix_rt::task::yield_now().await;\n\n        let conn = pool.call(req).await.unwrap();\n        // Note: spawned recycle connection is not ran yet.\n        // This is tokio current thread runtime specific behavior.\n        assert_eq!(2, generated_clone.get());\n\n        // yield task so the old connection is properly dropped.\n        actix_rt::task::yield_now().await;\n        assert_eq!(1, generated_clone.get());\n\n        release(conn);\n    }\n\n    #[actix_rt::test]\n    async fn test_pool_authority_key() {\n        let generated = Rc::new(Cell::new(0));\n        let generated_clone = generated.clone();\n\n        let connector = TestPoolConnector { generated };\n\n        let config = ConnectorConfig::default();\n\n        let pool = super::ConnectionPool::new(connector, config);\n\n        let req = Connect {\n            uri: Uri::from_static(\"https://crates.io\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        let conn = pool.call(req).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        let req = Connect {\n            uri: Uri::from_static(\"https://google.com\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(2, generated_clone.get());\n        release(conn);\n        let conn = pool.call(req).await.unwrap();\n        assert_eq!(2, generated_clone.get());\n        release(conn);\n    }\n\n    #[actix_rt::test]\n    async fn test_pool_drop() {\n        let generated = Rc::new(Cell::new(0));\n        let generated_clone = generated.clone();\n\n        let connector = TestPoolConnector { generated };\n\n        let config = ConnectorConfig::default();\n\n        let pool = Rc::new(super::ConnectionPool::new(connector, config));\n\n        let req = Connect {\n            uri: Uri::from_static(\"https://crates.io\"),\n            addr: None,\n        };\n\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(1, generated_clone.get());\n        release(conn);\n\n        let req = Connect {\n            uri: Uri::from_static(\"https://google.com\"),\n            addr: None,\n        };\n        let conn = pool.call(req.clone()).await.unwrap();\n        assert_eq!(2, generated_clone.get());\n        release(conn);\n\n        let clone1 = pool.clone();\n        let clone2 = clone1.clone();\n\n        drop(clone2);\n        for _ in 0..2 {\n            actix_rt::task::yield_now().await;\n        }\n        assert_eq!(2, generated_clone.get());\n\n        drop(clone1);\n        for _ in 0..2 {\n            actix_rt::task::yield_now().await;\n        }\n        assert_eq!(2, generated_clone.get());\n\n        drop(pool);\n        for _ in 0..2 {\n            actix_rt::task::yield_now().await;\n        }\n        assert_eq!(0, generated_clone.get());\n    }\n}\n"
  },
  {
    "path": "awc/src/connect.rs",
    "content": "use std::{\n    future::Future,\n    net,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_codec::Framed;\nuse actix_http::{h1::ClientCodec, Payload, RequestHead, RequestHeadType, ResponseHead};\nuse actix_service::Service;\nuse futures_core::{future::LocalBoxFuture, ready};\n\nuse crate::{\n    any_body::AnyBody,\n    client::{Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError},\n    ClientResponse,\n};\n\npub type BoxConnectorService = Rc<\n    dyn Service<\n        ConnectRequest,\n        Response = ConnectResponse,\n        Error = SendRequestError,\n        Future = LocalBoxFuture<'static, Result<ConnectResponse, SendRequestError>>,\n    >,\n>;\n\npub type BoxedSocket = Box<dyn ConnectionIo>;\n\n/// Combined HTTP and WebSocket request type received by connection service.\npub enum ConnectRequest {\n    /// Standard HTTP request.\n    ///\n    /// Contains the request head, body type, and optional pre-resolved socket address.\n    Client(RequestHeadType, AnyBody, Option<net::SocketAddr>),\n\n    /// Tunnel used by WebSocket connection requests.\n    ///\n    /// Contains the request head and optional pre-resolved socket address.\n    Tunnel(RequestHead, Option<net::SocketAddr>),\n}\n\n/// Combined HTTP response & WebSocket tunnel type returned from connection service.\npub enum ConnectResponse {\n    /// Standard HTTP response.\n    Client(ClientResponse),\n\n    /// Tunnel used for WebSocket communication.\n    ///\n    /// Contains response head and framed HTTP/1.1 codec.\n    Tunnel(ResponseHead, Framed<BoxedSocket, ClientCodec>),\n}\n\nimpl ConnectResponse {\n    /// Unwraps type into HTTP response.\n    ///\n    /// # Panics\n    /// Panics if enum variant is not `Client`.\n    pub fn into_client_response(self) -> ClientResponse {\n        match self {\n            ConnectResponse::Client(res) => res,\n            _ => {\n                panic!(\"ClientResponse only reachable with ConnectResponse::ClientResponse variant\")\n            }\n        }\n    }\n\n    /// Unwraps type into WebSocket tunnel response.\n    ///\n    /// # Panics\n    /// Panics if enum variant is not `Tunnel`.\n    pub fn into_tunnel_response(self) -> (ResponseHead, Framed<BoxedSocket, ClientCodec>) {\n        match self {\n            ConnectResponse::Tunnel(head, framed) => (head, framed),\n            _ => {\n                panic!(\"TunnelResponse only reachable with ConnectResponse::TunnelResponse variant\")\n            }\n        }\n    }\n}\n\npub struct DefaultConnector<S> {\n    connector: S,\n}\n\nimpl<S> DefaultConnector<S> {\n    pub(crate) fn new(connector: S) -> Self {\n        Self { connector }\n    }\n}\n\nimpl<S, Io> Service<ConnectRequest> for DefaultConnector<S>\nwhere\n    S: Service<ClientConnect, Error = ConnectError, Response = Connection<Io>>,\n    Io: ConnectionIo,\n{\n    type Response = ConnectResponse;\n    type Error = SendRequestError;\n    type Future = ConnectRequestFuture<S::Future, Io>;\n\n    actix_service::forward_ready!(connector);\n\n    fn call(&self, req: ConnectRequest) -> Self::Future {\n        // connect to the host\n        let fut = match req {\n            ConnectRequest::Client(ref head, .., addr) => self.connector.call(ClientConnect {\n                uri: head.as_ref().uri.clone(),\n                addr,\n            }),\n            ConnectRequest::Tunnel(ref head, addr) => self.connector.call(ClientConnect {\n                uri: head.uri.clone(),\n                addr,\n            }),\n        };\n\n        ConnectRequestFuture::Connection {\n            fut,\n            req: Some(req),\n        }\n    }\n}\n\npin_project_lite::pin_project! {\n    #[project = ConnectRequestProj]\n    pub enum ConnectRequestFuture<Fut, Io>\n    where\n        Io: ConnectionIo\n    {\n        Connection {\n            #[pin]\n            fut: Fut,\n            req: Option<ConnectRequest>\n        },\n        Client {\n            fut: LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>>\n        },\n        Tunnel {\n            fut: LocalBoxFuture<\n                'static,\n                Result<(ResponseHead, Framed<Connection<Io>, ClientCodec>), SendRequestError>,\n            >,\n        }\n    }\n}\n\nimpl<Fut, Io> Future for ConnectRequestFuture<Fut, Io>\nwhere\n    Fut: Future<Output = Result<Connection<Io>, ConnectError>>,\n    Io: ConnectionIo,\n{\n    type Output = Result<ConnectResponse, SendRequestError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.as_mut().project() {\n            ConnectRequestProj::Connection { fut, req } => {\n                let connection = ready!(fut.poll(cx))?;\n                let req = req.take().unwrap();\n\n                match req {\n                    ConnectRequest::Client(head, body, ..) => {\n                        // send request\n                        let fut = ConnectRequestFuture::Client {\n                            fut: connection.send_request(head, body),\n                        };\n\n                        self.set(fut);\n                    }\n\n                    ConnectRequest::Tunnel(head, ..) => {\n                        // send request\n                        let fut = ConnectRequestFuture::Tunnel {\n                            fut: connection.open_tunnel(RequestHeadType::from(head)),\n                        };\n\n                        self.set(fut);\n                    }\n                }\n\n                self.poll(cx)\n            }\n\n            ConnectRequestProj::Client { fut } => {\n                let (head, payload) = ready!(fut.as_mut().poll(cx))?;\n                Poll::Ready(Ok(ConnectResponse::Client(ClientResponse::new(\n                    head, payload,\n                ))))\n            }\n\n            ConnectRequestProj::Tunnel { fut } => {\n                let (head, framed) = ready!(fut.as_mut().poll(cx))?;\n                let framed = framed.into_map_io(|io| Box::new(io) as _);\n                Poll::Ready(Ok(ConnectResponse::Tunnel(head, framed)))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "awc/src/error.rs",
    "content": "//! HTTP client errors\n\n// TODO: figure out how best to expose http::Error vs actix_http::Error\npub use actix_http::{\n    error::{HttpError, PayloadError},\n    header::HeaderValue,\n    ws::{HandshakeError as WsHandshakeError, ProtocolError as WsProtocolError},\n    StatusCode,\n};\nuse derive_more::{Display, From};\nuse serde_json::error::Error as JsonError;\n\npub use crate::client::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};\n\n// TODO: address display, error, and from impls\n\n/// Websocket client error\n#[derive(Debug, Display, From)]\npub enum WsClientError {\n    /// Invalid response status\n    #[display(\"Invalid response status\")]\n    InvalidResponseStatus(StatusCode),\n\n    /// Invalid upgrade header\n    #[display(\"Invalid upgrade header\")]\n    InvalidUpgradeHeader,\n\n    /// Invalid connection header\n    #[display(\"Invalid connection header\")]\n    InvalidConnectionHeader(HeaderValue),\n\n    /// Missing Connection header\n    #[display(\"Missing Connection header\")]\n    MissingConnectionHeader,\n\n    /// Missing Sec-Websocket-Accept header\n    #[display(\"Missing Sec-Websocket-Accept header\")]\n    MissingWebSocketAcceptHeader,\n\n    /// Invalid challenge response\n    #[display(\"Invalid challenge response\")]\n    InvalidChallengeResponse([u8; 28], HeaderValue),\n\n    /// Protocol error\n    #[display(\"{}\", _0)]\n    Protocol(WsProtocolError),\n\n    /// Send request error\n    #[display(\"{}\", _0)]\n    SendRequest(SendRequestError),\n}\n\nimpl std::error::Error for WsClientError {}\n\nimpl From<InvalidUrl> for WsClientError {\n    fn from(err: InvalidUrl) -> Self {\n        WsClientError::SendRequest(err.into())\n    }\n}\n\nimpl From<HttpError> for WsClientError {\n    fn from(err: HttpError) -> Self {\n        WsClientError::SendRequest(err.into())\n    }\n}\n\n/// A set of errors that can occur during parsing json payloads\n#[derive(Debug, Display, From)]\npub enum JsonPayloadError {\n    /// Content type error\n    #[display(\"Content type error\")]\n    ContentType,\n    /// Deserialize error\n    #[display(\"Json deserialize error: {}\", _0)]\n    Deserialize(JsonError),\n    /// Payload error\n    #[display(\"Error that occur during reading payload: {}\", _0)]\n    Payload(PayloadError),\n}\n\nimpl std::error::Error for JsonPayloadError {}\n"
  },
  {
    "path": "awc/src/frozen.rs",
    "content": "use std::{net, rc::Rc, time::Duration};\n\nuse actix_http::{\n    body::MessageBody,\n    error::HttpError,\n    header::{HeaderMap, TryIntoHeaderPair},\n    Method, RequestHead, Uri,\n};\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse serde::Serialize;\n\nuse crate::{\n    client::ClientConfig,\n    sender::{RequestSender, SendClientRequest},\n    BoxError,\n};\n\n/// `FrozenClientRequest` struct represents cloneable client request.\n///\n/// It could be used to send same request multiple times.\n#[derive(Clone)]\npub struct FrozenClientRequest {\n    pub(crate) head: Rc<RequestHead>,\n    pub(crate) addr: Option<net::SocketAddr>,\n    pub(crate) response_decompress: bool,\n    pub(crate) timeout: Option<Duration>,\n    pub(crate) config: ClientConfig,\n}\n\nimpl FrozenClientRequest {\n    /// Get HTTP URI of request\n    pub fn get_uri(&self) -> &Uri {\n        &self.head.uri\n    }\n\n    /// Get HTTP method of this request\n    pub fn get_method(&self) -> &Method {\n        &self.head.method\n    }\n\n    /// Returns request's headers.\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head.headers\n    }\n\n    /// Send a body.\n    pub fn send_body<B>(&self, body: B) -> SendClientRequest\n    where\n        B: MessageBody + 'static,\n    {\n        RequestSender::Rc(Rc::clone(&self.head), None).send_body(\n            self.addr,\n            self.response_decompress,\n            self.timeout,\n            &self.config,\n            body,\n        )\n    }\n\n    /// Send a json body.\n    pub fn send_json<T: Serialize>(&self, value: &T) -> SendClientRequest {\n        RequestSender::Rc(Rc::clone(&self.head), None).send_json(\n            self.addr,\n            self.response_decompress,\n            self.timeout,\n            &self.config,\n            value,\n        )\n    }\n\n    /// Send an urlencoded body.\n    pub fn send_form<T: Serialize>(&self, value: &T) -> SendClientRequest {\n        RequestSender::Rc(Rc::clone(&self.head), None).send_form(\n            self.addr,\n            self.response_decompress,\n            self.timeout,\n            &self.config,\n            value,\n        )\n    }\n\n    /// Send a streaming body.\n    pub fn send_stream<S, E>(&self, stream: S) -> SendClientRequest\n    where\n        S: Stream<Item = Result<Bytes, E>> + 'static,\n        E: Into<BoxError> + 'static,\n    {\n        RequestSender::Rc(Rc::clone(&self.head), None).send_stream(\n            self.addr,\n            self.response_decompress,\n            self.timeout,\n            &self.config,\n            stream,\n        )\n    }\n\n    /// Send an empty body.\n    pub fn send(&self) -> SendClientRequest {\n        RequestSender::Rc(Rc::clone(&self.head), None).send(\n            self.addr,\n            self.response_decompress,\n            self.timeout,\n            &self.config,\n        )\n    }\n\n    /// Clones this `FrozenClientRequest`, returning a new one with extra headers added.\n    pub fn extra_headers(&self, extra_headers: HeaderMap) -> FrozenSendBuilder {\n        FrozenSendBuilder::new(self.clone(), extra_headers)\n    }\n\n    /// Clones this `FrozenClientRequest`, returning a new one with the extra header added.\n    pub fn extra_header(&self, header: impl TryIntoHeaderPair) -> FrozenSendBuilder {\n        self.extra_headers(HeaderMap::new()).extra_header(header)\n    }\n}\n\n/// Builder that allows to modify extra headers.\npub struct FrozenSendBuilder {\n    req: FrozenClientRequest,\n    extra_headers: HeaderMap,\n    err: Option<HttpError>,\n}\n\nimpl FrozenSendBuilder {\n    pub(crate) fn new(req: FrozenClientRequest, extra_headers: HeaderMap) -> Self {\n        Self {\n            req,\n            extra_headers,\n            err: None,\n        }\n    }\n\n    /// Insert a header, it overrides existing header in `FrozenClientRequest`.\n    pub fn extra_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => {\n                self.extra_headers.insert(key, value);\n            }\n\n            Err(err) => self.err = Some(err.into()),\n        }\n\n        self\n    }\n\n    /// Complete request construction and send a body.\n    pub fn send_body(self, body: impl MessageBody + 'static) -> SendClientRequest {\n        if let Some(err) = self.err {\n            return err.into();\n        }\n\n        RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_body(\n            self.req.addr,\n            self.req.response_decompress,\n            self.req.timeout,\n            &self.req.config,\n            body,\n        )\n    }\n\n    /// Complete request construction and send a json body.\n    pub fn send_json(self, value: impl Serialize) -> SendClientRequest {\n        if let Some(err) = self.err {\n            return err.into();\n        }\n\n        RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_json(\n            self.req.addr,\n            self.req.response_decompress,\n            self.req.timeout,\n            &self.req.config,\n            value,\n        )\n    }\n\n    /// Complete request construction and send an urlencoded body.\n    pub fn send_form(self, value: impl Serialize) -> SendClientRequest {\n        if let Some(err) = self.err {\n            return err.into();\n        }\n\n        RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_form(\n            self.req.addr,\n            self.req.response_decompress,\n            self.req.timeout,\n            &self.req.config,\n            value,\n        )\n    }\n\n    /// Complete request construction and send a streaming body.\n    pub fn send_stream<S, E>(self, stream: S) -> SendClientRequest\n    where\n        S: Stream<Item = Result<Bytes, E>> + 'static,\n        E: Into<BoxError> + 'static,\n    {\n        if let Some(err) = self.err {\n            return err.into();\n        }\n\n        RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_stream(\n            self.req.addr,\n            self.req.response_decompress,\n            self.req.timeout,\n            &self.req.config,\n            stream,\n        )\n    }\n\n    /// Complete request construction and send an empty body.\n    pub fn send(self) -> SendClientRequest {\n        if let Some(err) = self.err {\n            return err.into();\n        }\n\n        RequestSender::Rc(self.req.head, Some(self.extra_headers)).send(\n            self.req.addr,\n            self.req.response_decompress,\n            self.req.timeout,\n            &self.req.config,\n        )\n    }\n}\n"
  },
  {
    "path": "awc/src/lib.rs",
    "content": "//! `awc` is an asynchronous HTTP and WebSocket client library.\n//!\n//! # `GET` Requests\n//! ```no_run\n//! # #[actix_rt::main]\n//! # async fn main() -> Result<(), awc::error::SendRequestError> {\n//! // create client\n//! let mut client = awc::Client::default();\n//!\n//! // construct request\n//! let req = client.get(\"http://www.rust-lang.org\")\n//!     .insert_header((\"User-Agent\", \"awc/3.0\"));\n//!\n//! // send request and await response\n//! let res = req.send().await?;\n//! println!(\"Response: {:?}\", res);\n//! # Ok(())\n//! # }\n//! ```\n//!\n//! # `POST` Requests\n//! ## Raw Body\n//! ```no_run\n//! # #[actix_rt::main]\n//! # async fn main() -> Result<(), awc::error::SendRequestError> {\n//! let mut client = awc::Client::default();\n//! let response = client.post(\"http://httpbin.org/post\")\n//!     .send_body(\"Raw body contents\")\n//!     .await?;\n//! # Ok(())\n//! # }\n//! ```\n//!\n//! ## JSON\n//! ```no_run\n//! # #[actix_rt::main]\n//! # async fn main() -> Result<(), awc::error::SendRequestError> {\n//! let request = serde_json::json!({\n//!     \"lang\": \"rust\",\n//!     \"body\": \"json\"\n//! });\n//!\n//! let mut client = awc::Client::default();\n//! let response = client.post(\"http://httpbin.org/post\")\n//!     .send_json(&request)\n//!     .await?;\n//! # Ok(())\n//! # }\n//! ```\n//!\n//! ## URL Encoded Form\n//! ```no_run\n//! # #[actix_rt::main]\n//! # async fn main() -> Result<(), awc::error::SendRequestError> {\n//! let params = [(\"foo\", \"bar\"), (\"baz\", \"quux\")];\n//!\n//! let mut client = awc::Client::default();\n//! let response = client.post(\"http://httpbin.org/post\")\n//!     .send_form(&params)\n//!     .await?;\n//! # Ok(())\n//! # }\n//! ```\n//!\n//! # Response Compression\n//! All [official][iana-encodings] and common content encoding codecs are supported, optionally.\n//!\n//! The `Accept-Encoding` header will automatically be populated with enabled codecs and added to\n//! outgoing requests, allowing servers to select their `Content-Encoding` accordingly.\n//!\n//! Feature flags enable these codecs according to the table below. By default, all `compress-*`\n//! features are enabled.\n//!\n//! | Feature           | Codecs        |\n//! | ----------------- | ------------- |\n//! | `compress-brotli` | brotli        |\n//! | `compress-gzip`   | gzip, deflate |\n//! | `compress-zstd`   | zstd          |\n//!\n//! [iana-encodings]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding\n//!\n//! # WebSockets\n//! ```no_run\n//! # #[actix_rt::main]\n//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//! use futures_util::{SinkExt as _, StreamExt as _};\n//!\n//! let (_resp, mut connection) = awc::Client::new()\n//!     .ws(\"ws://echo.websocket.org\")\n//!     .connect()\n//!     .await?;\n//!\n//! connection\n//!     .send(awc::ws::Message::Text(\"Echo\".into()))\n//!     .await?;\n//!\n//! let response = connection.next().await.unwrap()?;\n//! assert_eq!(response, awc::ws::Frame::Text(\"Echo\".into()));\n//! # Ok(())\n//! # }\n//! ```\n\n#![allow(unknown_lints)] // temp: #[allow(non_local_definitions)]\n#![allow(\n    clippy::type_complexity,\n    clippy::borrow_interior_mutable_const,\n    clippy::needless_doctest_main\n)]\n#![doc(html_logo_url = \"https://actix.rs/img/logo.png\")]\n#![doc(html_favicon_url = \"https://actix.rs/favicon.ico\")]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\npub use actix_http::body;\n#[cfg(feature = \"cookies\")]\npub use cookie;\n\nmod any_body;\nmod builder;\nmod client;\nmod connect;\npub mod error;\nmod frozen;\npub mod middleware;\nmod request;\nmod responses;\nmod sender;\npub mod test;\npub mod ws;\n\npub mod http {\n    //! Various HTTP related types.\n\n    // TODO: figure out how best to expose http::Error vs actix_http::Error\n    pub use actix_http::{header, uri, ConnectionType, Error, Method, StatusCode, Uri, Version};\n}\n\n#[allow(deprecated)]\npub use self::responses::{ClientResponse, JsonBody, MessageBody, ResponseBody};\npub use self::{\n    builder::ClientBuilder,\n    client::{Client, Connect, Connector},\n    connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse},\n    frozen::{FrozenClientRequest, FrozenSendBuilder},\n    request::ClientRequest,\n    sender::SendClientRequest,\n};\n\npub(crate) type BoxError = Box<dyn std::error::Error>;\n"
  },
  {
    "path": "awc/src/middleware/mod.rs",
    "content": "mod redirect;\n\nuse std::marker::PhantomData;\n\nuse actix_service::Service;\n\npub use self::redirect::Redirect;\n\n/// Trait for transform a type to another one.\n/// Both the input and output type should impl [actix_service::Service] trait.\npub trait Transform<S, Req> {\n    type Transform: Service<Req>;\n\n    /// Creates and returns a new Transform component.\n    fn new_transform(self, service: S) -> Self::Transform;\n}\n\n#[doc(hidden)]\n/// Helper struct for constructing Nested types that would call `Transform::new_transform`\n/// in a chain.\n///\n/// The child field would be called first and the output `Service` type is\n/// passed to parent as input type.\npub struct NestTransform<T1, T2, S, Req>\nwhere\n    T1: Transform<S, Req>,\n    T2: Transform<T1::Transform, Req>,\n{\n    child: T1,\n    parent: T2,\n    _service: PhantomData<(S, Req)>,\n}\n\nimpl<T1, T2, S, Req> NestTransform<T1, T2, S, Req>\nwhere\n    T1: Transform<S, Req>,\n    T2: Transform<T1::Transform, Req>,\n{\n    pub(crate) fn new(child: T1, parent: T2) -> Self {\n        NestTransform {\n            child,\n            parent,\n            _service: PhantomData,\n        }\n    }\n}\n\nimpl<T1, T2, S, Req> Transform<S, Req> for NestTransform<T1, T2, S, Req>\nwhere\n    T1: Transform<S, Req>,\n    T2: Transform<T1::Transform, Req>,\n{\n    type Transform = T2::Transform;\n\n    fn new_transform(self, service: S) -> Self::Transform {\n        let service = self.child.new_transform(service);\n        self.parent.new_transform(service)\n    }\n}\n\n/// Dummy impl for kick start `NestTransform` type in `ClientBuilder` type\nimpl<S, Req> Transform<S, Req> for ()\nwhere\n    S: Service<Req>,\n{\n    type Transform = S;\n\n    fn new_transform(self, service: S) -> Self::Transform {\n        service\n    }\n}\n"
  },
  {
    "path": "awc/src/middleware/redirect.rs",
    "content": "use std::{\n    future::Future,\n    net::SocketAddr,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n};\n\nuse actix_http::{header, Method, RequestHead, RequestHeadType, StatusCode, Uri};\nuse actix_service::Service;\nuse bytes::Bytes;\nuse futures_core::ready;\n\nuse super::Transform;\nuse crate::{\n    any_body::AnyBody,\n    client::{InvalidUrl, SendRequestError},\n    connect::{ConnectRequest, ConnectResponse},\n    ClientResponse,\n};\n\npub struct Redirect {\n    max_redirect_times: u8,\n}\n\nimpl Default for Redirect {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Redirect {\n    pub fn new() -> Self {\n        Self {\n            max_redirect_times: 10,\n        }\n    }\n\n    pub fn max_redirect_times(mut self, times: u8) -> Self {\n        self.max_redirect_times = times;\n        self\n    }\n}\n\nimpl<S> Transform<S, ConnectRequest> for Redirect\nwhere\n    S: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError> + 'static,\n{\n    type Transform = RedirectService<S>;\n\n    fn new_transform(self, service: S) -> Self::Transform {\n        RedirectService {\n            max_redirect_times: self.max_redirect_times,\n            connector: Rc::new(service),\n        }\n    }\n}\n\npub struct RedirectService<S> {\n    max_redirect_times: u8,\n    connector: Rc<S>,\n}\n\nimpl<S> Service<ConnectRequest> for RedirectService<S>\nwhere\n    S: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError> + 'static,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    type Future = RedirectServiceFuture<S>;\n\n    actix_service::forward_ready!(connector);\n\n    fn call(&self, req: ConnectRequest) -> Self::Future {\n        match req {\n            ConnectRequest::Tunnel(head, addr) => {\n                let fut = self.connector.call(ConnectRequest::Tunnel(head, addr));\n                RedirectServiceFuture::Tunnel { fut }\n            }\n            ConnectRequest::Client(head, body, addr) => {\n                let connector = Rc::clone(&self.connector);\n                let max_redirect_times = self.max_redirect_times;\n\n                // backup the uri and method for reuse schema and authority.\n                let (uri, method, headers) = match head {\n                    RequestHeadType::Owned(ref head) => {\n                        (head.uri.clone(), head.method.clone(), head.headers.clone())\n                    }\n                    RequestHeadType::Rc(ref head, ..) => {\n                        (head.uri.clone(), head.method.clone(), head.headers.clone())\n                    }\n                };\n\n                let body_opt = match body {\n                    AnyBody::Bytes { ref body } => Some(body.clone()),\n                    _ => None,\n                };\n\n                let fut = connector.call(ConnectRequest::Client(head, body, addr));\n\n                RedirectServiceFuture::Client {\n                    fut,\n                    max_redirect_times,\n                    uri: Some(uri),\n                    method: Some(method),\n                    headers: Some(headers),\n                    body: body_opt,\n                    addr,\n                    connector: Some(connector),\n                }\n            }\n        }\n    }\n}\n\npin_project_lite::pin_project! {\n    #[project = RedirectServiceProj]\n    pub enum RedirectServiceFuture<S>\n    where\n        S: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,\n        S: 'static\n    {\n        Tunnel { #[pin] fut: S::Future },\n        Client {\n            #[pin]\n            fut: S::Future,\n            max_redirect_times: u8,\n            uri: Option<Uri>,\n            method: Option<Method>,\n            headers: Option<header::HeaderMap>,\n            body: Option<Bytes>,\n            addr: Option<SocketAddr>,\n            connector: Option<Rc<S>>,\n        }\n    }\n}\n\nimpl<S> Future for RedirectServiceFuture<S>\nwhere\n    S: Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError> + 'static,\n{\n    type Output = Result<ConnectResponse, SendRequestError>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.as_mut().project() {\n            RedirectServiceProj::Tunnel { fut } => fut.poll(cx),\n            RedirectServiceProj::Client {\n                fut,\n                max_redirect_times,\n                uri,\n                method,\n                headers,\n                body,\n                addr,\n                connector,\n            } => match ready!(fut.poll(cx))? {\n                ConnectResponse::Client(res) => match res.head().status {\n                    StatusCode::MOVED_PERMANENTLY\n                    | StatusCode::FOUND\n                    | StatusCode::SEE_OTHER\n                    | StatusCode::TEMPORARY_REDIRECT\n                    | StatusCode::PERMANENT_REDIRECT\n                        if *max_redirect_times > 0\n                            && res.headers().contains_key(header::LOCATION) =>\n                    {\n                        let reuse_body = res.head().status == StatusCode::TEMPORARY_REDIRECT\n                            || res.head().status == StatusCode::PERMANENT_REDIRECT;\n\n                        let prev_uri = uri.take().unwrap();\n\n                        // rebuild uri from the location header value.\n                        let next_uri = build_next_uri(&res, &prev_uri)?;\n\n                        // take ownership of states that could be reused\n                        let addr = addr.take();\n                        let connector = connector.take();\n\n                        // reset method\n                        let method = if reuse_body {\n                            method.take().unwrap()\n                        } else {\n                            let method = method.take().unwrap();\n                            match method {\n                                Method::GET | Method::HEAD => method,\n                                _ => Method::GET,\n                            }\n                        };\n\n                        let mut body = body.take();\n                        let body_new = if reuse_body {\n                            // try to reuse saved body\n                            match body {\n                                Some(ref bytes) => AnyBody::Bytes {\n                                    body: bytes.clone(),\n                                },\n\n                                // body was a non-reusable type so send an empty body instead\n                                _ => AnyBody::empty(),\n                            }\n                        } else {\n                            body = None;\n                            // remove body since we're downgrading to a GET\n                            AnyBody::None\n                        };\n\n                        let mut headers = headers.take().unwrap();\n\n                        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n\n                        // use a new request head.\n                        let mut head = RequestHead::default();\n                        head.uri = next_uri.clone();\n                        head.method = method.clone();\n                        head.headers = headers.clone();\n\n                        let head = RequestHeadType::Owned(head);\n\n                        let mut max_redirect_times = *max_redirect_times;\n                        max_redirect_times -= 1;\n\n                        let fut = connector\n                            .as_ref()\n                            .unwrap()\n                            .call(ConnectRequest::Client(head, body_new, addr));\n\n                        self.set(RedirectServiceFuture::Client {\n                            fut,\n                            max_redirect_times,\n                            uri: Some(next_uri),\n                            method: Some(method),\n                            headers: Some(headers),\n                            body,\n                            addr,\n                            connector,\n                        });\n\n                        self.poll(cx)\n                    }\n                    _ => Poll::Ready(Ok(ConnectResponse::Client(res))),\n                },\n                _ => unreachable!(\"ConnectRequest::Tunnel is not handled by Redirect\"),\n            },\n        }\n    }\n}\n\nfn build_next_uri(res: &ClientResponse, prev_uri: &Uri) -> Result<Uri, SendRequestError> {\n    // responses without this header are not processed\n    let location = res.headers().get(header::LOCATION).unwrap();\n\n    // try to parse the location and resolve to a full URI but fall back to default if it fails\n    let uri = Uri::try_from(location.as_bytes()).unwrap_or_else(|_| Uri::default());\n\n    let uri = if uri.scheme().is_none() || uri.authority().is_none() {\n        let builder = Uri::builder()\n            .scheme(prev_uri.scheme().cloned().unwrap())\n            .authority(prev_uri.authority().cloned().unwrap());\n\n        // scheme-relative address\n        if location.as_bytes().starts_with(b\"//\") {\n            let scheme = prev_uri.scheme_str().unwrap();\n            let mut full_url: Vec<u8> = scheme.as_bytes().to_vec();\n            full_url.push(b':');\n            full_url.extend(location.as_bytes());\n\n            return Uri::try_from(full_url)\n                .map_err(|_| SendRequestError::Url(InvalidUrl::MissingScheme));\n        }\n        // when scheme or authority is missing treat the location value as path and query\n        // recover error where location does not have leading slash\n        let path = if location.as_bytes().starts_with(b\"/\") {\n            location.as_bytes().to_owned()\n        } else {\n            [b\"/\", location.as_bytes()].concat()\n        };\n\n        builder\n            .path_and_query(path)\n            .build()\n            .map_err(|err| SendRequestError::Url(InvalidUrl::HttpError(err)))?\n    } else {\n        uri\n    };\n\n    Ok(uri)\n}\n\nfn remove_sensitive_headers(headers: &mut header::HeaderMap, prev_uri: &Uri, next_uri: &Uri) {\n    if next_uri.host() != prev_uri.host()\n        || next_uri.port() != prev_uri.port()\n        || next_uri.scheme() != prev_uri.scheme()\n    {\n        headers.remove(header::COOKIE);\n        headers.remove(header::AUTHORIZATION);\n        headers.remove(header::PROXY_AUTHORIZATION);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::str::FromStr;\n\n    use actix_web::{web, App, Error, HttpRequest, HttpResponse};\n\n    use super::*;\n    use crate::{http::header::HeaderValue, ClientBuilder};\n\n    #[actix_rt::test]\n    async fn basic_redirect() {\n        let client = ClientBuilder::new()\n            .disable_redirects()\n            .wrap(Redirect::new().max_redirect_times(10))\n            .finish();\n\n        let srv = actix_test::start(|| {\n            App::new()\n                .service(web::resource(\"/test\").route(web::to(|| async {\n                    Ok::<_, Error>(HttpResponse::BadRequest())\n                })))\n                .service(web::resource(\"/\").route(web::to(|| async {\n                    Ok::<_, Error>(\n                        HttpResponse::Found()\n                            .append_header((\"location\", \"/test\"))\n                            .finish(),\n                    )\n                })))\n        });\n\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n\n        assert_eq!(res.status().as_u16(), 400);\n    }\n\n    #[actix_rt::test]\n    async fn redirect_relative_without_leading_slash() {\n        let client = ClientBuilder::new().finish();\n\n        let srv = actix_test::start(|| {\n            App::new()\n                .service(web::resource(\"/\").route(web::to(|| async {\n                    HttpResponse::Found()\n                        .insert_header((\"location\", \"abc/\"))\n                        .finish()\n                })))\n                .service(\n                    web::resource(\"/abc/\")\n                        .route(web::to(|| async { HttpResponse::Accepted().finish() })),\n                )\n        });\n\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status(), StatusCode::ACCEPTED);\n    }\n\n    #[actix_rt::test]\n    async fn redirect_without_location() {\n        let client = ClientBuilder::new()\n            .disable_redirects()\n            .wrap(Redirect::new().max_redirect_times(10))\n            .finish();\n\n        let srv = actix_test::start(|| {\n            App::new().service(web::resource(\"/\").route(web::to(|| async {\n                Ok::<_, Error>(HttpResponse::Found().finish())\n            })))\n        });\n\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status(), StatusCode::FOUND);\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_limit() {\n        let client = ClientBuilder::new()\n            .disable_redirects()\n            .wrap(Redirect::new().max_redirect_times(1))\n            .connector(crate::Connector::new())\n            .finish();\n\n        let srv = actix_test::start(|| {\n            App::new()\n                .service(web::resource(\"/\").route(web::to(|| async {\n                    Ok::<_, Error>(\n                        HttpResponse::Found()\n                            .insert_header((\"location\", \"/test\"))\n                            .finish(),\n                    )\n                })))\n                .service(web::resource(\"/test\").route(web::to(|| async {\n                    Ok::<_, Error>(\n                        HttpResponse::Found()\n                            .insert_header((\"location\", \"/test2\"))\n                            .finish(),\n                    )\n                })))\n                .service(web::resource(\"/test2\").route(web::to(|| async {\n                    Ok::<_, Error>(HttpResponse::BadRequest())\n                })))\n        });\n\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status(), StatusCode::FOUND);\n        assert_eq!(\n            res.headers()\n                .get(header::LOCATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"/test2\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_status_kind_307_308() {\n        let srv = actix_test::start(|| {\n            async fn root() -> HttpResponse {\n                HttpResponse::TemporaryRedirect()\n                    .append_header((\"location\", \"/test\"))\n                    .finish()\n            }\n\n            async fn test(req: HttpRequest, body: Bytes) -> HttpResponse {\n                if req.method() == Method::POST && !body.is_empty() {\n                    HttpResponse::Ok().finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            App::new()\n                .service(web::resource(\"/\").route(web::to(root)))\n                .service(web::resource(\"/test\").route(web::to(test)))\n        });\n\n        let res = srv.post(\"/\").send_body(\"Hello\").await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_status_kind_301_302_303() {\n        let srv = actix_test::start(|| {\n            async fn root() -> HttpResponse {\n                HttpResponse::Found()\n                    .append_header((\"location\", \"/test\"))\n                    .finish()\n            }\n\n            async fn test(req: HttpRequest, body: Bytes) -> HttpResponse {\n                if (req.method() == Method::GET || req.method() == Method::HEAD) && body.is_empty()\n                {\n                    HttpResponse::Ok().finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            App::new()\n                .service(web::resource(\"/\").route(web::to(root)))\n                .service(web::resource(\"/test\").route(web::to(test)))\n        });\n\n        let res = srv.post(\"/\").send_body(\"Hello\").await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n\n        let res = srv.post(\"/\").send().await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_headers() {\n        let srv = actix_test::start(|| {\n            async fn root(req: HttpRequest) -> HttpResponse {\n                if req\n                    .headers()\n                    .get(\"custom\")\n                    .unwrap_or(&HeaderValue::from_str(\"\").unwrap())\n                    == \"value\"\n                {\n                    HttpResponse::Found()\n                        .append_header((\"location\", \"/test\"))\n                        .finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            async fn test(req: HttpRequest) -> HttpResponse {\n                if req\n                    .headers()\n                    .get(\"custom\")\n                    .unwrap_or(&HeaderValue::from_str(\"\").unwrap())\n                    == \"value\"\n                {\n                    HttpResponse::Ok().finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            App::new()\n                .service(web::resource(\"/\").route(web::to(root)))\n                .service(web::resource(\"/test\").route(web::to(test)))\n        });\n\n        let client = ClientBuilder::new()\n            .add_default_header((\"custom\", \"value\"))\n            .disable_redirects()\n            .finish();\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status().as_u16(), 302);\n\n        let client = ClientBuilder::new()\n            .add_default_header((\"custom\", \"value\"))\n            .finish();\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n\n        let client = ClientBuilder::new().finish();\n        let res = client\n            .get(srv.url(\"/\"))\n            .insert_header((\"custom\", \"value\"))\n            .send()\n            .await\n            .unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n    }\n\n    #[actix_rt::test]\n    async fn test_redirect_cross_origin_headers() {\n        // defining two services to have two different origins\n        let srv2 = actix_test::start(|| {\n            async fn root(req: HttpRequest) -> HttpResponse {\n                if req.headers().get(header::AUTHORIZATION).is_none() {\n                    HttpResponse::Ok().finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            App::new().service(web::resource(\"/\").route(web::to(root)))\n        });\n        let srv2_port: u16 = srv2.addr().port();\n\n        let srv1 = actix_test::start(move || {\n            async fn root(req: HttpRequest) -> HttpResponse {\n                let port = *req.app_data::<u16>().unwrap();\n                if req.headers().get(header::AUTHORIZATION).is_some() {\n                    HttpResponse::Found()\n                        .append_header((\"location\", format!(\"http://localhost:{}/\", port).as_str()))\n                        .finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            async fn test1(req: HttpRequest) -> HttpResponse {\n                if req.headers().get(header::AUTHORIZATION).is_some() {\n                    HttpResponse::Found()\n                        .append_header((\"location\", \"/test2\"))\n                        .finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            async fn test2(req: HttpRequest) -> HttpResponse {\n                if req.headers().get(header::AUTHORIZATION).is_some() {\n                    HttpResponse::Ok().finish()\n                } else {\n                    HttpResponse::InternalServerError().finish()\n                }\n            }\n\n            App::new()\n                .app_data(srv2_port)\n                .service(web::resource(\"/\").route(web::to(root)))\n                .service(web::resource(\"/test1\").route(web::to(test1)))\n                .service(web::resource(\"/test2\").route(web::to(test2)))\n        });\n\n        // send a request to different origins, http://srv1/ then http://srv2/. So it should remove the header\n        let client = ClientBuilder::new()\n            .add_default_header((header::AUTHORIZATION, \"auth_key_value\"))\n            .finish();\n        let res = client.get(srv1.url(\"/\")).send().await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n\n        // send a request to same origin, http://srv1/test1 then http://srv1/test2. So it should NOT remove any header\n        let res = client.get(srv1.url(\"/test1\")).send().await.unwrap();\n        assert_eq!(res.status().as_u16(), 200);\n    }\n\n    #[actix_rt::test]\n    async fn test_double_slash_redirect() {\n        let client = ClientBuilder::new()\n            .disable_redirects()\n            .wrap(Redirect::new().max_redirect_times(10))\n            .finish();\n\n        let srv = actix_test::start(|| {\n            App::new()\n                .service(web::resource(\"/test\").route(web::to(|| async {\n                    Ok::<_, Error>(HttpResponse::BadRequest())\n                })))\n                .service(\n                    web::resource(\"/\").route(web::to(|req: HttpRequest| async move {\n                        Ok::<_, Error>(\n                            HttpResponse::Found()\n                                .append_header((\n                                    \"location\",\n                                    format!(\n                                        \"//localhost:{}/test\",\n                                        req.app_config().local_addr().port()\n                                    )\n                                    .as_str(),\n                                ))\n                                .finish(),\n                        )\n                    })),\n                )\n        });\n\n        let res = client.get(srv.url(\"/\")).send().await.unwrap();\n\n        assert_eq!(res.status().as_u16(), 400);\n    }\n\n    #[actix_rt::test]\n    async fn test_remove_sensitive_headers() {\n        fn gen_headers() -> header::HeaderMap {\n            let mut headers = header::HeaderMap::new();\n            headers.insert(header::USER_AGENT, HeaderValue::from_str(\"value\").unwrap());\n            headers.insert(\n                header::AUTHORIZATION,\n                HeaderValue::from_str(\"value\").unwrap(),\n            );\n            headers.insert(\n                header::PROXY_AUTHORIZATION,\n                HeaderValue::from_str(\"value\").unwrap(),\n            );\n            headers.insert(header::COOKIE, HeaderValue::from_str(\"value\").unwrap());\n            headers\n        }\n\n        // Same origin\n        let prev_uri = Uri::from_str(\"https://host/path1\").unwrap();\n        let next_uri = Uri::from_str(\"https://host/path2\").unwrap();\n        let mut headers = gen_headers();\n        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n        assert_eq!(headers.len(), 4);\n\n        // different schema\n        let prev_uri = Uri::from_str(\"http://host/\").unwrap();\n        let next_uri = Uri::from_str(\"https://host/\").unwrap();\n        let mut headers = gen_headers();\n        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n        assert_eq!(headers.len(), 1);\n\n        // different host\n        let prev_uri = Uri::from_str(\"https://host1/\").unwrap();\n        let next_uri = Uri::from_str(\"https://host2/\").unwrap();\n        let mut headers = gen_headers();\n        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n        assert_eq!(headers.len(), 1);\n\n        // different port\n        let prev_uri = Uri::from_str(\"https://host:12/\").unwrap();\n        let next_uri = Uri::from_str(\"https://host:23/\").unwrap();\n        let mut headers = gen_headers();\n        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n        assert_eq!(headers.len(), 1);\n\n        // different everything!\n        let prev_uri = Uri::from_str(\"http://host1:12/path1\").unwrap();\n        let next_uri = Uri::from_str(\"https://host2:23/path2\").unwrap();\n        let mut headers = gen_headers();\n        remove_sensitive_headers(&mut headers, &prev_uri, &next_uri);\n        assert_eq!(headers.len(), 1);\n    }\n}\n"
  },
  {
    "path": "awc/src/request.rs",
    "content": "use std::{fmt, net, rc::Rc, time::Duration};\n\nuse actix_http::{\n    body::MessageBody,\n    error::HttpError,\n    header::{self, HeaderMap, HeaderValue, TryIntoHeaderPair},\n    ConnectionType, Method, RequestHead, Uri, Version,\n};\nuse base64::prelude::*;\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse serde::Serialize;\n\n#[cfg(feature = \"cookies\")]\nuse crate::cookie::{Cookie, CookieJar};\nuse crate::{\n    client::ClientConfig,\n    error::{FreezeRequestError, InvalidUrl},\n    frozen::FrozenClientRequest,\n    sender::{PrepForSendingError, RequestSender, SendClientRequest},\n    BoxError,\n};\n\n/// An HTTP Client request builder\n///\n/// This type can be used to construct an instance of `ClientRequest` through a\n/// builder-like pattern.\n///\n/// ```no_run\n/// # #[actix_rt::main]\n/// # async fn main() {\n/// let response = awc::Client::new()\n///      .get(\"http://www.rust-lang.org\") // <- Create request builder\n///      .insert_header((\"User-Agent\", \"Actix-web\"))\n///      .send()                          // <- Send HTTP request\n///      .await;\n///\n/// response.and_then(|response| {   // <- server HTTP response\n///      println!(\"Response: {:?}\", response);\n///      Ok(())\n/// });\n/// # }\n/// ```\npub struct ClientRequest {\n    pub(crate) head: RequestHead,\n    err: Option<HttpError>,\n    addr: Option<net::SocketAddr>,\n    response_decompress: bool,\n    timeout: Option<Duration>,\n    config: ClientConfig,\n\n    #[cfg(feature = \"cookies\")]\n    cookies: Option<CookieJar>,\n}\n\nimpl ClientRequest {\n    /// Create new client request builder.\n    pub(crate) fn new<U>(method: Method, uri: U, config: ClientConfig) -> Self\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        ClientRequest {\n            config,\n            head: RequestHead::default(),\n            err: None,\n            addr: None,\n            #[cfg(feature = \"cookies\")]\n            cookies: None,\n            timeout: None,\n            response_decompress: true,\n        }\n        .method(method)\n        .uri(uri)\n    }\n\n    /// Set HTTP URI of request.\n    #[inline]\n    pub fn uri<U>(mut self, uri: U) -> Self\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        match Uri::try_from(uri) {\n            Ok(uri) => self.head.uri = uri,\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Get HTTP URI of request.\n    pub fn get_uri(&self) -> &Uri {\n        &self.head.uri\n    }\n\n    /// Set socket address of the server.\n    ///\n    /// This address is used for connection. If address is not\n    /// provided url's host name get resolved.\n    pub fn address(mut self, addr: net::SocketAddr) -> Self {\n        self.addr = Some(addr);\n        self\n    }\n\n    /// Set HTTP method of this request.\n    #[inline]\n    pub fn method(mut self, method: Method) -> Self {\n        self.head.method = method;\n        self\n    }\n\n    /// Get HTTP method of this request\n    pub fn get_method(&self) -> &Method {\n        &self.head.method\n    }\n\n    /// Set HTTP version of this request.\n    ///\n    /// By default requests's HTTP version depends on network stream\n    #[doc(hidden)]\n    #[inline]\n    pub fn version(mut self, version: Version) -> Self {\n        self.head.version = version;\n        self\n    }\n\n    /// Get HTTP version of this request.\n    pub fn get_version(&self) -> &Version {\n        &self.head.version\n    }\n\n    /// Get peer address of this request.\n    pub fn get_peer_addr(&self) -> &Option<net::SocketAddr> {\n        &self.head.peer_addr\n    }\n\n    /// Returns request's headers.\n    #[inline]\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head.headers\n    }\n\n    /// Returns request's mutable headers.\n    #[inline]\n    pub fn headers_mut(&mut self) -> &mut HeaderMap {\n        &mut self.head.headers\n    }\n\n    /// Insert a header, replacing any that were set with an equivalent field name.\n    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => {\n                self.head.headers.insert(key, value);\n            }\n            Err(err) => self.err = Some(err.into()),\n        };\n\n        self\n    }\n\n    /// Insert a header only if it is not yet set.\n    pub fn insert_header_if_none(mut self, header: impl TryIntoHeaderPair) -> Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => {\n                if !self.head.headers.contains_key(&key) {\n                    self.head.headers.insert(key, value);\n                }\n            }\n            Err(err) => self.err = Some(err.into()),\n        };\n\n        self\n    }\n\n    /// Append a header, keeping any that were set with an equivalent field name.\n    ///\n    /// ```no_run\n    /// use awc::{http::header, Client};\n    ///\n    /// Client::new()\n    ///     .get(\"http://www.rust-lang.org\")\n    ///     .insert_header((\"X-TEST\", \"value\"))\n    ///     .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));\n    /// ```\n    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        match header.try_into_pair() {\n            Ok((key, value)) => self.head.headers.append(key, value),\n            Err(err) => self.err = Some(err.into()),\n        };\n\n        self\n    }\n\n    /// Send headers in `Camel-Case` form.\n    #[inline]\n    pub fn camel_case(mut self) -> Self {\n        self.head.set_camel_case_headers(true);\n        self\n    }\n\n    /// Force close connection instead of returning it back to connections pool.\n    /// This setting affect only HTTP/1 connections.\n    #[inline]\n    pub fn force_close(mut self) -> Self {\n        self.head.set_connection_type(ConnectionType::Close);\n        self\n    }\n\n    /// Set request's content type\n    #[inline]\n    pub fn content_type<V>(mut self, value: V) -> Self\n    where\n        HeaderValue: TryFrom<V>,\n        <HeaderValue as TryFrom<V>>::Error: Into<HttpError>,\n    {\n        match HeaderValue::try_from(value) {\n            Ok(value) => {\n                self.head.headers.insert(header::CONTENT_TYPE, value);\n            }\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Set content length\n    #[inline]\n    pub fn content_length(self, len: u64) -> Self {\n        let mut buf = itoa::Buffer::new();\n        self.insert_header((header::CONTENT_LENGTH, buf.format(len)))\n    }\n\n    /// Set HTTP basic authorization header.\n    ///\n    /// If no password is needed, just provide an empty string.\n    pub fn basic_auth(self, username: impl fmt::Display, password: impl fmt::Display) -> Self {\n        let auth = format!(\"{}:{}\", username, password);\n\n        self.insert_header((\n            header::AUTHORIZATION,\n            format!(\"Basic {}\", BASE64_STANDARD.encode(auth)),\n        ))\n    }\n\n    /// Set HTTP bearer authentication header\n    pub fn bearer_auth(self, token: impl fmt::Display) -> Self {\n        self.insert_header((header::AUTHORIZATION, format!(\"Bearer {}\", token)))\n    }\n\n    /// Set a cookie\n    ///\n    /// ```no_run\n    /// use awc::{cookie::Cookie, Client};\n    ///\n    /// # #[actix_rt::main]\n    /// # async fn main() {\n    /// let res = Client::new().get(\"https://httpbin.org/cookies\")\n    ///     .cookie(Cookie::new(\"name\", \"value\"))\n    ///     .send()\n    ///     .await;\n    ///\n    /// println!(\"Response: {:?}\", res);\n    /// # }\n    /// ```\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {\n        self.cookies\n            .get_or_insert_with(CookieJar::new)\n            .add(cookie.into_owned());\n        self\n    }\n\n    /// Disable automatic decompress of response's body\n    pub fn no_decompress(mut self) -> Self {\n        self.response_decompress = false;\n        self\n    }\n\n    /// Set request timeout. Overrides client wide timeout setting.\n    ///\n    /// Request timeout is the total time before a response must be received.\n    /// Default value is 5 seconds.\n    pub fn timeout(mut self, timeout: Duration) -> Self {\n        self.timeout = Some(timeout);\n        self\n    }\n\n    /// Sets the query part of the request\n    pub fn query<T: Serialize>(mut self, query: &T) -> Result<Self, serde_urlencoded::ser::Error> {\n        let mut parts = self.head.uri.clone().into_parts();\n\n        if let Some(path_and_query) = parts.path_and_query {\n            let query = serde_urlencoded::to_string(query)?;\n            let path = path_and_query.path();\n            parts.path_and_query = format!(\"{}?{}\", path, query).parse().ok();\n\n            match Uri::from_parts(parts) {\n                Ok(uri) => self.head.uri = uri,\n                Err(err) => self.err = Some(err.into()),\n            }\n        }\n\n        Ok(self)\n    }\n\n    /// Freeze request builder and construct `FrozenClientRequest`,\n    /// which could be used for sending same request multiple times.\n    pub fn freeze(self) -> Result<FrozenClientRequest, FreezeRequestError> {\n        let slf = self.prep_for_sending()?;\n\n        let request = FrozenClientRequest {\n            head: Rc::new(slf.head),\n            addr: slf.addr,\n            response_decompress: slf.response_decompress,\n            timeout: slf.timeout,\n            config: slf.config,\n        };\n\n        Ok(request)\n    }\n\n    /// Complete request construction and send body.\n    pub fn send_body<B>(self, body: B) -> SendClientRequest\n    where\n        B: MessageBody + 'static,\n    {\n        let slf = match self.prep_for_sending() {\n            Ok(slf) => slf,\n            Err(err) => return err.into(),\n        };\n\n        RequestSender::Owned(slf.head).send_body(\n            slf.addr,\n            slf.response_decompress,\n            slf.timeout,\n            &slf.config,\n            body,\n        )\n    }\n\n    /// Set a JSON body and generate `ClientRequest`\n    pub fn send_json<T: Serialize>(self, value: &T) -> SendClientRequest {\n        let slf = match self.prep_for_sending() {\n            Ok(slf) => slf,\n            Err(err) => return err.into(),\n        };\n\n        RequestSender::Owned(slf.head).send_json(\n            slf.addr,\n            slf.response_decompress,\n            slf.timeout,\n            &slf.config,\n            value,\n        )\n    }\n\n    /// Set a urlencoded body and generate `ClientRequest`\n    ///\n    /// `ClientRequestBuilder` can not be used after this call.\n    pub fn send_form<T: Serialize>(self, value: &T) -> SendClientRequest {\n        let slf = match self.prep_for_sending() {\n            Ok(slf) => slf,\n            Err(err) => return err.into(),\n        };\n\n        RequestSender::Owned(slf.head).send_form(\n            slf.addr,\n            slf.response_decompress,\n            slf.timeout,\n            &slf.config,\n            value,\n        )\n    }\n\n    /// Set an streaming body and generate `ClientRequest`.\n    pub fn send_stream<S, E>(self, stream: S) -> SendClientRequest\n    where\n        S: Stream<Item = Result<Bytes, E>> + 'static,\n        E: Into<BoxError> + 'static,\n    {\n        let slf = match self.prep_for_sending() {\n            Ok(slf) => slf,\n            Err(err) => return err.into(),\n        };\n\n        RequestSender::Owned(slf.head).send_stream(\n            slf.addr,\n            slf.response_decompress,\n            slf.timeout,\n            &slf.config,\n            stream,\n        )\n    }\n\n    /// Set an empty body and generate `ClientRequest`.\n    pub fn send(self) -> SendClientRequest {\n        let slf = match self.prep_for_sending() {\n            Ok(slf) => slf,\n            Err(err) => return err.into(),\n        };\n\n        RequestSender::Owned(slf.head).send(\n            slf.addr,\n            slf.response_decompress,\n            slf.timeout,\n            &slf.config,\n        )\n    }\n\n    // allow unused mut when cookies feature is disabled\n    fn prep_for_sending(#[allow(unused_mut)] mut self) -> Result<Self, PrepForSendingError> {\n        if let Some(err) = self.err {\n            return Err(err.into());\n        }\n\n        // validate uri\n        let uri = &self.head.uri;\n        if uri.host().is_none() {\n            return Err(InvalidUrl::MissingHost.into());\n        } else if uri.scheme().is_none() {\n            return Err(InvalidUrl::MissingScheme.into());\n        } else if let Some(scheme) = uri.scheme() {\n            match scheme.as_str() {\n                \"http\" | \"ws\" | \"https\" | \"wss\" => {}\n                _ => return Err(InvalidUrl::UnknownScheme.into()),\n            }\n        } else {\n            return Err(InvalidUrl::UnknownScheme.into());\n        }\n\n        // set cookies\n        #[cfg(feature = \"cookies\")]\n        if let Some(ref mut jar) = self.cookies {\n            let cookie: String = jar\n                .delta()\n                // ensure only name=value is written to cookie header\n                .map(|c| c.stripped().encoded().to_string())\n                .collect::<Vec<_>>()\n                .join(\"; \");\n\n            if !cookie.is_empty() {\n                self.head\n                    .headers\n                    .insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());\n            }\n        }\n\n        let mut slf = self;\n\n        // Set Accept-Encoding HTTP header depending on enabled feature.\n        // If decompress is not ask, then we are not able to find which encoding is\n        // supported, so we cannot guess Accept-Encoding HTTP header.\n        if slf.response_decompress {\n            // Set Accept-Encoding with compression algorithm awc is built with.\n            #[allow(clippy::vec_init_then_push)]\n            #[cfg(feature = \"__compress\")]\n            let accept_encoding = {\n                let mut encoding = vec![];\n\n                #[cfg(feature = \"compress-brotli\")]\n                {\n                    encoding.push(\"br\");\n                }\n\n                #[cfg(feature = \"compress-gzip\")]\n                {\n                    encoding.push(\"gzip\");\n                    encoding.push(\"deflate\");\n                }\n\n                #[cfg(feature = \"compress-zstd\")]\n                encoding.push(\"zstd\");\n\n                assert!(\n                    !encoding.is_empty(),\n                    \"encoding can not be empty unless __compress feature has been explicitly enabled\"\n                );\n\n                encoding.join(\", \")\n            };\n\n            // Otherwise tell the server, we do not support any compression algorithm.\n            // So we clearly indicate that we do want identity encoding.\n            #[cfg(not(feature = \"__compress\"))]\n            let accept_encoding = \"identity\";\n\n            slf = slf.insert_header_if_none((header::ACCEPT_ENCODING, accept_encoding));\n        }\n\n        Ok(slf)\n    }\n}\n\nimpl fmt::Debug for ClientRequest {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(\n            f,\n            \"\\nClientRequest {:?} {} {}\",\n            self.head.version, self.head.method, self.head.uri\n        )?;\n        writeln!(f, \"  headers:\")?;\n        for (key, val) in self.head.headers.iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::SystemTime;\n\n    use actix_http::header::HttpDate;\n\n    use super::*;\n    use crate::Client;\n\n    #[actix_rt::test]\n    async fn test_debug() {\n        let request = Client::new().get(\"/\").append_header((\"x-test\", \"111\"));\n        let repr = format!(\"{:?}\", request);\n        assert!(repr.contains(\"ClientRequest\"));\n        assert!(repr.contains(\"x-test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_basics() {\n        let req = Client::new()\n            .put(\"/\")\n            .version(Version::HTTP_2)\n            .insert_header((header::DATE, HttpDate::from(SystemTime::now())))\n            .content_type(\"plain/text\")\n            .append_header((header::SERVER, \"awc\"));\n\n        let req = if let Some(val) = Some(\"server\") {\n            req.append_header((header::USER_AGENT, val))\n        } else {\n            req\n        };\n\n        let req = if let Some(_val) = Option::<&str>::None {\n            req.append_header((header::ALLOW, \"1\"))\n        } else {\n            req\n        };\n\n        let mut req = req.content_length(100);\n\n        assert!(req.headers().contains_key(header::CONTENT_TYPE));\n        assert!(req.headers().contains_key(header::DATE));\n        assert!(req.headers().contains_key(header::SERVER));\n        assert!(req.headers().contains_key(header::USER_AGENT));\n        assert!(!req.headers().contains_key(header::ALLOW));\n        assert!(!req.headers().contains_key(header::EXPECT));\n        assert_eq!(req.head.version, Version::HTTP_2);\n\n        let _ = req.headers_mut();\n\n        #[allow(clippy::let_underscore_future)]\n        let _ = req.send_body(\"\");\n    }\n\n    #[actix_rt::test]\n    async fn test_client_header() {\n        let req = Client::builder()\n            .add_default_header((header::CONTENT_TYPE, \"111\"))\n            .finish()\n            .get(\"/\");\n\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::CONTENT_TYPE)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"111\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn test_client_header_override() {\n        let req = Client::builder()\n            .add_default_header((header::CONTENT_TYPE, \"111\"))\n            .finish()\n            .get(\"/\")\n            .insert_header((header::CONTENT_TYPE, \"222\"));\n\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::CONTENT_TYPE)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"222\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn client_basic_auth() {\n        let req = Client::new().get(\"/\").basic_auth(\"username\", \"password\");\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\"\n        );\n\n        let req = Client::new().get(\"/\").basic_auth(\"username\", \"\");\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn client_bearer_auth() {\n        let req = Client::new().get(\"/\").bearer_auth(\"someS3cr3tAutht0k3n\");\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Bearer someS3cr3tAutht0k3n\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn client_query() {\n        let req = Client::new()\n            .get(\"/\")\n            .query(&[(\"key1\", \"val1\"), (\"key2\", \"val2\")])\n            .unwrap();\n        assert_eq!(req.get_uri().query().unwrap(), \"key1=val1&key2=val2\");\n    }\n}\n"
  },
  {
    "path": "awc/src/responses/json_body.rs",
    "content": "use std::{\n    future::Future,\n    marker::PhantomData,\n    mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::{error::PayloadError, header, HttpMessage};\nuse bytes::Bytes;\nuse futures_core::{ready, Stream};\nuse pin_project_lite::pin_project;\nuse serde::de::DeserializeOwned;\n\nuse super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT};\nuse crate::{error::JsonPayloadError, ClientResponse};\n\npin_project! {\n    /// A `Future` that reads a body stream, parses JSON, resolving to a deserialized `T`.\n    ///\n    /// # Errors\n    /// `Future` implementation returns error if:\n    /// - content type is not `application/json`;\n    /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).\n    pub struct JsonBody<S, T> {\n        #[pin]\n        body: Option<ReadBody<S>>,\n        length: Option<usize>,\n        timeout: ResponseTimeout,\n        err: Option<JsonPayloadError>,\n        _phantom: PhantomData<T>,\n    }\n}\n\nimpl<S, T> JsonBody<S, T>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n    T: DeserializeOwned,\n{\n    /// Creates a JSON body stream reader from a response by taking its payload.\n    pub fn new(res: &mut ClientResponse<S>) -> Self {\n        // check content-type\n        let json = if let Ok(Some(mime)) = res.mime_type() {\n            mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)\n        } else {\n            false\n        };\n\n        if !json {\n            return JsonBody {\n                length: None,\n                body: None,\n                timeout: ResponseTimeout::default(),\n                err: Some(JsonPayloadError::ContentType),\n                _phantom: PhantomData,\n            };\n        }\n\n        let length = res\n            .headers()\n            .get(&header::CONTENT_LENGTH)\n            .and_then(|len_hdr| len_hdr.to_str().ok())\n            .and_then(|len_str| len_str.parse::<usize>().ok());\n\n        JsonBody {\n            body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)),\n            length,\n            timeout: mem::take(&mut res.timeout),\n            err: None,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Change max size of payload. Default limit is 2 MiB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        if let Some(ref mut fut) = self.body {\n            fut.limit = limit;\n        }\n\n        self\n    }\n}\n\nimpl<S, T> Future for JsonBody<S, T>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n    T: DeserializeOwned,\n{\n    type Output = Result<T, JsonPayloadError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n\n        if let Some(err) = this.err.take() {\n            return Poll::Ready(Err(err));\n        }\n\n        if let Some(len) = this.length.take() {\n            let body = Option::as_ref(&this.body).unwrap();\n            if len > body.limit {\n                return Poll::Ready(Err(JsonPayloadError::Payload(PayloadError::Overflow)));\n            }\n        }\n\n        this.timeout\n            .poll_timeout(cx)\n            .map_err(JsonPayloadError::Payload)?;\n\n        let body = ready!(this.body.as_pin_mut().unwrap().poll(cx))?;\n        Poll::Ready(serde_json::from_slice::<T>(&body).map_err(JsonPayloadError::from))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use actix_http::BoxedPayloadStream;\n    use serde::{Deserialize, Serialize};\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::test::TestResponse;\n\n    assert_impl_all!(JsonBody<BoxedPayloadStream, String>: Unpin);\n\n    #[derive(Serialize, Deserialize, PartialEq, Debug)]\n    struct MyObject {\n        name: String,\n    }\n\n    fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {\n        match err {\n            JsonPayloadError::Payload(PayloadError::Overflow) => {\n                matches!(other, JsonPayloadError::Payload(PayloadError::Overflow))\n            }\n            JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),\n            _ => false,\n        }\n    }\n\n    #[actix_rt::test]\n    async fn read_json_body() {\n        let mut req = TestResponse::default().finish();\n        let json = JsonBody::<_, MyObject>::new(&mut req).await;\n        assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));\n\n        let mut req = TestResponse::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/text\"),\n            ))\n            .finish();\n        let json = JsonBody::<_, MyObject>::new(&mut req).await;\n        assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));\n\n        let mut req = TestResponse::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"10000\"),\n            ))\n            .finish();\n\n        let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await;\n        assert!(json_eq(\n            json.err().unwrap(),\n            JsonPayloadError::Payload(PayloadError::Overflow)\n        ));\n\n        let mut req = TestResponse::default()\n            .insert_header((\n                header::CONTENT_TYPE,\n                header::HeaderValue::from_static(\"application/json\"),\n            ))\n            .insert_header((\n                header::CONTENT_LENGTH,\n                header::HeaderValue::from_static(\"16\"),\n            ))\n            .set_payload(Bytes::from_static(b\"{\\\"name\\\": \\\"test\\\"}\"))\n            .finish();\n\n        let json = JsonBody::<_, MyObject>::new(&mut req).await;\n        assert_eq!(\n            json.ok().unwrap(),\n            MyObject {\n                name: \"test\".to_owned()\n            }\n        );\n    }\n}\n"
  },
  {
    "path": "awc/src/responses/mod.rs",
    "content": "use std::{future::Future, io, pin::Pin, task::Context};\n\nuse actix_http::error::PayloadError;\nuse actix_rt::time::Sleep;\n\nmod json_body;\nmod read_body;\nmod response;\nmod response_body;\n\n#[allow(deprecated)]\npub use self::response_body::{MessageBody, ResponseBody};\npub use self::{json_body::JsonBody, response::ClientResponse};\n\n/// Default body size limit: 2 MiB\nconst DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024;\n\n/// Helper enum with reusable sleep passed from `SendClientResponse`.\n///\n/// See [`ClientResponse::_timeout`] for reason.\npub(crate) enum ResponseTimeout {\n    Disabled(Option<Pin<Box<Sleep>>>),\n    Enabled(Pin<Box<Sleep>>),\n}\n\nimpl Default for ResponseTimeout {\n    fn default() -> Self {\n        Self::Disabled(None)\n    }\n}\n\nimpl ResponseTimeout {\n    fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {\n        match *self {\n            Self::Enabled(ref mut timeout) => {\n                if timeout.as_mut().poll(cx).is_ready() {\n                    Err(PayloadError::Io(io::Error::new(\n                        io::ErrorKind::TimedOut,\n                        \"Response Payload IO timed out\",\n                    )))\n                } else {\n                    Ok(())\n                }\n            }\n            Self::Disabled(_) => Ok(()),\n        }\n    }\n}\n"
  },
  {
    "path": "awc/src/responses/read_body.rs",
    "content": "use std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::{error::PayloadError, Payload};\nuse bytes::{Bytes, BytesMut};\nuse futures_core::{ready, Stream};\nuse pin_project_lite::pin_project;\n\npin_project! {\n    pub(crate) struct ReadBody<S> {\n        #[pin]\n        pub(crate) stream: Payload<S>,\n        pub(crate) buf: BytesMut,\n        pub(crate) limit: usize,\n    }\n}\n\nimpl<S> ReadBody<S> {\n    pub(crate) fn new(stream: Payload<S>, limit: usize) -> Self {\n        Self {\n            stream,\n            buf: BytesMut::new(),\n            limit,\n        }\n    }\n}\n\nimpl<S> Future for ReadBody<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    type Output = Result<Bytes, PayloadError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let mut this = self.project();\n\n        while let Some(chunk) = ready!(this.stream.as_mut().poll_next(cx)?) {\n            if (this.buf.len() + chunk.len()) > *this.limit {\n                return Poll::Ready(Err(PayloadError::Overflow));\n            }\n\n            this.buf.extend_from_slice(&chunk);\n        }\n\n        Poll::Ready(Ok(this.buf.split().freeze()))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::any_body::AnyBody;\n\n    assert_impl_all!(ReadBody<()>: Unpin);\n    assert_impl_all!(ReadBody<AnyBody>: Unpin);\n}\n"
  },
  {
    "path": "awc/src/responses/response.rs",
    "content": "use std::{\n    cell::{Ref, RefCell, RefMut},\n    fmt, mem,\n    pin::Pin,\n    task::{Context, Poll},\n    time::{Duration, Instant},\n};\n\nuse actix_http::{\n    error::PayloadError, header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Payload,\n    ResponseHead, StatusCode, Version,\n};\nuse actix_rt::time::{sleep, Sleep};\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse pin_project_lite::pin_project;\nuse serde::de::DeserializeOwned;\n\nuse super::{JsonBody, ResponseBody, ResponseTimeout};\n#[cfg(feature = \"cookies\")]\nuse crate::cookie::{Cookie, ParseError as CookieParseError};\n\npin_project! {\n    /// Client Response\n    pub struct ClientResponse<S = BoxedPayloadStream> {\n        pub(crate) head: ResponseHead,\n        #[pin]\n        pub(crate) payload: Payload<S>,\n        pub(crate) timeout: ResponseTimeout,\n        pub(crate) extensions: RefCell<Extensions>,\n\n    }\n}\n\nimpl<S> ClientResponse<S> {\n    /// Create new Request instance\n    pub(crate) fn new(head: ResponseHead, payload: Payload<S>) -> Self {\n        ClientResponse {\n            head,\n            payload,\n            timeout: ResponseTimeout::default(),\n            extensions: RefCell::new(Extensions::new()),\n        }\n    }\n\n    #[inline]\n    pub(crate) fn head(&self) -> &ResponseHead {\n        &self.head\n    }\n\n    /// Read the Request Version.\n    #[inline]\n    pub fn version(&self) -> Version {\n        self.head().version\n    }\n\n    /// Get the status from the server.\n    #[inline]\n    pub fn status(&self) -> StatusCode {\n        self.head().status\n    }\n\n    #[inline]\n    /// Returns request's headers.\n    pub fn headers(&self) -> &HeaderMap {\n        &self.head().headers\n    }\n\n    /// Map the current body type to another using a closure. Returns a new response.\n    ///\n    /// Closure receives the response head and the current body type.\n    pub fn map_body<F, U>(mut self, f: F) -> ClientResponse<U>\n    where\n        F: FnOnce(&mut ResponseHead, Payload<S>) -> Payload<U>,\n    {\n        let payload = f(&mut self.head, self.payload);\n\n        ClientResponse {\n            payload,\n            head: self.head,\n            timeout: self.timeout,\n            extensions: self.extensions,\n        }\n    }\n\n    /// Set a timeout duration for [`ClientResponse`](self::ClientResponse).\n    ///\n    /// This duration covers the duration of processing the response body stream\n    /// and would end it as timeout error when deadline met.\n    ///\n    /// Disabled by default.\n    pub fn timeout(self, dur: Duration) -> Self {\n        let timeout = match self.timeout {\n            ResponseTimeout::Disabled(Some(mut timeout))\n            | ResponseTimeout::Enabled(mut timeout) => match Instant::now().checked_add(dur) {\n                Some(deadline) => {\n                    timeout.as_mut().reset(deadline.into());\n                    ResponseTimeout::Enabled(timeout)\n                }\n                None => ResponseTimeout::Enabled(Box::pin(sleep(dur))),\n            },\n            _ => ResponseTimeout::Enabled(Box::pin(sleep(dur))),\n        };\n\n        Self {\n            payload: self.payload,\n            head: self.head,\n            timeout,\n            extensions: self.extensions,\n        }\n    }\n\n    /// This method does not enable timeout. It's used to pass the boxed `Sleep` from\n    /// `SendClientRequest` and reuse it's heap allocation together with it's slot in\n    /// timer wheel.\n    pub(crate) fn _timeout(mut self, timeout: Option<Pin<Box<Sleep>>>) -> Self {\n        self.timeout = ResponseTimeout::Disabled(timeout);\n        self\n    }\n\n    /// Load request cookies.\n    #[cfg(feature = \"cookies\")]\n    pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {\n        struct Cookies(Vec<Cookie<'static>>);\n\n        if self.extensions().get::<Cookies>().is_none() {\n            let mut cookies = Vec::new();\n            for hdr in self.headers().get_all(&actix_http::header::SET_COOKIE) {\n                let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;\n                cookies.push(Cookie::parse_encoded(s)?.into_owned());\n            }\n            self.extensions_mut().insert(Cookies(cookies));\n        }\n\n        Ok(Ref::map(self.extensions(), |ext| {\n            &ext.get::<Cookies>().unwrap().0\n        }))\n    }\n\n    /// Return request cookie.\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {\n        if let Ok(cookies) = self.cookies() {\n            for cookie in cookies.iter() {\n                if cookie.name() == name {\n                    return Some(cookie.to_owned());\n                }\n            }\n        }\n        None\n    }\n}\n\nimpl<S> ClientResponse<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    /// Returns a [`Future`] that consumes the body stream and resolves to [`Bytes`].\n    ///\n    /// # Errors\n    /// `Future` implementation returns error if:\n    /// - content length is greater than [limit](ResponseBody::limit) (default: 2 MiB)\n    ///\n    /// # Examples\n    /// ```no_run\n    /// # use awc::Client;\n    /// # use bytes::Bytes;\n    /// # #[actix_rt::main]\n    /// # async fn async_ctx() -> Result<(), Box<dyn std::error::Error>> {\n    /// let client = Client::default();\n    /// let mut res = client.get(\"https://httpbin.org/robots.txt\").send().await?;\n    /// let body: Bytes = res.body().await?;\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Future`]: std::future::Future\n    pub fn body(&mut self) -> ResponseBody<S> {\n        ResponseBody::new(self)\n    }\n\n    /// Returns a [`Future`] consumes the body stream, parses JSON, and resolves to a deserialized\n    /// `T` value.\n    ///\n    /// # Errors\n    /// Future returns error if:\n    /// - content type is not `application/json`;\n    /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).\n    ///\n    /// # Examples\n    /// ```no_run\n    /// # use awc::Client;\n    /// # #[actix_rt::main]\n    /// # async fn async_ctx() -> Result<(), Box<dyn std::error::Error>> {\n    /// let client = Client::default();\n    /// let mut res = client.get(\"https://httpbin.org/json\").send().await?;\n    /// let val = res.json::<serde_json::Value>().await?;\n    /// assert!(val.is_object());\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Future`]: std::future::Future\n    pub fn json<T: DeserializeOwned>(&mut self) -> JsonBody<S, T> {\n        JsonBody::new(self)\n    }\n}\n\nimpl<S> fmt::Debug for ClientResponse<S> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(f, \"\\nClientResponse {:?} {}\", self.version(), self.status(),)?;\n        writeln!(f, \"  headers:\")?;\n        for (key, val) in self.headers().iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n        Ok(())\n    }\n}\n\nimpl<S> HttpMessage for ClientResponse<S> {\n    type Stream = S;\n\n    fn headers(&self) -> &HeaderMap {\n        &self.head.headers\n    }\n\n    fn take_payload(&mut self) -> Payload<S> {\n        mem::replace(&mut self.payload, Payload::None)\n    }\n\n    fn extensions(&self) -> Ref<'_, Extensions> {\n        self.extensions.borrow()\n    }\n\n    fn extensions_mut(&self) -> RefMut<'_, Extensions> {\n        self.extensions.borrow_mut()\n    }\n}\n\nimpl<S> Stream for ClientResponse<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,\n{\n    type Item = Result<Bytes, PayloadError>;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let this = self.project();\n        this.timeout.poll_timeout(cx)?;\n        this.payload.poll_next(cx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::any_body::AnyBody;\n\n    assert_impl_all!(ClientResponse: Unpin);\n    assert_impl_all!(ClientResponse<()>: Unpin);\n    assert_impl_all!(ClientResponse<AnyBody>: Unpin);\n}\n"
  },
  {
    "path": "awc/src/responses/response_body.rs",
    "content": "use std::{\n    future::Future,\n    mem,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse actix_http::{error::PayloadError, header, HttpMessage};\nuse bytes::Bytes;\nuse futures_core::Stream;\nuse pin_project_lite::pin_project;\n\nuse super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT};\nuse crate::ClientResponse;\n\npin_project! {\n    /// A `Future` that reads a body stream, resolving as [`Bytes`].\n    ///\n    /// # Errors\n    /// `Future` implementation returns error if:\n    /// - content type is not `application/json`;\n    /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).\n    pub struct ResponseBody<S> {\n        #[pin]\n        body: Option<ReadBody<S>>,\n        length: Option<usize>,\n        timeout: ResponseTimeout,\n        err: Option<PayloadError>,\n    }\n}\n\n#[deprecated(since = \"3.0.0\", note = \"Renamed to `ResponseBody`.\")]\npub type MessageBody<B> = ResponseBody<B>;\n\nimpl<S> ResponseBody<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    /// Creates a body stream reader from a response by taking its payload.\n    pub fn new(res: &mut ClientResponse<S>) -> ResponseBody<S> {\n        let length = match res.headers().get(&header::CONTENT_LENGTH) {\n            Some(value) => {\n                let len = value.to_str().ok().and_then(|s| s.parse::<usize>().ok());\n\n                match len {\n                    None => return Self::err(PayloadError::UnknownLength),\n                    len => len,\n                }\n            }\n            None => None,\n        };\n\n        ResponseBody {\n            body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)),\n            length,\n            timeout: mem::take(&mut res.timeout),\n            err: None,\n        }\n    }\n\n    /// Change max size limit of payload.\n    ///\n    /// The default limit is 2 MiB.\n    pub fn limit(mut self, limit: usize) -> Self {\n        if let Some(ref mut body) = self.body {\n            body.limit = limit;\n        }\n\n        self\n    }\n\n    fn err(err: PayloadError) -> Self {\n        ResponseBody {\n            body: None,\n            length: None,\n            timeout: ResponseTimeout::default(),\n            err: Some(err),\n        }\n    }\n}\n\nimpl<S> Future for ResponseBody<S>\nwhere\n    S: Stream<Item = Result<Bytes, PayloadError>>,\n{\n    type Output = Result<Bytes, PayloadError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n\n        if let Some(err) = this.err.take() {\n            return Poll::Ready(Err(err));\n        }\n\n        if let Some(len) = this.length.take() {\n            let body = Option::as_ref(&this.body).unwrap();\n            if len > body.limit {\n                return Poll::Ready(Err(PayloadError::Overflow));\n            }\n        }\n\n        this.timeout.poll_timeout(cx)?;\n\n        this.body.as_pin_mut().unwrap().poll(cx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use static_assertions::assert_impl_all;\n\n    use super::*;\n    use crate::test::TestResponse;\n\n    assert_impl_all!(ResponseBody<()>: Unpin);\n\n    #[actix_rt::test]\n    async fn read_body() {\n        let mut req = TestResponse::with_header((header::CONTENT_LENGTH, \"xxxx\")).finish();\n        match req.body().await.err().unwrap() {\n            PayloadError::UnknownLength => {}\n            _ => unreachable!(\"error\"),\n        }\n\n        let mut req = TestResponse::with_header((header::CONTENT_LENGTH, \"10000000\")).finish();\n        match req.body().await.err().unwrap() {\n            PayloadError::Overflow => {}\n            _ => unreachable!(\"error\"),\n        }\n\n        let mut req = TestResponse::default()\n            .set_payload(Bytes::from_static(b\"test\"))\n            .finish();\n        assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b\"test\"));\n\n        let mut req = TestResponse::default()\n            .set_payload(Bytes::from_static(b\"11111111111111\"))\n            .finish();\n        match req.body().limit(5).await.err().unwrap() {\n            PayloadError::Overflow => {}\n            _ => unreachable!(\"error\"),\n        }\n    }\n}\n"
  },
  {
    "path": "awc/src/sender.rs",
    "content": "use std::{\n    future::Future,\n    net,\n    pin::Pin,\n    rc::Rc,\n    task::{Context, Poll},\n    time::Duration,\n};\n\nuse actix_http::{\n    body::{BodyStream, MessageBody},\n    error::HttpError,\n    header::{self, HeaderMap, HeaderName, TryIntoHeaderValue},\n    RequestHead, RequestHeadType,\n};\n#[cfg(feature = \"__compress\")]\nuse actix_http::{encoding::Decoder, header::ContentEncoding, Payload};\nuse actix_rt::time::{sleep, Sleep};\nuse bytes::Bytes;\nuse derive_more::From;\nuse futures_core::Stream;\nuse serde::Serialize;\n\nuse crate::{\n    any_body::AnyBody,\n    client::ClientConfig,\n    error::{FreezeRequestError, InvalidUrl, SendRequestError},\n    BoxError, ClientResponse, ConnectRequest, ConnectResponse,\n};\n\n#[derive(Debug, From)]\npub(crate) enum PrepForSendingError {\n    Url(InvalidUrl),\n    Http(HttpError),\n    Json(serde_json::Error),\n    Form(serde_urlencoded::ser::Error),\n}\n\nimpl From<PrepForSendingError> for FreezeRequestError {\n    fn from(err: PrepForSendingError) -> FreezeRequestError {\n        match err {\n            PrepForSendingError::Url(err) => FreezeRequestError::Url(err),\n            PrepForSendingError::Http(err) => FreezeRequestError::Http(err),\n            PrepForSendingError::Json(err) => {\n                FreezeRequestError::Custom(Box::new(err), Box::new(\"json serialization error\"))\n            }\n            PrepForSendingError::Form(err) => {\n                FreezeRequestError::Custom(Box::new(err), Box::new(\"form serialization error\"))\n            }\n        }\n    }\n}\n\nimpl From<PrepForSendingError> for SendRequestError {\n    fn from(err: PrepForSendingError) -> SendRequestError {\n        match err {\n            PrepForSendingError::Url(err) => SendRequestError::Url(err),\n            PrepForSendingError::Http(err) => SendRequestError::Http(err),\n            PrepForSendingError::Json(err) => {\n                SendRequestError::Custom(Box::new(err), Box::new(\"json serialization error\"))\n            }\n            PrepForSendingError::Form(err) => {\n                SendRequestError::Custom(Box::new(err), Box::new(\"form serialization error\"))\n            }\n        }\n    }\n}\n\n/// Future that sends request's payload and resolves to a server response.\n#[must_use = \"futures do nothing unless polled\"]\npub enum SendClientRequest {\n    Fut(\n        Pin<Box<dyn Future<Output = Result<ConnectResponse, SendRequestError>>>>,\n        // FIXME: use a pinned Sleep instead of box.\n        Option<Pin<Box<Sleep>>>,\n        bool,\n    ),\n    Err(Option<SendRequestError>),\n}\n\nimpl SendClientRequest {\n    pub(crate) fn new(\n        send: Pin<Box<dyn Future<Output = Result<ConnectResponse, SendRequestError>>>>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n    ) -> SendClientRequest {\n        let delay = timeout.map(|d| Box::pin(sleep(d)));\n        SendClientRequest::Fut(send, delay, response_decompress)\n    }\n}\n\n#[cfg(feature = \"__compress\")]\nimpl Future for SendClientRequest {\n    type Output = Result<ClientResponse<Decoder<Payload>>, SendRequestError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n\n        match this {\n            SendClientRequest::Fut(send, delay, response_decompress) => {\n                if let Some(delay) = delay {\n                    if delay.as_mut().poll(cx).is_ready() {\n                        return Poll::Ready(Err(SendRequestError::Timeout));\n                    }\n                }\n\n                let res = futures_core::ready!(send.as_mut().poll(cx)).map(|res| {\n                    res.into_client_response()\n                        ._timeout(delay.take())\n                        .map_body(|head, payload| {\n                            if *response_decompress {\n                                Payload::Stream {\n                                    payload: Decoder::from_headers(payload, &head.headers),\n                                }\n                            } else {\n                                Payload::Stream {\n                                    payload: Decoder::new(payload, ContentEncoding::Identity),\n                                }\n                            }\n                        })\n                });\n\n                Poll::Ready(res)\n            }\n            SendClientRequest::Err(ref mut err) => match err.take() {\n                Some(err) => Poll::Ready(Err(err)),\n                None => panic!(\"Attempting to call completed future\"),\n            },\n        }\n    }\n}\n\n#[cfg(not(feature = \"__compress\"))]\nimpl Future for SendClientRequest {\n    type Output = Result<ClientResponse, SendRequestError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.get_mut();\n        match this {\n            SendClientRequest::Fut(send, delay, _) => {\n                if let Some(delay) = delay {\n                    if delay.as_mut().poll(cx).is_ready() {\n                        return Poll::Ready(Err(SendRequestError::Timeout));\n                    }\n                }\n                send.as_mut()\n                    .poll(cx)\n                    .map_ok(|res| res.into_client_response()._timeout(delay.take()))\n            }\n            SendClientRequest::Err(ref mut err) => match err.take() {\n                Some(err) => Poll::Ready(Err(err)),\n                None => panic!(\"Attempting to call completed future\"),\n            },\n        }\n    }\n}\n\nimpl From<SendRequestError> for SendClientRequest {\n    fn from(err: SendRequestError) -> Self {\n        SendClientRequest::Err(Some(err))\n    }\n}\n\nimpl From<HttpError> for SendClientRequest {\n    fn from(err: HttpError) -> Self {\n        SendClientRequest::Err(Some(err.into()))\n    }\n}\n\nimpl From<PrepForSendingError> for SendClientRequest {\n    fn from(err: PrepForSendingError) -> Self {\n        SendClientRequest::Err(Some(err.into()))\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum RequestSender {\n    Owned(RequestHead),\n    Rc(Rc<RequestHead>, Option<HeaderMap>),\n}\n\nimpl RequestSender {\n    pub(crate) fn send_body(\n        self,\n        addr: Option<net::SocketAddr>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n        config: &ClientConfig,\n        body: impl MessageBody + 'static,\n    ) -> SendClientRequest {\n        let req = match self {\n            RequestSender::Owned(head) => ConnectRequest::Client(\n                RequestHeadType::Owned(head),\n                AnyBody::from_message_body(body).into_boxed(),\n                addr,\n            ),\n            RequestSender::Rc(head, extra_headers) => ConnectRequest::Client(\n                RequestHeadType::Rc(head, extra_headers),\n                AnyBody::from_message_body(body).into_boxed(),\n                addr,\n            ),\n        };\n\n        let fut = config.connector.call(req);\n\n        SendClientRequest::new(fut, response_decompress, timeout.or(config.timeout))\n    }\n\n    pub(crate) fn send_json(\n        mut self,\n        addr: Option<net::SocketAddr>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n        config: &ClientConfig,\n        value: impl Serialize,\n    ) -> SendClientRequest {\n        let body = match serde_json::to_string(&value) {\n            Ok(body) => body,\n            Err(err) => return PrepForSendingError::Json(err).into(),\n        };\n\n        if let Err(err) = self.set_header_if_none(header::CONTENT_TYPE, \"application/json\") {\n            return err.into();\n        }\n\n        self.send_body(addr, response_decompress, timeout, config, body)\n    }\n\n    pub(crate) fn send_form(\n        mut self,\n        addr: Option<net::SocketAddr>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n        config: &ClientConfig,\n        value: impl Serialize,\n    ) -> SendClientRequest {\n        let body = match serde_urlencoded::to_string(value) {\n            Ok(body) => body,\n            Err(err) => return PrepForSendingError::Form(err).into(),\n        };\n\n        // set content-type\n        if let Err(err) =\n            self.set_header_if_none(header::CONTENT_TYPE, \"application/x-www-form-urlencoded\")\n        {\n            return err.into();\n        }\n\n        self.send_body(addr, response_decompress, timeout, config, body)\n    }\n\n    pub(crate) fn send_stream<S, E>(\n        self,\n        addr: Option<net::SocketAddr>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n        config: &ClientConfig,\n        stream: S,\n    ) -> SendClientRequest\n    where\n        S: Stream<Item = Result<Bytes, E>> + 'static,\n        E: Into<BoxError> + 'static,\n    {\n        self.send_body(\n            addr,\n            response_decompress,\n            timeout,\n            config,\n            BodyStream::new(stream),\n        )\n    }\n\n    pub(crate) fn send(\n        self,\n        addr: Option<net::SocketAddr>,\n        response_decompress: bool,\n        timeout: Option<Duration>,\n        config: &ClientConfig,\n    ) -> SendClientRequest {\n        self.send_body(addr, response_decompress, timeout, config, ())\n    }\n\n    fn set_header_if_none<V>(&mut self, key: HeaderName, value: V) -> Result<(), HttpError>\n    where\n        V: TryIntoHeaderValue,\n    {\n        match self {\n            RequestSender::Owned(head) => {\n                if !head.headers.contains_key(&key) {\n                    match value.try_into_value() {\n                        Ok(value) => {\n                            head.headers.insert(key, value);\n                        }\n                        Err(err) => return Err(err.into()),\n                    }\n                }\n            }\n            RequestSender::Rc(head, extra_headers) => {\n                if !head.headers.contains_key(&key)\n                    && !extra_headers.iter().any(|h| h.contains_key(&key))\n                {\n                    match value.try_into_value() {\n                        Ok(v) => {\n                            let h = extra_headers.get_or_insert(HeaderMap::new());\n                            h.insert(key, v)\n                        }\n                        Err(err) => return Err(err.into()),\n                    };\n                }\n            }\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "awc/src/test.rs",
    "content": "//! Test helpers for actix http client to use during testing.\n\nuse actix_http::{h1, header::TryIntoHeaderPair, Payload, ResponseHead, StatusCode, Version};\nuse bytes::Bytes;\n\n#[cfg(feature = \"cookies\")]\nuse crate::cookie::{Cookie, CookieJar};\nuse crate::ClientResponse;\n\n/// Test `ClientResponse` builder\npub struct TestResponse {\n    head: ResponseHead,\n    #[cfg(feature = \"cookies\")]\n    cookies: CookieJar,\n    payload: Option<Payload>,\n}\n\nimpl Default for TestResponse {\n    fn default() -> TestResponse {\n        TestResponse {\n            head: ResponseHead::new(StatusCode::OK),\n            #[cfg(feature = \"cookies\")]\n            cookies: CookieJar::new(),\n            payload: None,\n        }\n    }\n}\n\nimpl TestResponse {\n    /// Create TestResponse and set header\n    pub fn with_header(header: impl TryIntoHeaderPair) -> Self {\n        Self::default().insert_header(header)\n    }\n\n    /// Set HTTP version of this response\n    pub fn version(mut self, ver: Version) -> Self {\n        self.head.version = ver;\n        self\n    }\n\n    /// Insert a header\n    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        if let Ok((key, value)) = header.try_into_pair() {\n            self.head.headers.insert(key, value);\n            return self;\n        }\n        panic!(\"Can not set header\");\n    }\n\n    /// Append a header\n    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {\n        if let Ok((key, value)) = header.try_into_pair() {\n            self.head.headers.append(key, value);\n            return self;\n        }\n        panic!(\"Can not create header\");\n    }\n\n    /// Set cookie for this response\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {\n        self.cookies.add(cookie.into_owned());\n        self\n    }\n\n    /// Set response's payload\n    pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {\n        self.payload = Some(Payload::from(data.into()));\n        self\n    }\n\n    /// Complete response creation and generate `ClientResponse` instance\n    pub fn finish(self) -> ClientResponse {\n        // allow unused mut when cookies feature is disabled\n        #[allow(unused_mut)]\n        let mut head = self.head;\n\n        #[cfg(feature = \"cookies\")]\n        for cookie in self.cookies.delta() {\n            use actix_http::header::{self, HeaderValue};\n\n            head.headers.insert(\n                header::SET_COOKIE,\n                HeaderValue::from_str(&cookie.encoded().to_string()).unwrap(),\n            );\n        }\n\n        if let Some(pl) = self.payload {\n            ClientResponse::new(head, pl)\n        } else {\n            let (_, payload) = h1::Payload::create(true);\n            ClientResponse::new(head, payload.into())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::SystemTime;\n\n    use actix_http::header::HttpDate;\n\n    use super::*;\n    use crate::http::header;\n\n    #[test]\n    fn test_basics() {\n        let res = TestResponse::default()\n            .version(Version::HTTP_2)\n            .insert_header((header::DATE, HttpDate::from(SystemTime::now())))\n            .cookie(cookie::Cookie::build(\"name\", \"value\").finish())\n            .finish();\n        assert!(res.headers().contains_key(header::SET_COOKIE));\n        assert!(res.headers().contains_key(header::DATE));\n        assert_eq!(res.version(), Version::HTTP_2);\n    }\n}\n"
  },
  {
    "path": "awc/src/ws.rs",
    "content": "//! Websockets client\n//!\n//! Type definitions required to use [`awc::Client`](super::Client) as a WebSocket client.\n//!\n//! # Examples\n//!\n//! ```no_run\n//! use awc::{Client, ws};\n//! use futures_util::{SinkExt as _, StreamExt as _};\n//!\n//! #[actix_rt::main]\n//! async fn main() {\n//!     let (_resp, mut connection) = Client::new()\n//!         .ws(\"ws://echo.websocket.org\")\n//!         .connect()\n//!         .await\n//!         .unwrap();\n//!\n//!     connection\n//!         .send(ws::Message::Text(\"Echo\".into()))\n//!         .await\n//!         .unwrap();\n//!     let response = connection.next().await.unwrap().unwrap();\n//!\n//!     assert_eq!(response, ws::Frame::Text(\"Echo\".as_bytes().into()));\n//! }\n//! ```\n\nuse std::{fmt, net::SocketAddr, str};\n\nuse actix_codec::Framed;\npub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message};\nuse actix_http::{ws, Payload, RequestHead};\nuse actix_rt::time::timeout;\nuse actix_service::Service as _;\nuse base64::prelude::*;\n\n#[cfg(feature = \"cookies\")]\nuse crate::cookie::{Cookie, CookieJar};\nuse crate::{\n    client::ClientConfig,\n    connect::{BoxedSocket, ConnectRequest},\n    error::{HttpError, InvalidUrl, SendRequestError, WsClientError},\n    http::{\n        header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION},\n        ConnectionType, Method, StatusCode, Uri, Version,\n    },\n    ClientResponse,\n};\n\n/// WebSocket connection.\npub struct WebsocketsRequest {\n    pub(crate) head: RequestHead,\n    err: Option<HttpError>,\n    origin: Option<HeaderValue>,\n    protocols: Option<String>,\n    addr: Option<SocketAddr>,\n    max_size: usize,\n    server_mode: bool,\n    config: ClientConfig,\n\n    #[cfg(feature = \"cookies\")]\n    cookies: Option<CookieJar>,\n}\n\nimpl WebsocketsRequest {\n    /// Create new WebSocket connection.\n    pub(crate) fn new<U>(uri: U, config: ClientConfig) -> Self\n    where\n        Uri: TryFrom<U>,\n        <Uri as TryFrom<U>>::Error: Into<HttpError>,\n    {\n        let mut err = None;\n\n        #[allow(clippy::field_reassign_with_default)]\n        let mut head = {\n            let mut head = RequestHead::default();\n            head.method = Method::GET;\n            head.version = Version::HTTP_11;\n            head\n        };\n\n        match Uri::try_from(uri) {\n            Ok(uri) => head.uri = uri,\n            Err(error) => err = Some(error.into()),\n        }\n\n        WebsocketsRequest {\n            head,\n            err,\n            config,\n            addr: None,\n            origin: None,\n            protocols: None,\n            max_size: 65_536,\n            server_mode: false,\n            #[cfg(feature = \"cookies\")]\n            cookies: None,\n        }\n    }\n\n    /// Set socket address of the server.\n    ///\n    /// This address is used for connection. If address is not\n    /// provided url's host name get resolved.\n    pub fn address(mut self, addr: SocketAddr) -> Self {\n        self.addr = Some(addr);\n        self\n    }\n\n    /// Set supported WebSocket protocols\n    pub fn protocols<U, V>(mut self, protos: U) -> Self\n    where\n        U: IntoIterator<Item = V>,\n        V: AsRef<str>,\n    {\n        let mut protos = protos\n            .into_iter()\n            .fold(String::new(), |acc, s| acc + s.as_ref() + \",\");\n        protos.pop();\n        self.protocols = Some(protos);\n        self\n    }\n\n    /// Set a cookie\n    #[cfg(feature = \"cookies\")]\n    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {\n        self.cookies\n            .get_or_insert_with(CookieJar::new)\n            .add(cookie.into_owned());\n        self\n    }\n\n    /// Set request Origin\n    pub fn origin<V, E>(mut self, origin: V) -> Self\n    where\n        HeaderValue: TryFrom<V, Error = E>,\n        HttpError: From<E>,\n    {\n        match HeaderValue::try_from(origin) {\n            Ok(value) => self.origin = Some(value),\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Set max frame size\n    ///\n    /// By default max size is set to 64kB\n    pub fn max_frame_size(mut self, size: usize) -> Self {\n        self.max_size = size;\n        self\n    }\n\n    /// Disable payload masking. By default ws client masks frame payload.\n    pub fn server_mode(mut self) -> Self {\n        self.server_mode = true;\n        self\n    }\n\n    /// Append a header.\n    ///\n    /// Header gets appended to existing header.\n    /// To override header use `set_header()` method.\n    pub fn header<K, V>(mut self, key: K, value: V) -> Self\n    where\n        HeaderName: TryFrom<K>,\n        <HeaderName as TryFrom<K>>::Error: Into<HttpError>,\n        V: TryIntoHeaderValue,\n    {\n        match HeaderName::try_from(key) {\n            Ok(key) => match value.try_into_value() {\n                Ok(value) => {\n                    self.head.headers.append(key, value);\n                }\n                Err(err) => self.err = Some(err.into()),\n            },\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Insert a header, replaces existing header.\n    pub fn set_header<K, V>(mut self, key: K, value: V) -> Self\n    where\n        HeaderName: TryFrom<K>,\n        <HeaderName as TryFrom<K>>::Error: Into<HttpError>,\n        V: TryIntoHeaderValue,\n    {\n        match HeaderName::try_from(key) {\n            Ok(key) => match value.try_into_value() {\n                Ok(value) => {\n                    self.head.headers.insert(key, value);\n                }\n                Err(err) => self.err = Some(err.into()),\n            },\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Insert a header only if it is not yet set.\n    pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self\n    where\n        HeaderName: TryFrom<K>,\n        <HeaderName as TryFrom<K>>::Error: Into<HttpError>,\n        V: TryIntoHeaderValue,\n    {\n        match HeaderName::try_from(key) {\n            Ok(key) => {\n                if !self.head.headers.contains_key(&key) {\n                    match value.try_into_value() {\n                        Ok(value) => {\n                            self.head.headers.insert(key, value);\n                        }\n                        Err(err) => self.err = Some(err.into()),\n                    }\n                }\n            }\n            Err(err) => self.err = Some(err.into()),\n        }\n        self\n    }\n\n    /// Set HTTP basic authorization header\n    pub fn basic_auth<U>(self, username: U, password: Option<&str>) -> Self\n    where\n        U: fmt::Display,\n    {\n        let auth = match password {\n            Some(password) => format!(\"{}:{}\", username, password),\n            None => format!(\"{}:\", username),\n        };\n        self.header(\n            AUTHORIZATION,\n            format!(\"Basic {}\", BASE64_STANDARD.encode(auth)),\n        )\n    }\n\n    /// Set HTTP bearer authentication header\n    pub fn bearer_auth<T>(self, token: T) -> Self\n    where\n        T: fmt::Display,\n    {\n        self.header(AUTHORIZATION, format!(\"Bearer {}\", token))\n    }\n\n    /// Returns whether headers should be sent in Camel-Case.\n    ///\n    /// Default is `false`.\n    #[inline]\n    pub fn camel_case_headers(&self) -> bool {\n        self.head.camel_case_headers()\n    }\n\n    /// Sets whether to send headers formatted as Camel-Case.\n    #[inline]\n    pub fn set_camel_case_headers(mut self, val: bool) -> Self {\n        self.head.set_camel_case_headers(val);\n        self\n    }\n\n    /// Complete request construction and connect to a WebSocket server.\n    pub async fn connect(\n        mut self,\n    ) -> Result<(ClientResponse, Framed<BoxedSocket, Codec>), WsClientError> {\n        if let Some(err) = self.err.take() {\n            return Err(err.into());\n        }\n\n        // validate URI\n        let uri = &self.head.uri;\n\n        if uri.host().is_none() {\n            return Err(InvalidUrl::MissingHost.into());\n        } else if uri.scheme().is_none() {\n            return Err(InvalidUrl::MissingScheme.into());\n        } else if let Some(scheme) = uri.scheme() {\n            match scheme.as_str() {\n                \"http\" | \"ws\" | \"https\" | \"wss\" => {}\n                _ => return Err(InvalidUrl::UnknownScheme.into()),\n            }\n        } else {\n            return Err(InvalidUrl::UnknownScheme.into());\n        }\n\n        if !self.head.headers.contains_key(header::HOST) {\n            let hostname = uri.host().unwrap();\n            let port = uri.port();\n\n            self.head.headers.insert(\n                header::HOST,\n                HeaderValue::from_str(&Host { hostname, port }.to_string()).unwrap(),\n            );\n        }\n\n        // set cookies\n        #[cfg(feature = \"cookies\")]\n        if let Some(ref mut jar) = self.cookies {\n            let cookie: String = jar\n                .delta()\n                // ensure only name=value is written to cookie header\n                .map(|c| c.stripped().encoded().to_string())\n                .collect::<Vec<_>>()\n                .join(\"; \");\n\n            if !cookie.is_empty() {\n                self.head\n                    .headers\n                    .insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());\n            }\n        }\n\n        // origin\n        if let Some(origin) = self.origin.take() {\n            self.head.headers.insert(header::ORIGIN, origin);\n        }\n\n        self.head.set_connection_type(ConnectionType::Upgrade);\n\n        #[allow(clippy::declare_interior_mutable_const)]\n        const HV_WEBSOCKET: HeaderValue = HeaderValue::from_static(\"websocket\");\n        self.head.headers.insert(header::UPGRADE, HV_WEBSOCKET);\n\n        #[allow(clippy::declare_interior_mutable_const)]\n        const HV_THIRTEEN: HeaderValue = HeaderValue::from_static(\"13\");\n        self.head\n            .headers\n            .insert(header::SEC_WEBSOCKET_VERSION, HV_THIRTEEN);\n\n        if let Some(protocols) = self.protocols.take() {\n            self.head.headers.insert(\n                header::SEC_WEBSOCKET_PROTOCOL,\n                HeaderValue::try_from(protocols.as_str()).unwrap(),\n            );\n        }\n\n        // Generate a random key for the `Sec-WebSocket-Key` header which is a base64-encoded\n        // (see RFC 4648 §4) value that, when decoded, is 16 bytes in length (RFC 6455 §1.3).\n        let sec_key = rand::random::<[u8; 16]>();\n        let key = BASE64_STANDARD.encode(sec_key);\n\n        self.head.headers.insert(\n            header::SEC_WEBSOCKET_KEY,\n            HeaderValue::try_from(key.as_str()).unwrap(),\n        );\n\n        let head = self.head;\n        let max_size = self.max_size;\n        let server_mode = self.server_mode;\n\n        let req = ConnectRequest::Tunnel(head, self.addr);\n\n        let fut = self.config.connector.call(req);\n\n        // set request timeout\n        let res = if let Some(to) = self.config.timeout {\n            timeout(to, fut)\n                .await\n                .map_err(|_| SendRequestError::Timeout)??\n        } else {\n            fut.await?\n        };\n\n        let (head, framed) = res.into_tunnel_response();\n\n        // verify response\n        if head.status != StatusCode::SWITCHING_PROTOCOLS {\n            return Err(WsClientError::InvalidResponseStatus(head.status));\n        }\n\n        // check for \"UPGRADE\" to WebSocket header\n        let has_hdr = if let Some(hdr) = head.headers.get(&header::UPGRADE) {\n            if let Ok(s) = hdr.to_str() {\n                s.to_ascii_lowercase().contains(\"websocket\")\n            } else {\n                false\n            }\n        } else {\n            false\n        };\n        if !has_hdr {\n            log::trace!(\"Invalid upgrade header\");\n            return Err(WsClientError::InvalidUpgradeHeader);\n        }\n\n        // Check for \"CONNECTION\" header\n        if let Some(conn) = head.headers.get(&header::CONNECTION) {\n            if let Ok(s) = conn.to_str() {\n                if !s.to_ascii_lowercase().contains(\"upgrade\") {\n                    log::trace!(\"Invalid connection header: {}\", s);\n                    return Err(WsClientError::InvalidConnectionHeader(conn.clone()));\n                }\n            } else {\n                log::trace!(\"Invalid connection header: {:?}\", conn);\n                return Err(WsClientError::InvalidConnectionHeader(conn.clone()));\n            }\n        } else {\n            log::trace!(\"Missing connection header\");\n            return Err(WsClientError::MissingConnectionHeader);\n        }\n\n        if let Some(hdr_key) = head.headers.get(&header::SEC_WEBSOCKET_ACCEPT) {\n            let encoded = ws::hash_key(key.as_ref());\n\n            if hdr_key.as_bytes() != encoded {\n                log::trace!(\n                    \"Invalid challenge response: expected: {:?} received: {:?}\",\n                    &encoded,\n                    key\n                );\n\n                return Err(WsClientError::InvalidChallengeResponse(\n                    encoded,\n                    hdr_key.clone(),\n                ));\n            }\n        } else {\n            log::trace!(\"Missing SEC-WEBSOCKET-ACCEPT header\");\n            return Err(WsClientError::MissingWebSocketAcceptHeader);\n        };\n\n        // response and ws framed\n        Ok((\n            ClientResponse::new(head, Payload::None),\n            framed.into_map_codec(|_| {\n                if server_mode {\n                    ws::Codec::new().max_size(max_size)\n                } else {\n                    ws::Codec::new().max_size(max_size).client_mode()\n                }\n            }),\n        ))\n    }\n}\n\nimpl fmt::Debug for WebsocketsRequest {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(\n            f,\n            \"\\nWebsocketsRequest {}:{}\",\n            self.head.method, self.head.uri\n        )?;\n        writeln!(f, \"  headers:\")?;\n        for (key, val) in self.head.headers.iter() {\n            writeln!(f, \"    {:?}: {:?}\", key, val)?;\n        }\n        Ok(())\n    }\n}\n\n/// Formatter for host (hostname+port) header values.\nstruct Host<'a> {\n    hostname: &'a str,\n    port: Option<http::uri::Port<&'a str>>,\n}\n\nimpl fmt::Display for Host<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(self.hostname)?;\n\n        if let Some(port) = &self.port {\n            f.write_str(\":\")?;\n            f.write_str(port.as_str())?;\n        }\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::Client;\n\n    #[actix_rt::test]\n    async fn test_debug() {\n        let request = Client::new().ws(\"/\").header(\"x-test\", \"111\");\n        let repr = format!(\"{:?}\", request);\n        assert!(repr.contains(\"WebsocketsRequest\"));\n        assert!(repr.contains(\"x-test\"));\n    }\n\n    #[actix_rt::test]\n    async fn test_header_override() {\n        let req = Client::builder()\n            .add_default_header((header::CONTENT_TYPE, \"111\"))\n            .finish()\n            .ws(\"/\")\n            .set_header(header::CONTENT_TYPE, \"222\");\n\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::CONTENT_TYPE)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"222\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn basic_auth() {\n        let req = Client::new()\n            .ws(\"/\")\n            .basic_auth(\"username\", Some(\"password\"));\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\"\n        );\n\n        let req = Client::new().ws(\"/\").basic_auth(\"username\", None);\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Basic dXNlcm5hbWU6\"\n        );\n    }\n\n    #[actix_rt::test]\n    async fn bearer_auth() {\n        let req = Client::new().ws(\"/\").bearer_auth(\"someS3cr3tAutht0k3n\");\n        assert_eq!(\n            req.head\n                .headers\n                .get(header::AUTHORIZATION)\n                .unwrap()\n                .to_str()\n                .unwrap(),\n            \"Bearer someS3cr3tAutht0k3n\"\n        );\n\n        #[allow(clippy::let_underscore_future)]\n        let _ = req.connect();\n    }\n\n    #[actix_rt::test]\n    async fn camel_case_headers() {\n        let req = Client::new().ws(\"/\").set_camel_case_headers(true);\n        assert!(req.camel_case_headers());\n    }\n\n    #[actix_rt::test]\n    async fn basics() {\n        let req = Client::new()\n            .ws(\"http://localhost/\")\n            .origin(\"test-origin\")\n            .max_frame_size(100)\n            .server_mode()\n            .protocols([\"v1\", \"v2\"])\n            .set_header_if_none(header::CONTENT_TYPE, \"json\")\n            .set_header_if_none(header::CONTENT_TYPE, \"text\")\n            .cookie(Cookie::build(\"cookie1\", \"value1\").finish());\n        assert_eq!(\n            req.origin.as_ref().unwrap().to_str().unwrap(),\n            \"test-origin\"\n        );\n        assert_eq!(req.max_size, 100);\n        assert!(req.server_mode);\n        assert_eq!(req.protocols, Some(\"v1,v2\".to_string()));\n        assert_eq!(\n            req.head.headers.get(header::CONTENT_TYPE).unwrap(),\n            header::HeaderValue::from_static(\"json\")\n        );\n\n        let _ = req.connect().await;\n\n        assert!(Client::new().ws(\"/\").connect().await.is_err());\n        assert!(Client::new().ws(\"http:///test\").connect().await.is_err());\n        assert!(Client::new().ws(\"hmm://test.com/\").connect().await.is_err());\n    }\n}\n"
  },
  {
    "path": "awc/tests/test_client.rs",
    "content": "use std::{\n    collections::HashMap,\n    convert::Infallible,\n    io::{Read, Write},\n    net::{IpAddr, Ipv4Addr},\n    sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    },\n    time::Duration,\n};\n\nuse actix_http::{HttpService, StatusCode};\nuse actix_http_test::test_server;\nuse actix_service::{fn_service, map_config, ServiceFactoryExt as _};\nuse actix_utils::future::ok;\nuse actix_web::{dev::AppConfig, http::header, web, App, Error, HttpRequest, HttpResponse};\nuse awc::error::{JsonPayloadError, PayloadError, SendRequestError};\nuse base64::prelude::*;\nuse bytes::Bytes;\nuse cookie::Cookie;\nuse futures_util::stream;\nuse rand::distr::{Alphanumeric, SampleString as _};\n\nmod utils;\n\nconst S: &str = \"Hello World \";\nconst STR: &str = const_str::repeat!(S, 100);\n\n#[actix_rt::test]\nasync fn simple() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(STR) })))\n    });\n\n    let request = srv.get(\"/\").insert_header((\"x-test\", \"111\")).send();\n    let mut response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    let mut response = srv.post(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    // camel case\n    let response = srv.post(\"/\").camel_case().send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn json() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .service(web::resource(\"/\").route(web::to(|_: web::Json<String>| HttpResponse::Ok())))\n    });\n\n    let request = srv\n        .get(\"/\")\n        .insert_header((\"x-test\", \"111\"))\n        .send_json(&\"TEST\".to_string());\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn form() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(\n            |_: web::Form<HashMap<String, String>>| HttpResponse::Ok(),\n        )))\n    });\n\n    let mut data = HashMap::new();\n    let _ = data.insert(\"key\".to_string(), \"TEST\".to_string());\n\n    let request = srv\n        .get(\"/\")\n        .append_header((\"x-test\", \"111\"))\n        .send_form(&data);\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn timeout() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|| async {\n            actix_rt::time::sleep(Duration::from_millis(200)).await;\n            HttpResponse::Ok().body(STR)\n        })))\n    });\n\n    let connector = awc::Connector::new()\n        .connector(actix_tls::connect::ConnectorService::default())\n        .timeout(Duration::from_secs(15));\n\n    let client = awc::Client::builder()\n        .connector(connector)\n        .timeout(Duration::from_millis(50))\n        .finish();\n\n    let request = client.get(srv.url(\"/\")).send();\n    match request.await {\n        Err(SendRequestError::Timeout) => {}\n        _ => panic!(),\n    }\n}\n\n#[actix_rt::test]\nasync fn timeout_override() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|| async {\n            actix_rt::time::sleep(Duration::from_millis(200)).await;\n            HttpResponse::Ok().body(STR)\n        })))\n    });\n\n    let client = awc::Client::builder()\n        .timeout(Duration::from_millis(50000))\n        .finish();\n    let request = client\n        .get(srv.url(\"/\"))\n        .timeout(Duration::from_millis(50))\n        .send();\n    match request.await {\n        Err(SendRequestError::Timeout) => {}\n        _ => panic!(),\n    }\n}\n\n#[actix_rt::test]\nasync fn response_timeout() {\n    use futures_util::{stream::once, StreamExt as _};\n\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|| async {\n            Ok::<_, Error>(\n                HttpResponse::Ok()\n                    .content_type(\"application/json\")\n                    .streaming(Box::pin(once(async {\n                        actix_rt::time::sleep(Duration::from_millis(200)).await;\n                        Ok::<_, Error>(Bytes::from(STR))\n                    }))),\n            )\n        })))\n    });\n\n    let client = awc::Client::new();\n\n    let res = client\n        .get(srv.url(\"/\"))\n        .send()\n        .await\n        .unwrap()\n        .timeout(Duration::from_millis(500))\n        .body()\n        .await\n        .unwrap();\n    assert_eq!(std::str::from_utf8(res.as_ref()).unwrap(), STR);\n\n    let res = client\n        .get(srv.url(\"/\"))\n        .send()\n        .await\n        .unwrap()\n        .timeout(Duration::from_millis(100))\n        .next()\n        .await\n        .unwrap();\n    match res {\n        Err(PayloadError::Io(e)) => assert_eq!(e.kind(), std::io::ErrorKind::TimedOut),\n        _ => panic!(\"Response error type is not matched\"),\n    }\n\n    let res = client\n        .get(srv.url(\"/\"))\n        .send()\n        .await\n        .unwrap()\n        .timeout(Duration::from_millis(100))\n        .body()\n        .await;\n    match res {\n        Err(PayloadError::Io(e)) => assert_eq!(e.kind(), std::io::ErrorKind::TimedOut),\n        _ => panic!(\"Response error type is not matched\"),\n    }\n\n    let res = client\n        .get(srv.url(\"/\"))\n        .send()\n        .await\n        .unwrap()\n        .timeout(Duration::from_millis(100))\n        .json::<HashMap<String, String>>()\n        .await;\n    match res {\n        Err(JsonPayloadError::Payload(PayloadError::Io(e))) => {\n            assert_eq!(e.kind(), std::io::ErrorKind::TimedOut)\n        }\n        _ => panic!(\"Response error type is not matched\"),\n    }\n}\n\n#[actix_rt::test]\nasync fn connection_reuse() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::new(map_config(\n                App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n                |_| AppConfig::default(),\n            ))\n            .tcp(),\n        )\n    })\n    .await;\n\n    let client = awc::Client::default();\n\n    // req 1\n    let request = client.get(srv.url(\"/\")).send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req = client.post(srv.url(\"/\"));\n    let response = req.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // one connection\n    assert_eq!(num.load(Ordering::Relaxed), 1);\n}\n\n#[actix_rt::test]\nasync fn connection_force_close() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::new(map_config(\n                App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n                |_| AppConfig::default(),\n            ))\n            .tcp(),\n        )\n    })\n    .await;\n\n    let client = awc::Client::default();\n\n    // req 1\n    let request = client.get(srv.url(\"/\")).force_close().send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req = client.post(srv.url(\"/\")).force_close();\n    let response = req.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // two connection\n    assert_eq!(num.load(Ordering::Relaxed), 2);\n}\n\n#[actix_rt::test]\nasync fn connection_server_close() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::new(map_config(\n                App::new().service(web::resource(\"/\").route(web::to(|| async {\n                    HttpResponse::Ok().force_close().finish()\n                }))),\n                |_| AppConfig::default(),\n            ))\n            .tcp(),\n        )\n    })\n    .await;\n\n    let client = awc::Client::default();\n\n    // req 1\n    let request = client.get(srv.url(\"/\")).send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req = client.post(srv.url(\"/\"));\n    let response = req.send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // two connection\n    assert_eq!(num.load(Ordering::Relaxed), 2);\n}\n\n#[actix_rt::test]\nasync fn connection_wait_queue() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::new(map_config(\n                App::new().service(\n                    web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(STR) })),\n                ),\n                |_| AppConfig::default(),\n            ))\n            .tcp(),\n        )\n    })\n    .await;\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().limit(1))\n        .finish();\n\n    // req 1\n    let request = client.get(srv.url(\"/\")).send();\n    let mut response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req2 = client.post(srv.url(\"/\"));\n    let req2_fut = req2.send();\n\n    // read response 1\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    // req 2\n    let response = req2_fut.await.unwrap();\n    assert!(response.status().is_success());\n\n    // two connection\n    assert_eq!(num.load(Ordering::Relaxed), 1);\n}\n\n#[actix_rt::test]\nasync fn connection_wait_queue_force_close() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::new(map_config(\n                App::new().service(web::resource(\"/\").route(web::to(|| async {\n                    HttpResponse::Ok().force_close().body(STR)\n                }))),\n                |_| AppConfig::default(),\n            ))\n            .tcp(),\n        )\n    })\n    .await;\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().limit(1))\n        .finish();\n\n    // req 1\n    let request = client.get(srv.url(\"/\")).send();\n    let mut response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req2 = client.post(srv.url(\"/\"));\n    let req2_fut = req2.send();\n\n    // read response 1\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n\n    // req 2\n    let response = req2_fut.await.unwrap();\n    assert!(response.status().is_success());\n\n    // two connection\n    assert_eq!(num.load(Ordering::Relaxed), 2);\n}\n\n#[actix_rt::test]\nasync fn with_query_parameter() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").to(|req: HttpRequest| {\n            if req.query_string().contains(\"qp\") {\n                HttpResponse::Ok()\n            } else {\n                HttpResponse::BadRequest()\n            }\n        }))\n    });\n\n    let res = awc::Client::new()\n        .get(srv.url(\"/?qp=5\"))\n        .send()\n        .await\n        .unwrap();\n    assert!(res.status().is_success());\n}\n\n#[cfg(feature = \"compress-gzip\")]\n#[actix_rt::test]\nasync fn no_decompress() {\n    let srv = actix_test::start(|| {\n        App::new()\n            .wrap(actix_web::middleware::Compress::default())\n            .service(web::resource(\"/\").route(web::to(|| async { HttpResponse::Ok().body(STR) })))\n    });\n\n    let mut res = awc::Client::new()\n        .get(srv.url(\"/\"))\n        .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert!(res.status().is_success());\n\n    // read response\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), STR.as_bytes());\n\n    // POST\n    let mut res = awc::Client::new()\n        .post(srv.url(\"/\"))\n        .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .no_decompress()\n        .send()\n        .await\n        .unwrap();\n    assert!(res.status().is_success());\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(utils::gzip::decode(bytes), STR.as_bytes());\n}\n\n#[cfg(feature = \"compress-gzip\")]\n#[actix_rt::test]\nasync fn client_gzip_encoding() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|| async {\n            HttpResponse::Ok()\n                .insert_header(header::ContentEncoding::Gzip)\n                .body(utils::gzip::encode(STR))\n        })))\n    });\n\n    // client request\n    let mut response = srv.post(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, STR);\n}\n\n#[cfg(feature = \"compress-gzip\")]\n#[actix_rt::test]\nasync fn client_gzip_encoding_large() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|| async {\n            HttpResponse::Ok()\n                .insert_header(header::ContentEncoding::Gzip)\n                .body(utils::gzip::encode(STR.repeat(10)))\n        })))\n    });\n\n    // client request\n    let mut response = srv.post(\"/\").send().await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, STR.repeat(10));\n}\n\n#[cfg(feature = \"compress-gzip\")]\n#[actix_rt::test]\nasync fn client_gzip_encoding_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 100_000);\n\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|data: Bytes| async {\n            HttpResponse::Ok()\n                .insert_header(header::ContentEncoding::Gzip)\n                .body(utils::gzip::encode(data))\n        })))\n    });\n\n    // client request\n    let mut response = srv.post(\"/\").send_body(data.clone()).await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, data);\n}\n\n#[cfg(feature = \"compress-brotli\")]\n#[actix_rt::test]\nasync fn client_brotli_encoding() {\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|data: Bytes| async {\n            HttpResponse::Ok()\n                .insert_header((\"content-encoding\", \"br\"))\n                .body(utils::brotli::encode(data))\n        })))\n    });\n\n    // client request\n    let mut response = srv.post(\"/\").send_body(STR).await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[cfg(feature = \"compress-brotli\")]\n#[actix_rt::test]\nasync fn client_brotli_encoding_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 70_000);\n\n    let srv = actix_test::start(|| {\n        App::new().service(web::resource(\"/\").route(web::to(|data: Bytes| async {\n            HttpResponse::Ok()\n                .insert_header(header::ContentEncoding::Brotli)\n                .body(utils::brotli::encode(data))\n        })))\n    });\n\n    // client request\n    let mut response = srv.post(\"/\").send_body(data.clone()).await.unwrap();\n    assert!(response.status().is_success());\n\n    // read response\n    let bytes = response.body().await.unwrap();\n    assert_eq!(bytes, data);\n}\n\n#[actix_rt::test]\nasync fn client_deflate_encoding() {\n    let srv = actix_test::start(|| {\n        App::new().default_service(web::to(|body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        }))\n    });\n\n    let req = srv\n        .post(\"/\")\n        .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .send_body(STR);\n\n    let mut res = req.await.unwrap();\n    assert_eq!(res.status(), StatusCode::OK);\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, STR);\n}\n\n#[actix_rt::test]\nasync fn client_deflate_encoding_large_random() {\n    let data = Alphanumeric.sample_string(&mut rand::rng(), 70_000);\n\n    let srv = actix_test::start(|| {\n        App::new().default_service(web::to(|body: Bytes| async {\n            HttpResponse::Ok().body(body)\n        }))\n    });\n\n    let req = srv\n        .post(\"/\")\n        .insert_header((header::ACCEPT_ENCODING, \"br\"))\n        .send_body(data.clone());\n\n    let mut res = req.await.unwrap();\n    let bytes = res.body().await.unwrap();\n\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_eq!(bytes, Bytes::from(data));\n}\n\n#[actix_rt::test]\nasync fn client_streaming_explicit() {\n    let srv = actix_test::start(|| {\n        App::new().default_service(web::to(|body: web::Payload| async {\n            HttpResponse::Ok().streaming(body)\n        }))\n    });\n\n    let body =\n        stream::once(async { Ok::<_, actix_http::Error>(Bytes::from_static(STR.as_bytes())) });\n    let req = srv\n        .post(\"/\")\n        .insert_header((header::ACCEPT_ENCODING, \"identity\"))\n        .send_stream(Box::pin(body));\n\n    let mut res = req.await.unwrap();\n    assert!(res.status().is_success());\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn body_streaming_implicit() {\n    let srv = actix_test::start(|| {\n        App::new().default_service(web::to(|| async {\n            let body =\n                stream::once(async { Ok::<_, Infallible>(Bytes::from_static(STR.as_bytes())) });\n            HttpResponse::Ok().streaming(body)\n        }))\n    });\n\n    let req = srv\n        .get(\"/\")\n        .insert_header((header::ACCEPT_ENCODING, \"gzip\"))\n        .send();\n\n    let mut res = req.await.unwrap();\n    assert!(res.status().is_success());\n\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));\n}\n\n#[actix_rt::test]\nasync fn client_cookie_handling() {\n    use std::io::{Error as IoError, ErrorKind};\n\n    let cookie1 = Cookie::build(\"cookie1\", \"value1\").finish();\n    let cookie2 = Cookie::build(\"cookie2\", \"value2\")\n        .domain(\"www.example.org\")\n        .path(\"/\")\n        .secure(true)\n        .http_only(true)\n        .finish();\n    // Q: are all these clones really necessary? A: Yes, possibly\n    let cookie1b = cookie1.clone();\n    let cookie2b = cookie2.clone();\n\n    let srv = actix_test::start(move || {\n        let cookie1 = cookie1b.clone();\n        let cookie2 = cookie2b.clone();\n\n        App::new().route(\n            \"/\",\n            web::to(move |req: HttpRequest| {\n                let cookie1 = cookie1.clone();\n                let cookie2 = cookie2.clone();\n\n                async move {\n                    // Check cookies were sent correctly\n                    req.cookie(\"cookie1\")\n                        .ok_or(())\n                        .and_then(|c1| {\n                            if c1.value() == \"value1\" {\n                                Ok(())\n                            } else {\n                                Err(())\n                            }\n                        })\n                        .and_then(|()| req.cookie(\"cookie2\").ok_or(()))\n                        .and_then(|c2| {\n                            if c2.value() == \"value2\" {\n                                Ok(())\n                            } else {\n                                Err(())\n                            }\n                        })\n                        .map_err(|_| Error::from(IoError::from(ErrorKind::NotFound)))?;\n\n                    // Send some cookies back\n                    Ok::<_, Error>(HttpResponse::Ok().cookie(cookie1).cookie(cookie2).finish())\n                }\n            }),\n        )\n    });\n\n    let request = srv.get(\"/\").cookie(cookie1.clone()).cookie(cookie2.clone());\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n    let c1 = response.cookie(\"cookie1\").expect(\"Missing cookie1\");\n    assert_eq!(c1, cookie1);\n    let c2 = response.cookie(\"cookie2\").expect(\"Missing cookie2\");\n    assert_eq!(c2, cookie2);\n}\n\n#[actix_rt::test]\nasync fn client_unread_response() {\n    let addr = actix_test::unused_addr();\n    let lst = std::net::TcpListener::bind(addr).unwrap();\n\n    std::thread::spawn(move || {\n        let (mut stream, _) = lst.accept().unwrap();\n        let mut b = [0; 1000];\n        let _ = stream.read(&mut b).unwrap();\n        let _ = stream.write_all(\n            b\"HTTP/1.1 200 OK\\r\\n\\\n                connection: close\\r\\n\\\n                \\r\\n\\\n                welcome!\",\n        );\n    });\n\n    // client request\n    let req = awc::Client::new().get(format!(\"http://{}/\", addr).as_str());\n    let mut res = req.send().await.unwrap();\n    assert!(res.status().is_success());\n\n    // awc does not read all bytes unless content-length is specified\n    let bytes = res.body().await.unwrap();\n    assert_eq!(bytes, Bytes::from_static(b\"\"));\n}\n\n#[actix_rt::test]\nasync fn client_basic_auth() {\n    let srv = actix_test::start(|| {\n        App::new().route(\n            \"/\",\n            web::to(|req: HttpRequest| {\n                if req\n                    .headers()\n                    .get(header::AUTHORIZATION)\n                    .unwrap()\n                    .to_str()\n                    .unwrap()\n                    == format!(\"Basic {}\", BASE64_STANDARD.encode(\"username:password\"))\n                {\n                    HttpResponse::Ok()\n                } else {\n                    HttpResponse::BadRequest()\n                }\n            }),\n        )\n    });\n\n    // set authorization header to Basic <base64 encoded username:password>\n    let request = srv.get(\"/\").basic_auth(\"username\", \"password\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn client_bearer_auth() {\n    let srv = actix_test::start(|| {\n        App::new().route(\n            \"/\",\n            web::to(|req: HttpRequest| {\n                if req\n                    .headers()\n                    .get(header::AUTHORIZATION)\n                    .unwrap()\n                    .to_str()\n                    .unwrap()\n                    == \"Bearer someS3cr3tAutht0k3n\"\n                {\n                    HttpResponse::Ok()\n                } else {\n                    HttpResponse::BadRequest()\n                }\n            }),\n        )\n    });\n\n    // set authorization header to Bearer <token>\n    let request = srv.get(\"/\").bearer_auth(\"someS3cr3tAutht0k3n\");\n    let response = request.send().await.unwrap();\n    assert!(response.status().is_success());\n}\n\n#[actix_rt::test]\nasync fn local_address() {\n    let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));\n\n    let srv = actix_test::start(move || {\n        App::new().service(\n            web::resource(\"/\").route(web::to(move |req: HttpRequest| async move {\n                assert_eq!(req.peer_addr().unwrap().ip(), ip);\n                Ok::<_, Error>(HttpResponse::Ok())\n            })),\n        )\n    });\n    let client = awc::Client::builder().local_address(ip).finish();\n\n    let res = client.get(srv.url(\"/\")).send().await.unwrap();\n\n    assert_eq!(res.status(), 200);\n\n    let client = awc::Client::builder()\n        .connector(\n            // connector local address setting should always be override by client builder.\n            awc::Connector::new().local_address(IpAddr::V4(Ipv4Addr::new(128, 0, 0, 1))),\n        )\n        .local_address(ip)\n        .finish();\n\n    let res = client.get(srv.url(\"/\")).send().await.unwrap();\n\n    assert_eq!(res.status(), 200);\n}\n"
  },
  {
    "path": "awc/tests/test_connector.rs",
    "content": "#![cfg(feature = \"openssl\")]\n\nextern crate tls_openssl as openssl;\n\nuse actix_http::HttpService;\nuse actix_http_test::test_server;\nuse actix_service::{map_config, ServiceFactoryExt};\nuse actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};\nuse openssl::{\n    pkey::PKey,\n    ssl::{SslAcceptor, SslConnector, SslMethod, SslVerifyMode},\n    x509::X509,\n};\n\nfn tls_config() -> SslAcceptor {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_file = cert.pem();\n    let key_file = key_pair.serialize_pem();\n\n    let cert = X509::from_pem(cert_file.as_bytes()).unwrap();\n    let key = PKey::private_key_from_pem(key_file.as_bytes()).unwrap();\n\n    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();\n    builder.set_certificate(&cert).unwrap();\n    builder.set_private_key(&key).unwrap();\n\n    builder.set_alpn_select_callback(|_, protos| {\n        const H2: &[u8] = b\"\\x02h2\";\n        if protos.windows(3).any(|window| window == H2) {\n            Ok(b\"h2\")\n        } else {\n            Err(openssl::ssl::AlpnError::NOACK)\n        }\n    });\n    builder.set_alpn_protos(b\"\\x02h2\").unwrap();\n\n    builder.build()\n}\n\n#[actix_rt::test]\nasync fn test_connection_window_size() {\n    let srv = test_server(|| {\n        HttpService::build()\n            .h2(map_config(\n                App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n                |_| AppConfig::default(),\n            ))\n            .openssl(tls_config())\n            .map_err(|_| ())\n    })\n    .await;\n\n    // disable ssl verification\n    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n    builder.set_verify(SslVerifyMode::NONE);\n    let _ = builder\n        .set_alpn_protos(b\"\\x02h2\\x08http/1.1\")\n        .map_err(|e| log::error!(\"Can not set alpn protocol: {:?}\", e));\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().openssl(builder.build()))\n        .initial_window_size(100)\n        .initial_connection_window_size(100)\n        .finish();\n\n    let request = client.get(srv.surl(\"/\")).send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.version(), Version::HTTP_2);\n}\n"
  },
  {
    "path": "awc/tests/test_empty_stream.rs",
    "content": "use std::{convert::Infallible, time::Duration};\n\nuse actix_rt::net::TcpListener;\nuse awc::Client;\nuse bytes::Bytes;\nuse futures_util::stream;\nuse tokio::{\n    io::{AsyncReadExt as _, AsyncWriteExt as _},\n    time::timeout,\n};\n\n#[actix_rt::test]\nasync fn empty_body_stream_does_not_use_chunked_encoding() {\n    let listener = TcpListener::bind((\"127.0.0.1\", 0)).await.unwrap();\n    let addr = listener.local_addr().unwrap();\n\n    // Minimal HTTP/1.1 server that rejects chunked requests.\n    let srv = actix_rt::spawn(async move {\n        let (mut sock, _) = listener.accept().await.unwrap();\n\n        let mut buf = Vec::with_capacity(1024);\n        let mut tmp = [0u8; 1024];\n\n        let header_end = loop {\n            let n = timeout(Duration::from_secs(2), sock.read(&mut tmp))\n                .await\n                .unwrap()\n                .unwrap();\n            if n == 0 {\n                break None;\n            }\n\n            buf.extend_from_slice(&tmp[..n]);\n\n            if let Some(pos) = buf.windows(4).position(|w| w == b\"\\r\\n\\r\\n\") {\n                break Some(pos + 4);\n            }\n\n            if buf.len() > 16 * 1024 {\n                break None;\n            }\n        }\n        .expect(\"did not receive complete request headers\");\n\n        let headers_lower = String::from_utf8_lossy(&buf[..header_end]).to_ascii_lowercase();\n        let has_chunked = headers_lower.contains(\"\\r\\ntransfer-encoding: chunked\\r\\n\");\n\n        if has_chunked {\n            // Drain terminating chunk so client doesn't error on write before response is read.\n            let terminator = b\"0\\r\\n\\r\\n\";\n            while !buf[header_end..]\n                .windows(terminator.len())\n                .any(|w| w == terminator)\n            {\n                let n = match timeout(Duration::from_secs(2), sock.read(&mut tmp)).await {\n                    Ok(Ok(n)) => n,\n                    _ => break,\n                };\n\n                if n == 0 {\n                    break;\n                }\n\n                buf.extend_from_slice(&tmp[..n]);\n\n                if buf.len() > 32 * 1024 {\n                    break;\n                }\n            }\n        }\n\n        let status = if has_chunked {\n            \"400 Bad Request\"\n        } else {\n            \"200 OK\"\n        };\n        let resp = format!(\"HTTP/1.1 {status}\\r\\nContent-Length: 0\\r\\nConnection: close\\r\\n\\r\\n\");\n        sock.write_all(resp.as_bytes()).await.unwrap();\n    });\n\n    let url = format!(\"http://{addr}/\");\n    let res = Client::default()\n        .get(url)\n        .send_stream(stream::empty::<Result<Bytes, Infallible>>())\n        .await\n        .unwrap();\n\n    assert!(res.status().is_success());\n\n    srv.await.unwrap();\n}\n"
  },
  {
    "path": "awc/tests/test_rustls_client.rs",
    "content": "#![cfg(feature = \"rustls-0_23-webpki-roots\")]\n\nextern crate tls_rustls_0_23 as rustls;\n\nuse std::sync::{\n    atomic::{AtomicUsize, Ordering},\n    Arc,\n};\n\nuse actix_http::HttpService;\nuse actix_http_test::test_server;\nuse actix_service::{fn_service, map_config, ServiceFactoryExt};\nuse actix_tls::connect::rustls_0_23::webpki_roots_cert_store;\nuse actix_utils::future::ok;\nuse actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};\nuse rustls::{pki_types::ServerName, ClientConfig, ServerConfig};\nuse rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};\n\nfn tls_config() -> ServerConfig {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_chain = vec![cert.der().clone()];\n    let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));\n\n    ServerConfig::builder()\n        .with_no_client_auth()\n        .with_single_cert(cert_chain, key_der)\n        .unwrap()\n}\n\nmod danger {\n    use rustls::{\n        client::danger::{ServerCertVerified, ServerCertVerifier},\n        pki_types::UnixTime,\n    };\n\n    use super::*;\n\n    #[derive(Debug)]\n    pub struct NoCertificateVerification;\n\n    impl ServerCertVerifier for NoCertificateVerification {\n        fn verify_server_cert(\n            &self,\n            _end_entity: &CertificateDer<'_>,\n            _intermediates: &[CertificateDer<'_>],\n            _server_name: &ServerName<'_>,\n            _ocsp_response: &[u8],\n            _now: UnixTime,\n        ) -> Result<ServerCertVerified, rustls::Error> {\n            Ok(rustls::client::danger::ServerCertVerified::assertion())\n        }\n\n        fn verify_tls12_signature(\n            &self,\n            _message: &[u8],\n            _cert: &CertificateDer<'_>,\n            _dss: &rustls::DigitallySignedStruct,\n        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {\n            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())\n        }\n\n        fn verify_tls13_signature(\n            &self,\n            _message: &[u8],\n            _cert: &CertificateDer<'_>,\n            _dss: &rustls::DigitallySignedStruct,\n        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {\n            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())\n        }\n\n        fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {\n            rustls::crypto::aws_lc_rs::default_provider()\n                .signature_verification_algorithms\n                .supported_schemes()\n        }\n    }\n}\n\n#[actix_rt::test]\nasync fn test_connection_reuse_h2() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::build()\n                .h2(map_config(\n                    App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n                    |_| AppConfig::default(),\n                ))\n                .rustls_0_23(tls_config())\n                .map_err(|_| ()),\n        )\n    })\n    .await;\n\n    let mut config = ClientConfig::builder()\n        .with_root_certificates(webpki_roots_cert_store())\n        .with_no_client_auth();\n\n    let protos = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n    config.alpn_protocols = protos;\n\n    // disable TLS verification\n    config\n        .dangerous()\n        .set_certificate_verifier(Arc::new(danger::NoCertificateVerification));\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().rustls_0_23(Arc::new(config)))\n        .finish();\n\n    // req 1\n    let request = client.get(srv.surl(\"/\")).send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req = client.post(srv.surl(\"/\"));\n    let response = req.send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.version(), Version::HTTP_2);\n\n    // one connection\n    assert_eq!(num.load(Ordering::Relaxed), 1);\n}\n"
  },
  {
    "path": "awc/tests/test_ssl_client.rs",
    "content": "#![cfg(feature = \"openssl\")]\n\nextern crate tls_openssl as openssl;\n\nuse std::sync::{\n    atomic::{AtomicUsize, Ordering},\n    Arc,\n};\n\nuse actix_http::HttpService;\nuse actix_http_test::test_server;\nuse actix_service::{fn_service, map_config, ServiceFactoryExt};\nuse actix_utils::future::ok;\nuse actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};\nuse openssl::{\n    pkey::PKey,\n    ssl::{SslAcceptor, SslConnector, SslMethod, SslVerifyMode},\n    x509::X509,\n};\n\nfn tls_config() -> SslAcceptor {\n    let rcgen::CertifiedKey { cert, key_pair } =\n        rcgen::generate_simple_self_signed([\"localhost\".to_owned()]).unwrap();\n    let cert_file = cert.pem();\n    let key_file = key_pair.serialize_pem();\n\n    let cert = X509::from_pem(cert_file.as_bytes()).unwrap();\n    let key = PKey::private_key_from_pem(key_file.as_bytes()).unwrap();\n\n    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();\n    builder.set_certificate(&cert).unwrap();\n    builder.set_private_key(&key).unwrap();\n\n    builder.set_alpn_select_callback(|_, protos| {\n        const H2: &[u8] = b\"\\x02h2\";\n        if protos.windows(3).any(|window| window == H2) {\n            Ok(b\"h2\")\n        } else {\n            Err(openssl::ssl::AlpnError::NOACK)\n        }\n    });\n    builder.set_alpn_protos(b\"\\x02h2\").unwrap();\n\n    builder.build()\n}\n\n#[actix_rt::test]\nasync fn test_connection_reuse_h2() {\n    let num = Arc::new(AtomicUsize::new(0));\n    let num2 = num.clone();\n\n    let srv = test_server(move || {\n        let num2 = num2.clone();\n        fn_service(move |io| {\n            num2.fetch_add(1, Ordering::Relaxed);\n            ok(io)\n        })\n        .and_then(\n            HttpService::build()\n                .h2(map_config(\n                    App::new().service(web::resource(\"/\").route(web::to(HttpResponse::Ok))),\n                    |_| AppConfig::default(),\n                ))\n                .openssl(tls_config())\n                .map_err(|_| ()),\n        )\n    })\n    .await;\n\n    // disable ssl verification\n    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();\n    builder.set_verify(SslVerifyMode::NONE);\n    let _ = builder\n        .set_alpn_protos(b\"\\x02h2\\x08http/1.1\")\n        .map_err(|e| log::error!(\"Can not set alpn protocol: {:?}\", e));\n\n    let client = awc::Client::builder()\n        .connector(awc::Connector::new().openssl(builder.build()))\n        .finish();\n\n    // req 1\n    let request = client.get(srv.surl(\"/\")).send();\n    let response = request.await.unwrap();\n    assert!(response.status().is_success());\n\n    // req 2\n    let req = client.post(srv.surl(\"/\"));\n    let response = req.send().await.unwrap();\n    assert!(response.status().is_success());\n    assert_eq!(response.version(), Version::HTTP_2);\n\n    // one connection\n    assert_eq!(num.load(Ordering::Relaxed), 1);\n}\n"
  },
  {
    "path": "awc/tests/test_ws.rs",
    "content": "use std::io;\n\nuse actix_codec::Framed;\nuse actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response};\nuse actix_http_test::test_server;\nuse actix_utils::future::ok;\nuse bytes::Bytes;\nuse futures_util::{SinkExt as _, StreamExt as _};\n\nasync fn ws_service(req: ws::Frame) -> Result<ws::Message, io::Error> {\n    match req {\n        ws::Frame::Ping(msg) => Ok(ws::Message::Pong(msg)),\n        ws::Frame::Text(text) => Ok(ws::Message::Text(\n            String::from_utf8(Vec::from(text.as_ref())).unwrap().into(),\n        )),\n        ws::Frame::Binary(bin) => Ok(ws::Message::Binary(bin)),\n        ws::Frame::Close(reason) => Ok(ws::Message::Close(reason)),\n        _ => Ok(ws::Message::Close(None)),\n    }\n}\n\n#[actix_rt::test]\nasync fn test_simple() {\n    let mut srv = test_server(|| {\n        HttpService::build()\n            .upgrade(|(req, mut framed): (Request, Framed<_, _>)| {\n                async move {\n                    let res = ws::handshake_response(req.head()).finish();\n                    // send handshake response\n                    framed\n                        .send(h1::Message::Item((res.drop_body(), BodySize::None)))\n                        .await?;\n\n                    // start WebSocket service\n                    let framed = framed.replace_codec(ws::Codec::new());\n                    ws::Dispatcher::with(framed, ws_service).await\n                }\n            })\n            .finish(|_| ok::<_, Error>(Response::not_found()))\n            .tcp()\n    })\n    .await;\n\n    // client service\n    let mut framed = srv.ws().await.unwrap();\n    framed.send(ws::Message::Text(\"text\".into())).await.unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Text(Bytes::from_static(b\"text\")));\n\n    framed\n        .send(ws::Message::Binary(\"text\".into()))\n        .await\n        .unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Binary(Bytes::from_static(b\"text\")));\n\n    framed.send(ws::Message::Ping(\"text\".into())).await.unwrap();\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Pong(\"text\".to_string().into()));\n\n    framed\n        .send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))\n        .await\n        .unwrap();\n\n    let item = framed.next().await.unwrap().unwrap();\n    assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into())));\n}\n"
  },
  {
    "path": "awc/tests/utils.rs",
    "content": "// compiling some tests will trigger unused function warnings even though other tests use them\n#![allow(dead_code)]\n\nuse std::io::{Read as _, Write as _};\n\npub mod gzip {\n    use flate2::{read::GzDecoder, write::GzEncoder, Compression};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = GzEncoder::new(Vec::new(), Compression::fast());\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = GzDecoder::new(bytes.as_ref());\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod deflate {\n    use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = ZlibEncoder::new(Vec::new(), Compression::fast());\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = ZlibDecoder::new(bytes.as_ref());\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod brotli {\n    use ::brotli::{reader::Decompressor as BrotliDecoder, CompressorWriter as BrotliEncoder};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = BrotliEncoder::new(\n            Vec::new(),\n            8 * 1024, // 32 KiB buffer\n            3,        // BROTLI_PARAM_QUALITY\n            22,       // BROTLI_PARAM_LGWIN\n        );\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.flush().unwrap();\n        encoder.into_inner()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = BrotliDecoder::new(bytes.as_ref(), 8_096);\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n\npub mod zstd {\n    use ::zstd::stream::{read::Decoder, write::Encoder};\n\n    use super::*;\n\n    pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut encoder = Encoder::new(Vec::new(), 3).unwrap();\n        encoder.write_all(bytes.as_ref()).unwrap();\n        encoder.finish().unwrap()\n    }\n\n    pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {\n        let mut decoder = Decoder::new(bytes.as_ref()).unwrap();\n        let mut buf = Vec::new();\n        decoder.read_to_end(&mut buf).unwrap();\n        buf\n    }\n}\n"
  },
  {
    "path": "deny.toml",
    "content": "[licenses]\nconfidence-threshold = 0.90\nallow = [\n    \"Apache-2.0\",\n    \"MIT\",\n    \"Unicode-3.0\",\n    \"ISC\",\n    \"CDLA-Permissive-2.0\",\n    \"BSD-3-Clause\",\n    \"Zlib\",\n    \"OpenSSL\",\n    \"MPL-2.0\"\n]\nprivate = { ignore = true }\n\n# FIXME: old rustls introduces old ring which is not set license field properly.\n[[licenses.clarify]]\ncrate = \"ring\"\nexpression = \"MIT AND ISC AND OpenSSL\"\nlicense-files = [\n    { path = \"LICENSE\", hash = 0xbd0eed23 }\n]\n\n# FIXME: webpki is almost unmaintained and is not set license field properly.\n# rustls has its own fork now so removing old rustls should resolve the issue.\n[[licenses.clarify]]\ncrate = \"webpki\"\nexpression = \"ISC\"\nlicense-files = [\n    { path = \"LICENSE\", hash = 0x001c7e6c }\n]\n\n[bans]\nmultiple-versions = \"allow\"\n\n[bans.build]\nexecutables = \"deny\"\n\n[advisories]\n# because of old rustls support:\nignore = [\n    \"RUSTSEC-2024-0336\",\n    \"RUSTSEC-2025-0009\",\n    \"RUSTSEC-2025-0010\"\n]\n"
  },
  {
    "path": "docs/graphs/.gitignore",
    "content": "# do not track rendered graphs\n*.png\n"
  },
  {
    "path": "docs/graphs/README.md",
    "content": "# Actix Ecosystem Dependency Graphs\n\nSee rendered versions of these dot graphs [on the wiki](https://github.com/actix/actix-web/wiki/Dependency-Graph).\n\n## Rendering\n\nDot graphs were rendered using the `dot` command from [GraphViz](https://www.graphviz.org/doc/info/command.html):\n\n```sh\nfor f in $(ls docs/graphs/*.dot | xargs); do dot $f -Tpng -o${f:r}.png; done\n```\n"
  },
  {
    "path": "docs/graphs/net-only.dot",
    "content": "digraph {\n    rankdir=TB\n\n    subgraph cluster_net {\n        label=\"actix-net\"\n        \"actix-codec\" \"actix-macros\" \"actix-rt\" \"actix-server\" \"actix-service\"\n        \"actix-tls\" \"actix-tracing\" \"actix-utils\"\n    }\n    \n    subgraph cluster_other {\n        label=\"other actix owned crates\"\n        { rank=same; \"local-channel\" \"local-waker\" \"bytestring\" }\n    }\n\n    subgraph cluster_tokio {\n        label=\"tokio\"\n        \"tokio\" \"tokio-util\"\n    }\n\n    \"actix-codec\" -> { \"tokio\" }\n    \"actix-codec\" -> { \"tokio-util\" }[color=red]\n    \"actix-utils\" -> { \"local-waker\" }\n    \"actix-tracing\" -> { \"actix-service\" }\n    \"actix-tls\" -> { \"actix-service\" \"actix-codec\" \"actix-utils\" \"actix-rt\" }\n    \"actix-tls\" -> { \"tokio-util\" }[color=\"#009900\"]\n    \"actix-server\" -> { \"actix-service\" \"actix-rt\" \"actix-utils\" \"tokio\" }\n    \"actix-rt\" -> { \"actix-macros\" \"tokio\" }\n\n    \"local-channel\" -> { \"local-waker\" }\n\n    // invisible edges to force nicer layout\n    edge [style=invis]\n    \"actix-macros\" -> \"tokio\"\n    \"actix-service\" -> \"bytestring\"\n    \"actix-macros\" -> \"bytestring\"\n}\n"
  },
  {
    "path": "docs/graphs/web-focus.dot",
    "content": "digraph {\n    subgraph cluster_web {\n        label=\"actix/web\"\n\n        \"awc\"\n        \"web\"\n        \"files\"\n        \"http\"\n        \"multipart\"\n        \"web-actors\"\n        \"web-codegen\"\n        \"http-test\"\n        \"router\"\n\n        { rank=same; \"multipart\" \"web-actors\" \"http-test\" };\n        { rank=same; \"files\" \"awc\" \"web\" };\n        { rank=same; \"web-codegen\" \"http\" };\n    }\n\n    \"web\" -> { \"codec\" \"service\" \"utils\" \"router\" \"rt\" \"server\" \"macros\" \"web-codegen\" \"http\" \"awc\" }\n    \"web\" -> { \"tls\" }[color=blue] // optional\n    \"web-codegen\" -> { \"router\" }\n    \"awc\" -> { \"codec\" \"service\" \"http\" \"rt\" }\n    \"web-actors\" -> { \"actix\" \"web\" \"http\" \"codec\" }\n    \"multipart\" -> { \"web\" \"service\" \"utils\" }\n    \"http\" -> { \"service\" \"codec\" \"utils\" \"rt\" }\n    \"http\" -> { \"tls\" }[color=blue] // optional\n    \"files\" -> { \"web\" }\n    \"http-test\" -> { \"service\" \"codec\" \"utils\" \"rt\" \"server\" \"awc\" }\n    \"http-test\" -> { \"tls\" }[color=blue] // optional\n\n    // net\n\n    \"utils\" -> { \"service\" \"rt\" \"codec\" }\n    \"tracing\" -> { \"service\" }\n    \"tls\" -> { \"service\" \"codec\" \"utils\" }\n    \"server\" -> { \"service\" \"rt\" \"utils\" }\n    \"rt\" -> { \"macros\" }\n\n    { rank=same; \"utils\" \"codec\" };\n    { rank=same; \"rt\" \"macros\" \"service\" };\n\n    // actix\n\n    \"actix\" -> { \"rt\" }\n}\n"
  },
  {
    "path": "docs/graphs/web-only.dot",
    "content": "digraph {\n    subgraph cluster_web {\n        label=\"actix/actix-web\"\n        \"awc\"\n        \"web\"\n        \"files\"\n        \"http\"\n        \"multipart\"\n        \"web-actors\"\n        \"web-codegen\"\n        \"http-test\"\n        \"test\"\n        \"router\"\n    }\n\n    \"web\" -> { \"web-codegen\" \"http\" \"router\" }\n    \"awc\" -> { \"http\" }\n    \"web-codegen\" -> { \"router\" }[color = red]\n    \"web-actors\" -> { \"actix\" \"web\" \"http\" }\n    \"multipart\" -> { \"web\" }\n    \"files\" -> { \"web\" }\n    \"http-test\" -> { \"awc\" }\n    \"test\" -> { \"web\" \"awc\" \"http-test\" }\n}\n"
  },
  {
    "path": "justfile",
    "content": "_list:\n    @just --list\n\ntoolchain := \"\"\n\n# Format workspace.\nfmt:\n    just --unstable --fmt\n    cargo +nightly fmt\n    fd --hidden --type=file --extension=md --extension=yml --exec-batch npx -y prettier --write\n\n# Downgrade dependencies necessary to run MSRV checks/tests.\n[private]\ndowngrade-for-msrv:\n    # no downgrades currently needed\n\nmsrv := ```\n    cargo metadata --format-version=1 \\\n    | jq -r 'first(.packages[] | select(.source == null and .rust_version)) | .rust_version' \\\n    | sed -E 's/^1\\.([0-9]{2})$/1\\.\\1\\.0/'\n```\nmsrv_rustup := \"+\" + msrv\nnon_linux_all_features_list := ```\n    cargo metadata --format-version=1 \\\n    | jq '.packages[] | select(.source == null) | .features | keys' \\\n    | jq -r --slurp \\\n        --arg exclusions \"__tls,__compress,tokio-uring,io-uring,experimental-io-uring\" \\\n        'add | unique | . - ($exclusions | split(\",\")) | join(\",\")'\n```\nall_crate_features := if os() == \"linux\" { \"--all-features\" } else { \"--features='\" + non_linux_all_features_list + \"'\" }\n\n[private]\ncheck-min:\n    cargo hack --workspace check --no-default-features\n\n[private]\ncheck-default:\n    cargo hack --workspace check\n\n# Check workspace.\ncheck: && clippy\n    fd --hidden --type=file --extension=md --extension=yml --exec-batch npx -y prettier --check\n\n# Run Clippy over workspace.\nclippy:\n    cargo {{ toolchain }} clippy --workspace --all-targets {{ all_crate_features }}\n\n# Run Clippy over workspace using MSRV.\nclippy-msrv: downgrade-for-msrv\n    @just toolchain={{ msrv_rustup }} clippy\n\n# Test workspace code.\ntest:\n    cargo {{ toolchain }} test --lib --tests -p=actix-web-codegen --all-features\n    cargo {{ toolchain }} test --lib --tests -p=actix-multipart-derive --all-features\n    cargo {{ toolchain }} nextest run --no-tests=warn -p=actix-router --no-default-features\n    cargo {{ toolchain }} nextest run --no-tests=warn --workspace --exclude=actix-web-codegen --exclude=actix-multipart-derive {{ all_crate_features }} --filter-expr=\"not test(test_reading_deflate_encoding_large_random_rustls)\"\n\n# Test workspace using MSRV.\ntest-msrv: downgrade-for-msrv\n    @just toolchain={{ msrv_rustup }} test\n\n# Test workspace docs.\ntest-docs: && doc\n    cargo {{ toolchain }} test --doc --workspace {{ all_crate_features }} --no-fail-fast -- --nocapture\n\n# Test workspace.\ntest-all: test test-docs\n\n# Test workspace and collect coverage info.\n[private]\ntest-coverage:\n    cargo {{ toolchain }} llvm-cov nextest --no-tests=warn --no-report {{ all_crate_features }}\n    cargo {{ toolchain }} llvm-cov --doc --no-report {{ all_crate_features }}\n\n# Test workspace and generate Codecov report.\ntest-coverage-codecov: test-coverage\n    cargo {{ toolchain }} llvm-cov report --doctests --codecov --output-path=codecov.json\n\n# Test workspace and generate LCOV report.\ntest-coverage-lcov: test-coverage\n    cargo {{ toolchain }} llvm-cov report --doctests --lcov --output-path=lcov.info\n\n# Document crates in workspace.\ndoc *args: && doc-set-workspace-crates\n    rm -f \"$(cargo metadata --format-version=1 | jq -r '.target_directory')/doc/crates.js\"\n    RUSTDOCFLAGS=\"--cfg=docsrs -Dwarnings\" cargo +nightly doc --no-deps --workspace {{ all_crate_features }} {{ args }}\n\n[private]\ndoc-set-workspace-crates:\n    #!/usr/bin/env bash\n    (\n        echo \"window.ALL_CRATES =\"\n        cargo metadata --format-version=1 \\\n        | jq '[.packages[] | select(.source == null) | .targets | map(select(.doc) | .name)] | flatten'\n        echo \";\"\n    ) > \"$(cargo metadata --format-version=1 | jq -r '.target_directory')/doc/crates.js\"\n\n# Document crates in workspace and watch for changes.\ndoc-watch:\n    @just doc --open\n    cargo watch -- just doc\n\n# Update READMEs from crate root documentation.\nupdate-readmes: && fmt\n    cd ./actix-files && cargo rdme --force\n    cd ./actix-http-test && cargo rdme --force\n    cd ./actix-router && cargo rdme --force\n    cd ./actix-multipart && cargo rdme --force\n    cd ./actix-test && cargo rdme --force\n\nfeature_combo_skip_list := if os() == \"linux\" { \"__tls,__compress\" } else { \"__tls,__compress,experimental-io-uring\" }\n\n# Checks compatibility of feature combinations.\ncheck-feature-combinations:\n    cargo hack --workspace \\\n        --feature-powerset --depth=4 \\\n        --skip={{ feature_combo_skip_list }} \\\n        check\n\n# Check for unintentional external type exposure on all crates in workspace.\ncheck-external-types-all toolchain=\"+nightly\":\n    #!/usr/bin/env bash\n    set -euo pipefail\n    exit=0\n    for f in $(find . -mindepth 2 -maxdepth 2 -name Cargo.toml | grep -vE \"\\-codegen/|\\-derive/|\\-macros/\"); do\n        if ! just check-external-types-manifest \"$f\" {{ toolchain }}; then exit=1; fi\n        echo\n        echo\n    done\n    exit $exit\n\n# Check for unintentional external type exposure on all crates in workspace.\ncheck-external-types-all-table toolchain=\"+nightly\":\n    #!/usr/bin/env bash\n    set -euo pipefail\n    for f in $(find . -mindepth 2 -maxdepth 2 -name Cargo.toml | grep -vE \"\\-codegen/|\\-derive/|\\-macros/\"); do\n        echo\n        echo \"Checking for $f\"\n        just check-external-types-manifest \"$f\" {{ toolchain }} --output-format=markdown-table\n    done\n\n# Check for unintentional external type exposure on a crate.\ncheck-external-types-manifest manifest_path toolchain=\"+nightly\" *extra_args=\"\":\n    cargo {{ toolchain }} check-external-types --manifest-path \"{{ manifest_path }}\" {{ extra_args }}\n"
  },
  {
    "path": "scripts/bump",
    "content": "#!/usr/bin/env bash\n\n# developed on macOS and probably doesn't work on Linux yet due to minor\n# differences in flags on sed\n\n# requires github cli tool for automatic release draft creation\n\nset -eEuo pipefail\n\nDIR=$1\n\nLINUX=\"\"\nMACOS=\"\"\n\nif [ \"$(uname)\" = \"Darwin\" ]; then\n    MACOS=\"1\"\nfi\n\nCARGO_MANIFEST=$DIR/Cargo.toml\nREADME_FILE=$DIR/README.md\n\n# determine changelog file name\nif [ -f \"$DIR/CHANGES.md\" ]; then\n    CHANGELOG_FILE=\"$DIR/CHANGES.md\"\nelif [ -f \"$DIR/CHANGELOG.md\" ]; then\n    CHANGELOG_FILE=\"$DIR/CHANGELOG.md\"\nfi\n\n# get current version\nPACKAGE_NAME=\"$(sed -nE 's/^name ?= ?\"([^\"]+)\"$/\\1/ p' \"$CARGO_MANIFEST\" | head -n 1)\"\nCURRENT_VERSION=\"$(sed -nE 's/^version ?= ?\"([^\"]+)\"$/\\1/ p' \"$CARGO_MANIFEST\" | head -n 1)\"\n\nCHANGE_CHUNK_FILE=\"$(mktemp)\"\necho saving changelog to $CHANGE_CHUNK_FILE\necho\n\nif [ -n \"${CHANGELOG_FILE-}\" ]; then\n    # get changelog chunk and save to temp file\n    cat \"$CHANGELOG_FILE\" |\n        # skip up to unreleased heading\n        sed '1,/Unreleased/ d' |\n        # take up to previous version heading\n        sed \"/$CURRENT_VERSION/ q\" |\n        # drop last line\n        sed '$d' \\\n            >\"$CHANGE_CHUNK_FILE\"\nfi\n\n# if word count of changelog chunk is 0 then insert filler changelog chunk\nif [ \"$(wc -w \"$CHANGE_CHUNK_FILE\" | awk '{ print $1 }')\" = \"0\" ]; then\n    echo \"- No significant changes since \\`$CURRENT_VERSION\\`.\" >\"$CHANGE_CHUNK_FILE\"\n    echo >>\"$CHANGE_CHUNK_FILE\"\nfi\n\nif [ -n \"${2-}\" ]; then\n    NEW_VERSION=\"$2\"\nelse\n    echo\n    echo \"--- Changes since $CURRENT_VERSION ----\"\n    cat \"$CHANGE_CHUNK_FILE\"\n    echo\n    read -p \"Update version to: \" NEW_VERSION\nfi\n\n# strip leading v from input\nif [ \"${NEW_VERSION:0:1}\" = \"v\" ]; then\n    NEW_VERSION=\"${NEW_VERSION:1}\"\nfi\n\necho \"updating from $CURRENT_VERSION => $NEW_VERSION\"\n\n# update package.version field\nsed -i.bak -E \"s/^version ?= ?\\\"[^\\\"]+\\\"$/version = \\\"$NEW_VERSION\\\"/\" \"$CARGO_MANIFEST\"\n\n# update readme\n[ -f \"$README_FILE\" ] && sed -i.bak -E \"s#$CURRENT_VERSION([/)])#$NEW_VERSION\\1#g\" \"$README_FILE\"\n\nif [ -n \"${CHANGELOG_FILE-}\" ]; then\n    # update changelog file\n    (\n        sed '/Unreleased/ q' \"$CHANGELOG_FILE\"                   # up to unreleased heading\n        echo                                                     # blank line\n        echo \"## $NEW_VERSION\"                                   # new version heading\n        cat \"$CHANGE_CHUNK_FILE\"                                 # previously unreleased changes\n        sed \"/$CURRENT_VERSION/ q\" \"$CHANGELOG_FILE\" | tail -n 1 # the previous version heading\n        sed \"1,/$CURRENT_VERSION/ d\" \"$CHANGELOG_FILE\"           # everything after previous version heading\n    ) >\"$CHANGELOG_FILE.bak\"\n    mv \"$CHANGELOG_FILE.bak\" \"$CHANGELOG_FILE\"\n\n    # format CHANGELOG file according to prettier\n    npx -y prettier --write \"$CHANGELOG_FILE\" || true\nfi\n\n# done; remove backup files\nrm -f $CARGO_MANIFEST.bak\nrm -f $README_FILE.bak\n\nif [ -n \"${CHANGELOG_FILE-}\" ]; then\n    rm -f $CHANGELOG_FILE.bak\nfi\n\necho \"manifest, changelog, and readme updated\"\necho\necho \"check other references:\"\nrg --glob='**/{Cargo.toml,README.md}' \"\\\n${PACKAGE_NAME} ?= ?\\\"[^\\\"]+\\\"\\\n|${PACKAGE_NAME} ?=.*version ?= ?\\\"([^\\\"]+)\\\"\\\n|package ?= ?\\\"${PACKAGE_NAME}\\\".*version ?= ?\\\"([^\\\"]+)\\\"\\\n|version ?= ?\\\"([^\\\"]+)\\\".*package ?= ?\\\"${PACKAGE_NAME}\\\"\" || true\n\necho\nread -p \"Update all references: (y/N) \" UPDATE_REFERENCES\nUPDATE_REFERENCES=\"${UPDATE_REFERENCES:-n}\"\n\nif [ \"$UPDATE_REFERENCES\" = 'y' ] || [ \"$UPDATE_REFERENCES\" = 'Y' ]; then\n    if [[ $NEW_VERSION == *\".0.0\" ]]; then\n        NEW_VERSION_SPEC=\"${NEW_VERSION%.0.0}\"\n    elif [[ $NEW_VERSION == *\".0\" ]]; then\n        NEW_VERSION_SPEC=\"${NEW_VERSION%.0}\"\n    else\n        NEW_VERSION_SPEC=\"$NEW_VERSION\"\n    fi\n\n    for f in $(fd Cargo.toml); do\n        sed -i.bak -E \\\n            \"s/^(${PACKAGE_NAME} ?= ?\\\")[^\\\"]+(\\\")$/\\1${NEW_VERSION_SPEC}\\2/g\" $f\n        sed -i.bak -E \\\n            \"s/^(${PACKAGE_NAME} ?=.*version ?= ?\\\")[^\\\"]+(\\\".*)$/\\1${NEW_VERSION_SPEC}\\2/g\" $f\n        sed -i.bak -E \\\n            \"s/^(.*package ?= ?\\\"${PACKAGE_NAME}\\\".*version ?= ?\\\")[^\\\"]+(\\\".*)$/\\1${NEW_VERSION_SPEC}\\2/g\" $f\n        sed -i.bak -E \\\n            \"s/^(.*version ?= ?\\\")[^\\\"]+(\\\".*package ?= ?\\\"${PACKAGE_NAME}\\\".*)$/\\1${NEW_VERSION_SPEC}\\2/g\" $f\n\n        # remove backup file\n        rm -f $f.bak\n    done\n\nfi\n\nif [ $MACOS ]; then\n    printf \"chore($PACKAGE_NAME): prepare release $NEW_VERSION\" | pbcopy\n    echo \"placed the recommended commit message on the clipboard\"\nelse\n    echo\n    echo \"commit message:\"\n    echo \"chore($PACKAGE_NAME): prepare release $NEW_VERSION\"\nfi\n\nSHORT_PACKAGE_NAME=\"$(echo $PACKAGE_NAME | sed 's/^actix-web-//' | sed 's/^actix-//')\"\nGIT_TAG=\"$(echo $SHORT_PACKAGE_NAME-v$NEW_VERSION)\"\nRELEASE_TITLE=\"$(echo $PACKAGE_NAME: v$NEW_VERSION)\"\n\nif [ \"$(echo $NEW_VERSION | grep beta)\" ] || [ \"$(echo $NEW_VERSION | grep rc)\" ] || [ \"$(echo $NEW_VERSION | grep alpha)\" ]; then\n    FLAGS=\"--prerelease\"\nelse\n    FLAGS=\"--latest\"\nfi\n\necho\necho \"GitHub release command:\"\nGH_CMD=\"gh release create \\\"$GIT_TAG\\\" --draft --title \\\"$RELEASE_TITLE\\\" --notes-file \\\"$CHANGE_CHUNK_FILE\\\" ${FLAGS:-}\"\necho \"$GH_CMD\"\n\nread -p \"Submit draft GH release: (y/N) \" GH_RELEASE\nGH_RELEASE=\"${GH_RELEASE:-n}\"\n\nif [ \"$GH_RELEASE\" = 'y' ] || [ \"$GH_RELEASE\" = 'Y' ]; then\n    eval \"$GH_CMD\"\nfi\n\necho\n\ncargo update >/dev/null 2>&1 || true\n"
  },
  {
    "path": "scripts/free-disk-space.sh",
    "content": "#!/usr/bin/env bash\n\n# Licensed to the Apache Software Foundation (ASF) under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# The Azure provided machines typically have the following disk allocation:\n# Total space: 85GB\n# Allocated: 67 GB\n# Free: 17 GB\n# This script frees up 28 GB of disk space by deleting unneeded packages and \n# large directories.\n# The Flink end to end tests download and generate more than 17 GB of files,\n# causing unpredictable behavior and build failures.\n\necho \"==============================================================================\"\necho \"Freeing up disk space on CI system\"\necho \"==============================================================================\"\n\necho \"Listing 100 largest packages\"\ndpkg-query -Wf '${Installed-Size}\\t${Package}\\n' | sort -n | tail -n 100\ndf -h\n\necho \"Removing large packages\"\nsudo apt-get remove -y '^dotnet-.*'\nsudo apt-get remove -y 'php.*'\nsudo apt-get remove -y '^mongodb-.*'\nsudo apt-get remove -y '^mysql-.*'\nsudo apt-get remove -y azure-cli google-cloud-sdk hhvm google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri\nsudo apt-get autoremove -y\nsudo apt-get clean\ndf -h\n\necho \"Removing large directories\"\nsudo rm -rf /usr/share/dotnet/\nsudo rm -rf /usr/local/graalvm/\nsudo rm -rf /usr/local/.ghcup/\nsudo rm -rf /usr/local/share/powershell\nsudo rm -rf /usr/local/share/chromium\nsudo rm -rf /usr/local/lib/android\nsudo rm -rf /usr/local/lib/node_modules\ndf -h\n"
  },
  {
    "path": "scripts/publish",
    "content": "#!/usr/bin/env bash\n\nset -Euo pipefail\n\nfor dir in $@; do\n    cd \"$dir\"\n    \n    cargo publish --dry-run\n\n    read -p \"Look okay? \"\n    read -p \"Sure? \"\n    \n    cargo publish\n\n    if [ $? -ne 0 ]; then\n        echo\n        read -p \"Was the above error caused by cyclic dev-deps? Choosing yes will publish without a git backreference. (y/N) \" publish_no_dev_deps\n\n        if [[ \"$publish_no_dev_deps\" == \"y\" || \"$publish_no_dev_deps\" == \"Y\" ]]; then\n            cargo hack --no-dev-deps publish --allow-dirty\n        fi\n    fi\n\n    cd ..\ndone\n"
  },
  {
    "path": "scripts/unreleased",
    "content": "#!/bin/sh\n\nset -euo pipefail\n\nbold=\"\\033[1m\"\nreset=\"\\033[0m\"\n\nunreleased_for() {\n    DIR=$1\n\n    CARGO_MANIFEST=$DIR/Cargo.toml\n\n    # determine changelog file name\n    if [ -f \"$DIR/CHANGES.md\" ]; then\n        CHANGELOG_FILE=$DIR/CHANGES.md\n    elif [ -f \"$DIR/CHANGELOG.md\" ]; then\n        CHANGELOG_FILE=$DIR/CHANGELOG.md\n    else\n        echo \"No changelog file found\"\n        exit 1\n    fi\n\n    # get current version\n    PACKAGE_NAME=\"$(sed -nE 's/^name ?= ?\"([^\"]+)\"$/\\1/ p' \"$CARGO_MANIFEST\" | head -n 1)\"\n    CURRENT_VERSION=\"$(sed -nE 's/^version ?= ?\"([^\"]+)\"$/\\1/ p' \"$CARGO_MANIFEST\")\"\n\n    CHANGE_CHUNK_FILE=\"$(mktemp)\"\n\n    # get changelog chunk and save to temp file\n    cat \"$CHANGELOG_FILE\" |\n        # skip up to unreleased heading\n        sed '1,/Unreleased/ d' |\n        # take up to previous version heading\n        sed \"/$CURRENT_VERSION/ q\" |\n        # drop last line\n        sed '$d' \\\n            >\"$CHANGE_CHUNK_FILE\"\n\n    # if word count of changelog chunk is 0 then exit\n    if [ \"$(wc -w \"$CHANGE_CHUNK_FILE\" | awk '{ print $1 }')\" = \"0\" ]; then\n        return 0;\n    fi\n\n    echo \"${bold}# ${PACKAGE_NAME}${reset} since ${bold}v$CURRENT_VERSION${reset}\"\n    cat \"$CHANGE_CHUNK_FILE\"\n}\n\nfiles=$(fd --threads=1 --min-depth=2 --absolute-path 'CHANGE\\w+.md')\n\nfor f in $files; do\n    unreleased_for $(dirname $f) || true\ndone\n"
  }
]