[
  {
    "path": ".github/workflows/clippy.yml",
    "content": "on: [push, pull_request]\nname: Clippy\njobs:\n  clippy_check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: nightly\n          override: true\n          components: clippy\n      - uses: actions-rs/clippy-check@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          args: --all-targets --all-features"
  },
  {
    "path": ".github/workflows/code-coverage.yml",
    "content": "name: Code Coverage\non:\n  push:\n    branches:\n    - master\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: nightly\n          override: true\n      - name: Check all\n        uses: actions-rs/cargo@v1\n        with:\n          command: check\n          args: --all --all-features\n  cover:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: nightly\n          override: true\n      - name: Install libsqlite3-dev\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libsqlite3-dev\n      - name: Run cargo-tarpaulin\n        uses: actions-rs/tarpaulin@v0.1\n        with:\n          version: '0.21.0'\n          args: --avoid-cfg-tarpaulin --out Xml --all --all-features\n      - name: Upload to codecov.io\n        uses: codecov/codecov-action@v1.0.2\n        with:\n          token: ${{secrets.CODECOV_TOKEN}}\n      - name: Archive code coverage results\n        uses: actions/upload-artifact@v1\n        with:\n          name: code-coverage-report\n          path: cobertura.xml\n"
  },
  {
    "path": ".github/workflows/nightly-test.yml",
    "content": "name: Nightly Test\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '0 0 * * *'\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: nightly\n          override: true\n      - name: Check all\n        uses: actions-rs/cargo@v1\n        with:\n          command: check\n          args: --all --all-features\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Install libsqlite3-dev\n        run: |\n          sudo apt-get update\n          sudo apt-get -y install libsqlite3-dev\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: nightly\n          override: true\n      - name: Run all tests\n        uses: actions-rs/cargo@v1\n        with:\n          command: test\n          args: --all --all-features --no-fail-fast\n\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    branches:\n      - release\n    paths:\n      - '**/Cargo.toml'\n      - '.github/workflows/release.yml'\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      max-parallel: 1\n      matrix:\n        package:\n          - name: roa-core\n            registryName: roa-core\n            path: roa-core\n            publishPath: /target/package\n          - name: roa\n            registryName: roa\n            path: roa\n            publishPath: /target/package\n          - name: roa-juniper\n            registryName: roa-juniper\n            path: roa-juniper\n            publishPath: /target/package\n          - name: roa-diesel\n            registryName: roa-diesel\n            path: roa-diesel\n            publishPath: /target/package\n          - name: roa-async-std\n            registryName: roa-async-std\n            path: roa-async-std\n            publishPath: /target/package\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          override: true\n      - name: install libsqlite3-dev\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libsqlite3-dev\n      - name: get version\n        working-directory: ${{ matrix.package.path }}\n        run: echo \"PACKAGE_VERSION=$(sed -nE 's/^\\s*version = \\\"(.*?)\\\"/\\1/p' Cargo.toml)\" >> $GITHUB_ENV\n      - name: check published version\n        run: echo \"PUBLISHED_VERSION=$(cargo search ${{ matrix.package.registryName }} --limit 1 | sed -nE 's/^[^\\\"]*\\\"//; s/\\\".*//1p' -)\" >> $GITHUB_ENV\n      - name: cargo login\n        if: env.PACKAGE_VERSION != env.PUBLISHED_VERSION\n        run: cargo login ${{ secrets.CRATE_TOKEN }}\n      - name: cargo package\n        if: env.PACKAGE_VERSION != env.PUBLISHED_VERSION\n        working-directory: ${{ matrix.package.path }}\n        run: |\n          echo \"package dir:\"\n          ls\n          cargo package\n          echo \"We will publish:\" $PACKAGE_VERSION\n          echo \"This is current latest:\" $PUBLISHED_VERSION\n          echo \"post package dir:\"\n          cd ${{ matrix.publishPath }}\n          ls\n      - name: Publish ${{ matrix.package.name }}\n        if: env.PACKAGE_VERSION != env.PUBLISHED_VERSION\n        working-directory: ${{ matrix.package.path }}\n        run: |\n          echo \"# Cargo Publish\" | tee -a ${{runner.workspace }}/notes.md\n          echo \"\\`\\`\\`\" >> ${{runner.workspace }}/notes.md\n          cargo publish --no-verify 2>&1 | tee -a ${{runner.workspace }}/notes.md\n          echo \"\\`\\`\\`\" >> ${{runner.workspace }}/notes.md\n      - name: Create Release\n        id: create_crate_release\n        if: env.PACKAGE_VERSION != env.PUBLISHED_VERSION\n        uses: jbolda/create-release@v1.1.0\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ matrix.package.name }}-v${{ env.PACKAGE_VERSION }}\n          release_name: \"Release ${{ matrix.package.name }} v${{ env.PACKAGE_VERSION }} [crates.io]\"\n          bodyFromFile: ./../notes.md\n          draft: false\n          prerelease: false\n      - name: Upload Release Asset\n        id: upload-release-asset\n        if: env.PACKAGE_VERSION != env.PUBLISHED_VERSION\n        uses: actions/upload-release-asset@v1.0.1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_crate_release.outputs.upload_url }}\n          asset_path: ./${{ matrix.package.publishPath }}/${{ matrix.package.registryName }}-${{ env.PACKAGE_VERSION }}.crate\n          asset_name: ${{ matrix.package.registryName }}-${{ env.PACKAGE_VERSION }}.crate\n          asset_content_type: application/x-gtar"
  },
  {
    "path": ".github/workflows/security-audit.yml",
    "content": "name: Security Audit\non:\n  schedule:\n    - cron: '0 0 * * *'\njobs:\n  audit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/audit-check@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/stable-test.yml",
    "content": "on: [push, pull_request]\nname: Stable Test\njobs:\n  check:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        rust:\n          - stable\n          - 1.60.0\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: ${{ matrix.rust }}\n          override: true\n      - name: Check all\n        uses: actions-rs/cargo@v1\n        with:\n          command: check\n          args: --all --features \"roa/full\"\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        rust:\n          - stable\n          - 1.60.0\n    steps:\n      - name: Install libsqlite3-dev\n        run: |\n          sudo apt-get update\n          sudo apt-get -y install libsqlite3-dev\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: ${{ matrix.rust }}\n          override: true\n      - name: Run all tests\n        uses: actions-rs/cargo@v1\n        with:\n          command: test\n          args: --all --features \"roa/full\" --no-fail-fast\n        \n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\n**/upload/*\n.env\nnode_modules"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-core'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-core\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-core\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-diesel'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-diesel\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-diesel\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-tokio'\",\n            \"cargo\": {\n                \"args\": [\n                    \"+nightly\",\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-tokio\",\n                    \"--all-features\",\n                ],\n                \"filter\": {\n                    \"name\": \"roa-tokio\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-multipart'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-multipart\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-multipart\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-juniper'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-juniper\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-juniper\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-jsonrpc'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-jsonrpc\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-jsonrpc\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'diesel-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=diesel-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"diesel-example\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug executable 'api'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--bin=api\",\n                    \"--package=diesel-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"api\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in executable 'api'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--bin=api\",\n                    \"--package=diesel-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"api\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug integration test 'restful'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--test=restful\",\n                    \"--package=diesel-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"restful\",\n                    \"kind\": \"test\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug executable 'multipart-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--bin=multipart-example\",\n                    \"--package=multipart-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"multipart-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in executable 'multipart-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--bin=multipart-example\",\n                    \"--package=multipart-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"multipart-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug executable 'websocket-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--bin=websocket-example\",\n                    \"--package=websocket-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"websocket-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in executable 'websocket-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--bin=websocket-example\",\n                    \"--package=websocket-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"websocket-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug executable 'juniper-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--bin=juniper-example\",\n                    \"--package=juniper-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"juniper-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in executable 'juniper-example'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--bin=juniper-example\",\n                    \"--package=juniper-example\"\n                ],\n                \"filter\": {\n                    \"name\": \"juniper-example\",\n                    \"kind\": \"bin\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in library 'roa-root'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--lib\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"roa-root\",\n                    \"kind\": \"lib\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'echo'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=echo\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"echo\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'echo'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=echo\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"echo\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'hello-world'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=hello-world\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"hello-world\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'hello-world'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=hello-world\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"hello-world\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'https'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=https\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"https\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'https'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=https\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"https\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'restful-api'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=restful-api\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"restful-api\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'restful-api'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=restful-api\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"restful-api\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'serve-file'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=serve-file\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"serve-file\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'serve-file'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=serve-file\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"serve-file\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'websocket-echo'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=websocket-echo\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"websocket-echo\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'websocket-echo'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=websocket-echo\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"websocket-echo\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug example 'welcome'\",\n            \"cargo\": {\n                \"args\": [\n                    \"build\",\n                    \"--example=welcome\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"welcome\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug unit tests in example 'welcome'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--example=welcome\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"welcome\",\n                    \"kind\": \"example\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug integration test 'logger'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--test=logger\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"logger\",\n                    \"kind\": \"test\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug integration test 'restful'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--test=restful\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"restful\",\n                    \"kind\": \"test\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        },\n        {\n            \"type\": \"lldb\",\n            \"request\": \"launch\",\n            \"name\": \"Debug integration test 'serve-file'\",\n            \"cargo\": {\n                \"args\": [\n                    \"test\",\n                    \"--no-run\",\n                    \"--test=serve-file\",\n                    \"--package=roa-root\"\n                ],\n                \"filter\": {\n                    \"name\": \"serve-file\",\n                    \"kind\": \"test\"\n                }\n            },\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\"\n        }\n    ]\n}"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"roa-root\"\nversion = \"0.6.0\"\nauthors = [\"Hexilee <hexileee@gmail.com>\"]\nedition = \"2018\"\nlicense = \"MIT\"\npublish = false\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[workspace]\nmembers = [\n    \"roa\",\n    \"roa-core\",\n    \"roa-diesel\",\n    \"roa-async-std\",\n    \"roa-juniper\",\n    \"integration/diesel-example\",\n    \"integration/multipart-example\",\n    \"integration/websocket-example\",\n    \"integration/juniper-example\"\n]\n\n[dev-dependencies]\ntokio = { version = \"1.15\", features = [\"full\"] }\nreqwest = { version = \"0.11\", features = [\"json\", \"cookies\", \"gzip\"] }\nserde = { version = \"1\", features = [\"derive\"] }\nroa = { path = \"./roa\", features = [\"full\"] }\ntest-case = \"1.2\"\nonce_cell = \"1.8\"\nlog = \"0.4\"\nslab = \"0.4.2\"\nmultimap = \"0.8.0\"\nhyper = \"0.14\"\nchrono = \"0.4\"\nmime = \"0.3\"\nencoding = \"0.2\"\naskama = \"0.10\"\nhttp = \"0.2\"\nbytesize = \"1.1\"\nserde_json = \"1.0\"\ntracing = \"0.1\"\nfutures = \"0.3\"\ndoc-comment = \"0.3.3\"\nanyhow = \"1.0\"\ntracing-futures = \"0.2\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2020 Hexilee\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."
  },
  {
    "path": "Makefile",
    "content": "check:\n\tcargo check --all --features \"roa/full\"\nbuild: \n\tcargo build --all --features \"roa/full\"\ntest: \n\tcargo test --all --features \"roa/full\"\nfmt:\n\tcargo +nightly fmt\nlint:\n\tcargo clippy --all-targets -- -D warnings\ncheck-all:\n\tcargo +nightly check --all --all-features\ntest-all:\n\tcargo +nightly test --all --all-features\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <h1>Roa</h1>\n  <p><strong>Roa is an async web framework inspired by koajs, lightweight but powerful. </strong> </p>\n  <p>\n\n[![Stable Test](https://github.com/Hexilee/roa/workflows/Stable%20Test/badge.svg)](https://github.com/Hexilee/roa/actions)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa) \n[![wiki](https://img.shields.io/badge/roa-wiki-purple.svg)](https://github.com/Hexilee/roa/wiki)\n[![Rust Docs](https://docs.rs/roa/badge.svg)](https://docs.rs/roa)\n[![Crate version](https://img.shields.io/crates/v/roa.svg)](https://crates.io/crates/roa)\n[![Download](https://img.shields.io/crates/d/roa.svg)](https://crates.io/crates/roa)\n[![MSRV-1.54](https://img.shields.io/badge/MSRV-1.54-blue.svg)](https://blog.rust-lang.org/2021/07/29/Rust-1.54.0.html)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\n  </p>\n\n  <h3>\n    <a href=\"https://github.com/Hexilee/roa/tree/master/examples\">Examples</a>\n    <span> | </span>\n    <a href=\"https://github.com/Hexilee/roa/wiki/Guide\">Guide</a>\n    <span> | </span>\n    <a href=\"https://github.com/Hexilee/roa/wiki/Cookbook\">Cookbook</a>\n  </h3>\n</div>\n<br>\n\n\n#### Feature highlights\n\n- A lightweight, solid and well extensible core.\n    - Supports HTTP/1.x and HTTP/2.0 protocols.\n    - Full streaming.\n    - Highly extensible middleware system.\n    - Based on [`hyper`](https://github.com/hyperium/hyper), runtime-independent, you can chose async runtime as you like.\n- Many useful extensions.\n    - Official runtime schemes:\n        - (Default) [tokio](https://github.com/tokio-rs/tokio) runtime and TcpStream.\n        - [async-std](https://github.com/async-rs/async-std) runtime and TcpStream.\n    - Transparent content compression (br, gzip, deflate, zstd).\n    - Configurable and nestable router.\n    - Named uri parameters(query and router parameter).\n    - Cookie and jwt support.\n    - HTTPS support.\n    - WebSocket support.\n    - Asynchronous multipart form support.\n    - Other middlewares(logger, CORS .etc).\n- Integrations\n    - roa-diesel, integration with [diesel](https://github.com/diesel-rs/diesel).\n    - roa-juniper, integration with [juniper](https://github.com/graphql-rust/juniper).\n- Works on stable Rust.\n\n#### Get start\n\n```toml\n# Cargo.toml\n\n[dependencies]\nroa = \"0.6\"\ntokio = { version = \"1.15\", features = [\"rt\", \"macro\"] }\n```\n\n```rust,no_run\nuse roa::App;\nuse roa::preload::*;\n\n#[tokio::main]\nasync fn main() -> anyhow::Result<()> {\n    let app = App::new().end(\"Hello, World\");\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        println!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n```\nRefer to [wiki](https://github.com/Hexilee/roa/wiki) for more details.\n"
  },
  {
    "path": "assets/author.txt",
    "content": "Hexilee"
  },
  {
    "path": "assets/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFPjCCAyYCCQDvLYiYD+jqeTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww\nCgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xODAxMjUx\nNzQ2MDFaFw0xOTAxMjUxNzQ2MDFaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD\nQTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY\nMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\nMIICCgKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEPn8k1\nsQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+MIK5U\nNLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM54jXy\nvoLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZWLWr\nodGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAkoqND\nxdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNliJDmA\nCRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6/stI\nyFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuDYX2U\nUuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nPwPTO\nvRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA69un\nCEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEAATAN\nBgkqhkiG9w0BAQsFAAOCAgEApavsgsn7SpPHfhDSN5iZs1ILZQRewJg0Bty0xPfk\n3tynSW6bNH3nSaKbpsdmxxomthNSQgD2heOq1By9YzeOoNR+7Pk3s4FkASnf3ToI\nJNTUasBFFfaCG96s4Yvs8KiWS/k84yaWuU8c3Wb1jXs5Rv1qE1Uvuwat1DSGXSoD\nJNluuIkCsC4kWkyq5pWCGQrabWPRTWsHwC3PTcwSRBaFgYLJaR72SloHB1ot02zL\nd2age9dmFRFLLCBzP+D7RojBvL37qS/HR+rQ4SoQwiVc/JzaeqSe7ZbvEH9sZYEu\nALowJzgbwro7oZflwTWunSeSGDSltkqKjvWvZI61pwfHKDahUTmZ5h2y67FuGEaC\nCIOUI8dSVSPKITxaq3JL4ze2e9/0Lt7hj19YK2uUmtMAW5Tirz4Yx5lyGH9U8Wur\ny/X8VPxTc4A9TMlJgkyz0hqvhbPOT/zSWB10zXh0glKAsSBryAOEDxV1UygmSir7\nYV8Qaq+oyKUTMc1MFq5vZ07M51EPaietn85t8V2Y+k/8XYltRp32NxsypxAJuyxh\ng/ko6RVTrWa1sMvz/F9LFqAdKiK5eM96lh9IU4xiLg4ob8aS/GRAA8oIFkZFhLrt\ntOwjIUPmEPyHWFi8dLpNuQKYalLYhuwZftG/9xV+wqhKGZO9iPrpHSYBRTap8w2y\n1QU=\n-----END CERTIFICATE-----"
  },
  {
    "path": "assets/css/table.css",
    "content": "/* spacing */\n\ntable {\n    table-layout: fixed;\n    width: 80%;\n    border-collapse: collapse;\n}\n\nthead th {\n    text-align: left\n}\n\nthead th:nth-child(1) {\n    width: 40%;\n}\n\nthead th:nth-child(2) {\n    width: 20%;\n}\n\nthead th:nth-child(3) {\n    width: 40%;\n}\n\nth, td {\n    padding: 10px;\n}"
  },
  {
    "path": "assets/key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEP\nn8k1sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+M\nIK5UNLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM5\n4jXyvoLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZ\nWLWrodGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAk\noqNDxdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNli\nJDmACRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6\n/stIyFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuD\nYX2UUuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nP\nwPTOvRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA\n69unCEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEA\nAQKCAgAME3aoeXNCPxMrSri7u4Xnnk71YXl0Tm9vwvjRQlMusXZggP8VKN/KjP0/\n9AE/GhmoxqPLrLCZ9ZE1EIjgmZ9Xgde9+C8rTtfCG2RFUL7/5J2p6NonlocmxoJm\nYkxYwjP6ce86RTjQWL3RF3s09u0inz9/efJk5O7M6bOWMQ9VZXDlBiRY5BYvbqUR\n6FeSzD4MnMbdyMRoVBeXE88gTvZk8xhB6DJnLzYgc0tKiRoeKT0iYv5JZw25VyRM\nycLzfTrFmXCPfB1ylb483d9Ly4fBlM8nkx37PzEnAuukIawDxsPOb9yZC+hfvNJI\n7NFiMN+3maEqG2iC00w4Lep4skHY7eHUEUMl+Wjr+koAy2YGLWAwHZQTm7iXn9Ab\nL6adL53zyCKelRuEQOzbeosJAqS+5fpMK0ekXyoFIuskj7bWuIoCX7K/kg6q5IW+\nvC2FrlsrbQ79GztWLVmHFO1I4J9M5r666YS0qdh8c+2yyRl4FmSiHfGxb3eOKpxQ\nb6uI97iZlkxPF9LYUCSc7wq0V2gGz+6LnGvTHlHrOfVXqw/5pLAKhXqxvnroDTwz\n0Ay/xFF6ei/NSxBY5t8ztGCBm45wCU3l8pW0X6dXqwUipw5b4MRy1VFRu6rqlmbL\nOPSCuLxqyqsigiEYsBgS/icvXz9DWmCQMPd2XM9YhsHvUq+R4QKCAQEA98EuMMXI\n6UKIt1kK2t/3OeJRyDd4iv/fCMUAnuPjLBvFE4cXD/SbqCxcQYqb+pue3PYkiTIC\n71rN8OQAc5yKhzmmnCE5N26br/0pG4pwEjIr6mt8kZHmemOCNEzvhhT83nfKmV0g\n9lNtuGEQMiwmZrpUOF51JOMC39bzcVjYX2Cmvb7cFbIq3lR0zwM+aZpQ4P8LHCIu\nbgHmwbdlkLyIULJcQmHIbo6nPFB3ZZE4mqmjwY+rA6Fh9rgBa8OFCfTtrgeYXrNb\nIgZQ5U8GoYRPNC2ot0vpTinraboa/cgm6oG4M7FW1POCJTl+/ktHEnKuO5oroSga\n/BSg7hCNFVaOhwKCAQEA4Kkys0HtwEbV5mY/NnvUD5KwfXX7BxoXc9lZ6seVoLEc\nKjgPYxqYRVrC7dB2YDwwp3qcRTi/uBAgFNm3iYlDzI4xS5SeaudUWjglj7BSgXE2\niOEa7EwcvVPluLaTgiWjlzUKeUCNNHWSeQOt+paBOT+IgwRVemGVpAgkqQzNh/nP\ntl3p9aNtgzEm1qVlPclY/XUCtf3bcOR+z1f1b4jBdn0leu5OhnxkC+Htik+2fTXD\njt6JGrMkanN25YzsjnD3Sn+v6SO26H99wnYx5oMSdmb8SlWRrKtfJHnihphjG/YY\nl1cyorV6M/asSgXNQfGJm4OuJi0I4/FL2wLUHnU+JwKCAQEAzh4WipcRthYXXcoj\ngMKRkMOb3GFh1OpYqJgVExtudNTJmZxq8GhFU51MR27Eo7LycMwKy2UjEfTOnplh\nUs2qZiPtW7k8O8S2m6yXlYUQBeNdq9IuuYDTaYD94vsazscJNSAeGodjE+uGvb1q\n1wLqE87yoE7dUInYa1cOA3+xy2/CaNuviBFJHtzOrSb6tqqenQEyQf6h9/12+DTW\nt5pSIiixHrzxHiFqOoCLRKGToQB+71rSINwTf0nITNpGBWmSj5VcC3VV3TG5/XxI\nfPlxV2yhD5WFDPVNGBGvwPDSh4jSMZdZMSNBZCy4XWFNSKjGEWoK4DFYed3DoSt9\n5IG1YwKCAQA63ntHl64KJUWlkwNbboU583FF3uWBjee5VqoGKHhf3CkKMxhtGqnt\n+oN7t5VdUEhbinhqdx1dyPPvIsHCS3K1pkjqii4cyzNCVNYa2dQ00Qq+QWZBpwwc\n3GAkz8rFXsGIPMDa1vxpU6mnBjzPniKMcsZ9tmQDppCEpBGfLpio2eAA5IkK8eEf\ncIDB3CM0Vo94EvI76CJZabaE9IJ+0HIJb2+jz9BJ00yQBIqvJIYoNy9gP5Xjpi+T\nqV/tdMkD5jwWjHD3AYHLWKUGkNwwkAYFeqT/gX6jpWBP+ZRPOp011X3KInJFSpKU\nDT5GQ1Dux7EMTCwVGtXqjO8Ym5wjwwsfAoIBAEcxlhIW1G6BiNfnWbNPWBdh3v/K\n5Ln98Rcrz8UIbWyl7qNPjYb13C1KmifVG1Rym9vWMO3KuG5atK3Mz2yLVRtmWAVc\nfxzR57zz9MZFDun66xo+Z1wN3fVxQB4CYpOEI4Lb9ioX4v85hm3D6RpFukNtRQEc\nGfr4scTjJX4jFWDp0h6ffMb8mY+quvZoJ0TJqV9L9Yj6Ksdvqez/bdSraev97bHQ\n4gbQxaTZ6WjaD4HjpPQefMdWp97Metg0ZQSS8b8EzmNFgyJ3XcjirzwliKTAQtn6\nI2sd0NCIooelrKRD8EJoDUwxoOctY7R97wpZ7/wEHU45cBCbRV3H4JILS5c=\n-----END RSA PRIVATE KEY-----"
  },
  {
    "path": "assets/welcome.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Roa Framework</title>\n</head>\n<body>\n<h1>Welcome!</h1>\n<h4>Go to <a href=\"https://github.com/Hexilee/roa\">roa</a> for more information...</h4>\n</body>\n</html>"
  },
  {
    "path": "examples/echo.rs",
    "content": "//! RUST_LOG=info Cargo run --example echo,\n//! then request http://127.0.0.1:8000 with some payload.\n\nuse std::error::Error as StdError;\n\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::{App, Context};\nuse tracing::info;\nuse tracing_subscriber::EnvFilter;\n\nasync fn echo(ctx: &mut Context) -> roa::Result {\n    let stream = ctx.req.stream();\n    ctx.resp.write_stream(stream);\n    Ok(())\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let app = App::new().gate(logger).end(echo);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/hello-world.rs",
    "content": "//! RUST_LOG=info Cargo run --example hello-world,\n//! then request http://127.0.0.1:8000.\n\nuse log::info;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::App;\nuse tracing_subscriber::EnvFilter;\n\n#[tokio::main]\nasync fn main() -> anyhow::Result<()> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n    let app = App::new().gate(logger).end(\"Hello, World!\");\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/https.rs",
    "content": "//! RUST_LOG=info Cargo run --example https,\n//! then request https://127.0.0.1:8000.\n\nuse std::error::Error as StdError;\nuse std::fs::File;\nuse std::io::BufReader;\n\nuse log::info;\nuse roa::body::DispositionType;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::tls::pemfile::{certs, rsa_private_keys};\nuse roa::tls::{Certificate, PrivateKey, ServerConfig, TlsListener};\nuse roa::{App, Context};\nuse tracing_subscriber::EnvFilter;\n\nasync fn serve_file(ctx: &mut Context) -> roa::Result {\n    ctx.write_file(\"assets/welcome.html\", DispositionType::Inline)\n        .await\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let mut cert_file = BufReader::new(File::open(\"assets/cert.pem\")?);\n    let mut key_file = BufReader::new(File::open(\"assets/key.pem\")?);\n    let cert_chain = certs(&mut cert_file)?\n        .into_iter()\n        .map(Certificate)\n        .collect();\n\n    let config = ServerConfig::builder()\n        .with_safe_defaults()\n        .with_no_client_auth()\n        .with_single_cert(\n            cert_chain,\n            PrivateKey(rsa_private_keys(&mut key_file)?.remove(0)),\n        )?;\n\n    let app = App::new().gate(logger).end(serve_file);\n    app.listen_tls(\"127.0.0.1:8000\", config, |addr| {\n        info!(\"Server is listening on https://localhost:{}\", addr.port())\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/restful-api.rs",
    "content": "//! RUST_LOG=info Cargo run --example restful-api,\n//! then:\n//! - `curl 127.0.0.1:8000/user/0`\n//!     query user where id=0\n//! - `curl -H \"Content-type: application/json\" -d '{\"name\":\"Hexilee\", \"age\": 20}' -X POST 127.0.0.1:8000/user`\n//!     create a new user\n//! - `curl -H \"Content-type: application/json\" -d '{\"name\":\"Alice\", \"age\": 20}' -X PUT 127.0.0.1:8000/user/0`\n//!     update user where id=0, return the old data\n//! - `curl 127.0.0.1:8000/user/0 -X DELETE`\n//!     delete user where id=0\n\nuse std::result::Result as StdResult;\nuse std::sync::Arc;\n\nuse roa::http::StatusCode;\nuse roa::preload::*;\nuse roa::router::{get, post, Router};\nuse roa::{throw, App, Context, Result};\nuse serde::{Deserialize, Serialize};\nuse serde_json::json;\nuse slab::Slab;\nuse tokio::sync::RwLock;\n\n#[derive(Debug, Serialize, Deserialize, Clone)]\nstruct User {\n    name: String,\n    age: u8,\n}\n\n#[derive(Clone)]\nstruct Database {\n    table: Arc<RwLock<Slab<User>>>,\n}\n\nimpl Database {\n    fn new() -> Self {\n        Self {\n            table: Arc::new(RwLock::new(Slab::new())),\n        }\n    }\n\n    async fn create(&self, user: User) -> usize {\n        self.table.write().await.insert(user)\n    }\n\n    async fn retrieve(&self, id: usize) -> Result<User> {\n        match self.table.read().await.get(id) {\n            Some(user) => Ok(user.clone()),\n            None => throw!(StatusCode::NOT_FOUND),\n        }\n    }\n\n    async fn update(&self, id: usize, new_user: &mut User) -> Result {\n        match self.table.write().await.get_mut(id) {\n            Some(user) => {\n                std::mem::swap(new_user, user);\n                Ok(())\n            }\n            None => throw!(StatusCode::NOT_FOUND),\n        }\n    }\n\n    async fn delete(&self, id: usize) -> Result<User> {\n        if !self.table.read().await.contains(id) {\n            throw!(StatusCode::NOT_FOUND)\n        }\n        Ok(self.table.write().await.remove(id))\n    }\n}\n\nasync fn create_user(ctx: &mut Context<Database>) -> Result {\n    let user: User = ctx.read_json().await?;\n    let id = ctx.create(user).await;\n    ctx.write_json(&json!({ \"id\": id }))?;\n    ctx.resp.status = StatusCode::CREATED;\n    Ok(())\n}\n\nasync fn get_user(ctx: &mut Context<Database>) -> Result {\n    let id: usize = ctx.must_param(\"id\")?.parse()?;\n    let user = ctx.retrieve(id).await?;\n    ctx.write_json(&user)\n}\n\nasync fn update_user(ctx: &mut Context<Database>) -> Result {\n    let id: usize = ctx.must_param(\"id\")?.parse()?;\n    let mut user: User = ctx.read_json().await?;\n    ctx.update(id, &mut user).await?;\n    ctx.write_json(&user)\n}\n\nasync fn delete_user(ctx: &mut Context<Database>) -> Result {\n    let id: usize = ctx.must_param(\"id\")?.parse()?;\n    let user = ctx.delete(id).await?;\n    ctx.write_json(&user)\n}\n\n#[tokio::main]\nasync fn main() -> StdResult<(), Box<dyn std::error::Error>> {\n    let router = Router::new()\n        .on(\"/\", post(create_user))\n        .on(\"/:id\", get(get_user).put(update_user).delete(delete_user));\n    let app = App::state(Database::new()).end(router.routes(\"/user\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        println!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/serve-file.rs",
    "content": "//! RUST_LOG=info cargo run --example serve-file,\n//! then request http://127.0.0.1:8000.\n\nuse std::borrow::Cow;\nuse std::path::Path;\nuse std::result::Result as StdResult;\nuse std::time::SystemTime;\n\nuse askama::Template;\nuse bytesize::ByteSize;\nuse chrono::offset::Local;\nuse chrono::DateTime;\nuse log::info;\nuse roa::body::DispositionType::*;\nuse roa::compress::Compress;\nuse roa::http::StatusCode;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::router::{get, Router};\nuse roa::{throw, App, Context, Next, Result};\nuse tokio::fs::{metadata, read_dir};\nuse tracing_subscriber::EnvFilter;\n\n#[derive(Template)]\n#[template(path = \"directory.html\")]\nstruct Dir<'a> {\n    title: &'a str,\n    root: &'a str,\n    dirs: Vec<DirInfo>,\n    files: Vec<FileInfo>,\n}\n\nstruct DirInfo {\n    link: String,\n    name: String,\n    modified: String,\n}\n\nstruct FileInfo {\n    link: String,\n    name: String,\n    modified: String,\n    size: String,\n}\n\nimpl<'a> Dir<'a> {\n    fn new(title: &'a str, root: &'a str) -> Self {\n        Self {\n            title,\n            root,\n            dirs: Vec::new(),\n            files: Vec::new(),\n        }\n    }\n}\n\nasync fn path_checker(ctx: &mut Context, next: Next<'_>) -> Result {\n    if ctx.must_param(\"path\")?.contains(\"..\") {\n        throw!(StatusCode::BAD_REQUEST, \"invalid path\")\n    } else {\n        next.await\n    }\n}\n\nasync fn serve_path(ctx: &mut Context) -> Result {\n    let path_value = ctx.must_param(\"path\")?;\n    let path = path_value.as_ref();\n    let file_path = Path::new(\".\").join(path);\n    let meta = metadata(&file_path).await?;\n    if meta.is_file() {\n        ctx.write_file(file_path, Inline).await\n    } else if meta.is_dir() {\n        serve_dir(ctx, path).await\n    } else {\n        throw!(StatusCode::NOT_FOUND, \"path not found\")\n    }\n}\n\nasync fn serve_root(ctx: &mut Context) -> Result {\n    serve_dir(ctx, \"\").await\n}\n\nasync fn serve_dir(ctx: &mut Context, path: &str) -> Result {\n    let uri_path = Path::new(\"/\").join(path);\n    let mut entries = read_dir(Path::new(\".\").join(path)).await?;\n    let title = uri_path\n        .file_name()\n        .map(|os_str| os_str.to_string_lossy())\n        .unwrap_or(Cow::Borrowed(\"/\"));\n    let root_str = uri_path.to_string_lossy();\n    let mut dir = Dir::new(&title, &root_str);\n    while let Some(entry) = entries.next_entry().await? {\n        let metadata = entry.metadata().await?;\n        if metadata.is_dir() {\n            dir.dirs.push(DirInfo {\n                link: uri_path\n                    .join(entry.file_name())\n                    .to_string_lossy()\n                    .to_string(),\n                name: entry.file_name().to_string_lossy().to_string(),\n                modified: format_time(metadata.modified()?),\n            })\n        }\n        if metadata.is_file() {\n            dir.files.push(FileInfo {\n                link: uri_path\n                    .join(entry.file_name())\n                    .to_string_lossy()\n                    .to_string(),\n                name: entry.file_name().to_string_lossy().to_string(),\n                modified: format_time(metadata.modified()?),\n                size: ByteSize(metadata.len()).to_string(),\n            })\n        }\n    }\n    ctx.render(&dir)\n}\n\nfn format_time(time: SystemTime) -> String {\n    let datetime: DateTime<Local> = time.into();\n    datetime.format(\"%d/%m/%Y %T\").to_string()\n}\n\n#[tokio::main]\nasync fn main() -> StdResult<(), Box<dyn std::error::Error>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let wildcard_router = Router::new().gate(path_checker).on(\"/\", get(serve_path));\n    let router = Router::new()\n        .on(\"/\", serve_root)\n        .include(\"/*{path}\", wildcard_router);\n    let app = App::new()\n        .gate(logger)\n        .gate(Compress::default())\n        .end(router.routes(\"/\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await\n    .map_err(Into::into)\n}\n"
  },
  {
    "path": "examples/websocket-echo.rs",
    "content": "//! RUST_LOG=info cargo run --example websocket-echo,\n//! then request ws://127.0.0.1:8000/chat.\n\nuse std::error::Error as StdError;\n\nuse futures::StreamExt;\nuse http::Method;\nuse log::{error, info};\nuse roa::cors::Cors;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::router::{allow, Router};\nuse roa::websocket::Websocket;\nuse roa::App;\nuse tracing_subscriber::EnvFilter;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let router = Router::new().on(\n        \"/chat\",\n        allow(\n            [Method::GET],\n            Websocket::new(|_ctx, stream| async move {\n                let (write, read) = stream.split();\n                if let Err(err) = read.forward(write).await {\n                    error!(\"{}\", err);\n                }\n            }),\n        ),\n    );\n    let app = App::new()\n        .gate(logger)\n        .gate(Cors::new())\n        .end(router.routes(\"/\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/welcome.rs",
    "content": "//! RUST_LOG=info Cargo run --example welcome,\n//! then request http://127.0.0.1:8000 with some payload.\n\nuse std::error::Error as StdError;\n\nuse log::info;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::App;\nuse tracing_subscriber::EnvFilter;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let app = App::new()\n        .gate(logger)\n        .end(include_str!(\"../assets/welcome.html\"));\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "integration/diesel-example/Cargo.toml",
    "content": "[package]\nname = \"diesel-example\"\nversion = \"0.1.0\"\nauthors = [\"Hexilee <hexileee@gmail.com>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\ntokio = { version = \"1.15\", features = [\"full\"] }\ndiesel = { version = \"1.4\", features = [\"extras\", \"sqlite\"] }\nroa = { path = \"../../roa\", features = [\"router\", \"json\"] }\nroa-diesel = { path = \"../../roa-diesel\" }\ntracing-futures = \"0.2\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\ntracing = \"0.1\"\nserde = { version = \"1\", features = [\"derive\"] }\nanyhow = \"1.0\"\n\n[dev-dependencies]\nreqwest = { version = \"0.11\", features = [\"json\", \"cookies\", \"gzip\"] }"
  },
  {
    "path": "integration/diesel-example/README.md",
    "content": "```bash\nRUST_LOG=info cargo run --bin api\n```\n\n- `curl 127.0.0.1:8000/post/1`\n    query post where id=0 and published\n- `curl -H \"Content-type: application/json\" -d '{\"title\":\"Hello\", \"body\": \"Hello, world\", \"published\": false}' -X POST 127.0.0.1:8000/post`\n    create a new post\n- `curl -H \"Content-type: application/json\" -d '{\"title\":\"Hello\", \"body\": \"Hello, world\", \"published\": true}' -X PUT 127.0.0.1:8000/post/1`\n    update post where id=0, return the old data\n- `curl 127.0.0.1:8000/post/1 -X DELETE`\n    delete post where id=0\n"
  },
  {
    "path": "integration/diesel-example/src/bin/api.rs",
    "content": "use diesel_example::{create_pool, post_router};\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::App;\nuse tracing::info;\nuse tracing_subscriber::EnvFilter;\n\n#[tokio::main]\nasync fn main() -> anyhow::Result<()> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n    let app = App::state(create_pool()?)\n        .gate(logger)\n        .end(post_router().routes(\"/post\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "integration/diesel-example/src/data_object.rs",
    "content": "use serde::Deserialize;\n\nuse crate::schema::posts;\n\n// for both transfer and access\n#[derive(Debug, Insertable, Deserialize)]\n#[table_name = \"posts\"]\npub struct NewPost {\n    pub title: String,\n    pub body: String,\n    pub published: bool,\n}\n"
  },
  {
    "path": "integration/diesel-example/src/endpoints.rs",
    "content": "use diesel::prelude::*;\nuse diesel::result::Error;\nuse roa::http::StatusCode;\nuse roa::preload::*;\nuse roa::router::{get, post, Router};\nuse roa::{throw, Context, Result};\nuse roa_diesel::preload::*;\n\nuse crate::data_object::NewPost;\nuse crate::models::*;\nuse crate::schema::posts::dsl::{self, posts};\nuse crate::State;\n\npub fn post_router() -> Router<State> {\n    Router::new()\n        .on(\"/\", post(create_post))\n        .on(\"/:id\", get(get_post).put(update_post).delete(delete_post))\n}\n\nasync fn create_post(ctx: &mut Context<State>) -> Result {\n    let data: NewPost = ctx.read_json().await?;\n    let conn = ctx.get_conn().await?;\n    let post = ctx\n        .exec\n        .spawn_blocking(move || {\n            conn.transaction::<Post, Error, _>(|| {\n                diesel::insert_into(crate::schema::posts::table)\n                    .values(&data)\n                    .execute(&conn)?;\n                Ok(posts.order(dsl::id.desc()).first(&conn)?)\n            })\n        })\n        .await?;\n    ctx.resp.status = StatusCode::CREATED;\n    ctx.write_json(&post)\n}\n\nasync fn get_post(ctx: &mut Context<State>) -> Result {\n    let id: i32 = ctx.must_param(\"id\")?.parse()?;\n    match ctx\n        .first::<Post, _>(posts.find(id).filter(dsl::published.eq(true)))\n        .await?\n    {\n        None => throw!(StatusCode::NOT_FOUND, &format!(\"post({}) not found\", id)),\n        Some(post) => ctx.write_json(&post),\n    }\n}\n\nasync fn update_post(ctx: &mut Context<State>) -> Result {\n    let id: i32 = ctx.must_param(\"id\")?.parse()?;\n    let NewPost {\n        title,\n        body,\n        published,\n    } = ctx.read_json().await?;\n\n    match ctx.first::<Post, _>(posts.find(id)).await? {\n        None => throw!(StatusCode::NOT_FOUND, &format!(\"post({}) not found\", id)),\n        Some(post) => {\n            ctx.execute(diesel::update(posts.find(id)).set((\n                dsl::title.eq(title),\n                dsl::body.eq(body),\n                dsl::published.eq(published),\n            )))\n            .await?;\n            ctx.write_json(&post)\n        }\n    }\n}\n\nasync fn delete_post(ctx: &mut Context<State>) -> Result {\n    let id: i32 = ctx.must_param(\"id\")?.parse()?;\n    match ctx.first::<Post, _>(posts.find(id)).await? {\n        None => throw!(StatusCode::NOT_FOUND, &format!(\"post({}) not found\", id)),\n        Some(post) => {\n            ctx.execute(diesel::delete(posts.find(id))).await?;\n            ctx.write_json(&post)\n        }\n    }\n}\n"
  },
  {
    "path": "integration/diesel-example/src/lib.rs",
    "content": "#[macro_use]\nextern crate diesel;\n\nmod data_object;\nmod endpoints;\npub mod models;\npub mod schema;\n\nuse diesel::prelude::*;\nuse diesel::sqlite::SqliteConnection;\nuse roa_diesel::{make_pool, Pool};\n\n#[derive(Clone)]\npub struct State(pub Pool<SqliteConnection>);\n\nimpl AsRef<Pool<SqliteConnection>> for State {\n    fn as_ref(&self) -> &Pool<SqliteConnection> {\n        &self.0\n    }\n}\n\npub fn create_pool() -> anyhow::Result<State> {\n    let pool = make_pool(\":memory:\")?;\n    diesel::sql_query(\n        r\"\n        CREATE TABLE posts (\n          id INTEGER PRIMARY KEY,\n          title VARCHAR NOT NULL,\n          body TEXT NOT NULL,\n          published BOOLEAN NOT NULL DEFAULT 'f'\n        )\n    \",\n    )\n    .execute(&*pool.get()?)?;\n    Ok(State(pool))\n}\n\npub use endpoints::post_router;\n"
  },
  {
    "path": "integration/diesel-example/src/models.rs",
    "content": "use diesel::Queryable;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Queryable, Serialize, Deserialize)]\npub struct Post {\n    pub id: i32,\n    pub title: String,\n    pub body: String,\n    pub published: bool,\n}\n"
  },
  {
    "path": "integration/diesel-example/src/schema.rs",
    "content": "table! {\n    posts (id) {\n        id -> Integer,\n        title -> Text,\n        body -> Text,\n        published -> Bool,\n    }\n}\n"
  },
  {
    "path": "integration/diesel-example/tests/restful.rs",
    "content": "use diesel_example::models::Post;\nuse diesel_example::{create_pool, post_router};\nuse roa::http::StatusCode;\nuse roa::preload::*;\nuse roa::App;\nuse serde::Serialize;\nuse tracing::{debug, info};\nuse tracing_subscriber::EnvFilter;\n\n#[derive(Debug, Serialize, Copy, Clone)]\npub struct NewPost<'a> {\n    pub title: &'a str,\n    pub body: &'a str,\n    pub published: bool,\n}\n\nimpl PartialEq<Post> for NewPost<'_> {\n    fn eq(&self, other: &Post) -> bool {\n        self.title == other.title && self.body == other.body && self.published == other.published\n    }\n}\n\n#[tokio::test]\nasync fn test() -> anyhow::Result<()> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let app = App::state(create_pool()?).end(post_router().routes(\"/post\")?);\n    let (addr, server) = app.run()?;\n    tokio::task::spawn(server);\n    info!(\"server is running on {}\", addr);\n    let base_url = format!(\"http://{}/post\", addr);\n    let client = reqwest::Client::new();\n\n    // Not Found\n    let resp = client.get(&format!(\"{}/{}\", &base_url, 0)).send().await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n    debug!(\"{}/{} not found\", &base_url, 0);\n\n    // Create\n    let first_post = NewPost {\n        title: \"Hello\",\n        body: \"Welcome to roa-diesel\",\n        published: false,\n    };\n\n    let resp = client.post(&base_url).json(&first_post).send().await?;\n    assert_eq!(StatusCode::CREATED, resp.status());\n    let created_post: Post = resp.json().await?;\n    let id = created_post.id;\n    assert_eq!(&first_post, &created_post);\n\n    // Post isn't published, get nothing\n    let resp = client.get(&format!(\"{}/{}\", &base_url, id)).send().await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n\n    // Update\n    let second_post = NewPost {\n        published: true,\n        ..first_post\n    };\n    let resp = client\n        .put(&format!(\"{}/{}\", &base_url, id))\n        .json(&second_post)\n        .send()\n        .await?;\n    assert_eq!(StatusCode::OK, resp.status());\n\n    // Return old post\n    let updated_post: Post = resp.json().await?;\n    assert_eq!(&first_post, &updated_post);\n\n    // Get it\n    let resp = client.get(&format!(\"{}/{}\", &base_url, id)).send().await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let query_post: Post = resp.json().await?;\n    assert_eq!(&second_post, &query_post);\n\n    // Delete\n    let resp = client\n        .delete(&format!(\"{}/{}\", &base_url, id))\n        .send()\n        .await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let deleted_post: Post = resp.json().await?;\n    assert_eq!(&second_post, &deleted_post);\n\n    // Post is deleted, get nothing\n    let resp = client.get(&format!(\"{}/{}\", &base_url, id)).send().await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n    Ok(())\n}\n"
  },
  {
    "path": "integration/juniper-example/Cargo.toml",
    "content": "[package]\nname = \"juniper-example\"\nversion = \"0.1.0\"\nauthors = [\"Hexilee <hexileee@gmail.com>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\ndiesel = \"1.4\"\nroa = { path = \"../../roa\", features = [\"router\"] }\nroa-diesel = { path = \"../../roa-diesel\" }\nroa-juniper = { path = \"../../roa-juniper\" }\ndiesel-example = { path = \"../diesel-example\" }\ntokio = { version = \"1.15\", features = [\"full\"] }\ntracing = \"0.1\"\nserde = { version = \"1\", features = [\"derive\"] }\nfutures = \"0.3\"\njuniper = { version = \"0.15\", default-features = false }\ntracing-futures = \"0.2\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\nanyhow = \"1.0\""
  },
  {
    "path": "integration/juniper-example/README.md",
    "content": "```bash\nRUST_LOG=info cargo run\n```\n\nThen request http://127.0.0.1:8000, play with the GraphQL playground! "
  },
  {
    "path": "integration/juniper-example/src/main.rs",
    "content": "#[macro_use]\nextern crate diesel;\n\nmod models;\nmod schema;\nuse std::error::Error as StdError;\n\nuse diesel::prelude::*;\nuse diesel::result::Error;\nuse diesel_example::{create_pool, State};\nuse juniper::http::playground::playground_source;\nuse juniper::{\n    graphql_value, EmptySubscription, FieldError, FieldResult, GraphQLInputObject, RootNode,\n};\nuse roa::http::Method;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::router::{allow, get, Router};\nuse roa::App;\nuse roa_diesel::preload::*;\nuse roa_juniper::{GraphQL, JuniperContext};\nuse serde::Serialize;\nuse tracing::info;\nuse tracing_subscriber::EnvFilter;\n\nuse crate::models::Post;\nuse crate::schema::posts;\n\n#[derive(Debug, Insertable, Serialize, GraphQLInputObject)]\n#[table_name = \"posts\"]\n#[graphql(description = \"A new post\")]\nstruct NewPost {\n    title: String,\n    body: String,\n    published: bool,\n}\n\nstruct Query;\n\n#[juniper::graphql_object(\n    Context = JuniperContext<State>,\n)]\nimpl Query {\n    async fn post(\n        &self,\n        ctx: &JuniperContext<State>,\n        id: i32,\n        published: bool,\n    ) -> FieldResult<Post> {\n        use crate::schema::posts::dsl::{self, posts};\n        match ctx\n            .first(posts.find(id).filter(dsl::published.eq(published)))\n            .await?\n        {\n            Some(post) => Ok(post),\n            None => Err(FieldError::new(\n                \"post not found\",\n                graphql_value!({ \"status\": 404, \"id\": id }),\n            )),\n        }\n    }\n}\n\nstruct Mutation;\n\n#[juniper::graphql_object(\n    Context = JuniperContext<State>,\n)]\nimpl Mutation {\n    async fn create_post(\n        &self,\n        ctx: &JuniperContext<State>,\n        new_post: NewPost,\n    ) -> FieldResult<Post> {\n        use crate::schema::posts::dsl::{self, posts};\n        let conn = ctx.get_conn().await?;\n        let post = ctx\n            .exec\n            .spawn_blocking(move || {\n                conn.transaction::<Post, Error, _>(|| {\n                    diesel::insert_into(crate::schema::posts::table)\n                        .values(&new_post)\n                        .execute(&conn)?;\n                    Ok(posts.order(dsl::id.desc()).first(&conn)?)\n                })\n            })\n            .await?;\n        Ok(post)\n    }\n\n    async fn update_post(\n        &self,\n        id: i32,\n        ctx: &JuniperContext<State>,\n        new_post: NewPost,\n    ) -> FieldResult<Post> {\n        use crate::schema::posts::dsl::{self, posts};\n        match ctx.first(posts.find(id)).await? {\n            None => Err(FieldError::new(\n                \"post not found\",\n                graphql_value!({ \"status\": 404, \"id\": id }),\n            )),\n            Some(old_post) => {\n                let NewPost {\n                    title,\n                    body,\n                    published,\n                } = new_post;\n                ctx.execute(diesel::update(posts.find(id)).set((\n                    dsl::title.eq(title),\n                    dsl::body.eq(body),\n                    dsl::published.eq(published),\n                )))\n                .await?;\n                Ok(old_post)\n            }\n        }\n    }\n\n    async fn delete_post(&self, ctx: &JuniperContext<State>, id: i32) -> FieldResult<Post> {\n        use crate::schema::posts::dsl::posts;\n        match ctx.first(posts.find(id)).await? {\n            None => Err(FieldError::new(\n                \"post not found\",\n                graphql_value!({ \"status\": 404, \"id\": id }),\n            )),\n            Some(old_post) => {\n                ctx.execute(diesel::delete(posts.find(id))).await?;\n                Ok(old_post)\n            }\n        }\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let router = Router::new()\n        .on(\"/\", get(playground_source(\"/api\", None)))\n        .on(\n            \"/api\",\n            allow(\n                [Method::GET, Method::POST],\n                GraphQL(RootNode::new(Query, Mutation, EmptySubscription::new())),\n            ),\n        );\n    let app = App::state(create_pool()?)\n        .gate(logger)\n        .end(router.routes(\"/\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "integration/juniper-example/src/models.rs",
    "content": "use diesel::Queryable;\nuse juniper::GraphQLObject;\nuse serde::Deserialize;\n\n#[derive(Debug, Clone, Queryable, Deserialize, GraphQLObject)]\n#[graphql(description = \"A post\")]\npub struct Post {\n    pub id: i32,\n    pub title: String,\n    pub body: String,\n    pub published: bool,\n}\n"
  },
  {
    "path": "integration/juniper-example/src/schema.rs",
    "content": "table! {\n    posts (id) {\n        id -> Integer,\n        title -> Text,\n        body -> Text,\n        published -> Bool,\n    }\n}\n"
  },
  {
    "path": "integration/multipart-example/Cargo.toml",
    "content": "[package]\nname = \"multipart-example\"\nversion = \"0.1.0\"\nauthors = [\"Hexilee <hexileee@gmail.com>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nroa = { path = \"../../roa\", features = [\"router\", \"file\", \"multipart\"] }\ntokio = { version = \"1.15\", features = [\"full\"] }\ntracing = \"0.1\"\nfutures = \"0.3\"\ntracing-futures = \"0.2\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\nanyhow = \"1.0\"\n"
  },
  {
    "path": "integration/multipart-example/README.md",
    "content": "```bash\nRUST_LOG=info cargo run\n```\n\nThen visit `http://127.0.0.1:8000`, files will be stored in `./upload`."
  },
  {
    "path": "integration/multipart-example/assets/index.html",
    "content": "<html lang=\"en\">\n<head><title>Upload Test</title></head>\n<body>\n<form action=\"/file\" method=\"post\" enctype=\"multipart/form-data\">\n    <input type=\"file\" multiple name=\"file\"/>\n    <input type=\"submit\" value=\"Submit\">\n</form>\n</body>\n</html>"
  },
  {
    "path": "integration/multipart-example/src/main.rs",
    "content": "use std::error::Error as StdError;\nuse std::path::Path;\n\nuse roa::body::{DispositionType, PowerBody};\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::router::{get, post, Router};\nuse roa::{App, Context};\nuse tokio::fs::File;\nuse tokio::io::AsyncWriteExt;\nuse tracing::info;\nuse tracing_subscriber::EnvFilter;\n\nasync fn get_form(ctx: &mut Context) -> roa::Result {\n    ctx.write_file(\"./assets/index.html\", DispositionType::Inline)\n        .await\n}\n\nasync fn post_file(ctx: &mut Context) -> roa::Result {\n    let mut form = ctx.read_multipart().await?;\n    while let Some(mut field) = form.next_field().await? {\n        info!(\"{:?}\", field.content_type());\n        match field.file_name() {\n            None => continue, // ignore non-file field\n            Some(filename) => {\n                let path = Path::new(\"./upload\");\n                let mut file = File::create(path.join(filename)).await?;\n                while let Some(c) = field.chunk().await? {\n                    file.write_all(&c).await?;\n                }\n            }\n        }\n    }\n    Ok(())\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let router = Router::new()\n        .on(\"/\", get(get_form))\n        .on(\"/file\", post(post_file));\n    let app = App::new().gate(logger).end(router.routes(\"/\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n"
  },
  {
    "path": "integration/websocket-example/Cargo.toml",
    "content": "[package]\nname = \"websocket-example\"\nversion = \"0.1.0\"\nauthors = [\"Hexilee <hexileee@gmail.com>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nroa = { path = \"../../roa\", features = [\"router\", \"file\", \"websocket\"] }\ntokio = { version = \"1.15\", features = [\"full\"] }\ntracing = \"0.1\"\nfutures = \"0.3\"\nhttp = \"0.2\"\nslab = \"0.4\"\ntracing-futures = \"0.2\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\nanyhow = \"1.0\"\n\n[dev-dependencies]\ntokio-tungstenite = { version = \"0.15\", features = [\"connect\"] }\n"
  },
  {
    "path": "integration/websocket-example/README.md",
    "content": "WIP..."
  },
  {
    "path": "integration/websocket-example/src/main.rs",
    "content": "use std::borrow::Cow;\nuse std::error::Error as StdError;\nuse std::sync::Arc;\n\nuse futures::stream::{SplitSink, SplitStream};\nuse futures::{SinkExt, StreamExt};\nuse http::Method;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::router::{allow, RouteTable, Router, RouterError};\nuse roa::websocket::tungstenite::protocol::frame::coding::CloseCode;\nuse roa::websocket::tungstenite::protocol::frame::CloseFrame;\nuse roa::websocket::tungstenite::Error as WsError;\nuse roa::websocket::{Message, SocketStream, Websocket};\nuse roa::{App, Context};\nuse slab::Slab;\nuse tokio::sync::{Mutex, RwLock};\nuse tracing::{debug, error, info, warn};\nuse tracing_subscriber::EnvFilter;\n\ntype Sender = SplitSink<SocketStream, Message>;\ntype Channel = Slab<Mutex<Sender>>;\n#[derive(Clone)]\nstruct SyncChannel(Arc<RwLock<Channel>>);\n\nimpl SyncChannel {\n    fn new() -> Self {\n        Self(Arc::new(RwLock::new(Slab::new())))\n    }\n\n    async fn broadcast(&self, message: Message) {\n        let channel = self.0.read().await;\n        for (_, sender) in channel.iter() {\n            if let Err(err) = sender.lock().await.send(message.clone()).await {\n                error!(\"broadcast error: {}\", err);\n            }\n        }\n    }\n\n    async fn send(&self, index: usize, message: Message) {\n        if let Err(err) = self.0.read().await[index].lock().await.send(message).await {\n            error!(\"message send error: {}\", err)\n        }\n    }\n\n    async fn register(&self, sender: Sender) -> usize {\n        self.0.write().await.insert(Mutex::new(sender))\n    }\n\n    async fn deregister(&self, index: usize) -> Sender {\n        self.0.write().await.remove(index).into_inner()\n    }\n}\n\nasync fn handle_message(\n    ctx: &Context<SyncChannel>,\n    index: usize,\n    mut receiver: SplitStream<SocketStream>,\n) -> Result<(), WsError> {\n    while let Some(message) = receiver.next().await {\n        let message = message?;\n        match message {\n            Message::Close(frame) => {\n                debug!(\"websocket connection close: {:?}\", frame);\n                break;\n            }\n            Message::Ping(data) => ctx.send(index, Message::Pong(data)).await,\n            Message::Pong(data) => warn!(\"ignored pong: {:?}\", data),\n            msg => ctx.broadcast(msg).await,\n        }\n    }\n    Ok(())\n}\n\nfn route(prefix: &'static str) -> Result<RouteTable<SyncChannel>, RouterError> {\n    Router::new()\n        .on(\n            \"/chat\",\n            allow(\n                [Method::GET],\n                Websocket::new(|ctx: Context<SyncChannel>, stream| async move {\n                    let (sender, receiver) = stream.split();\n                    let index = ctx.register(sender).await;\n                    let result = handle_message(&ctx, index, receiver).await;\n                    let mut sender = ctx.deregister(index).await;\n                    if let Err(err) = result {\n                        let result = sender\n                            .send(Message::Close(Some(CloseFrame {\n                                code: CloseCode::Invalid,\n                                reason: Cow::Owned(err.to_string()),\n                            })))\n                            .await;\n                        if let Err(err) = result {\n                            warn!(\"send close message error: {}\", err)\n                        }\n                    }\n                }),\n            ),\n        )\n        .routes(prefix)\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::from_default_env())\n        .try_init()\n        .map_err(|err| anyhow::anyhow!(\"fail to init tracing subscriber: {}\", err))?;\n\n    let app = App::state(SyncChannel::new()).gate(logger).end(route(\"/\")?);\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use roa::preload::*;\n    use tokio_tungstenite::connect_async;\n\n    use super::{route, App, Message, SinkExt, StdError, StreamExt, SyncChannel};\n\n    #[tokio::test]\n    async fn echo() -> Result<(), Box<dyn StdError>> {\n        let channel = SyncChannel::new();\n        let app = App::state(channel.clone()).end(route(\"/\")?);\n        let (addr, server) = app.run()?;\n        tokio::task::spawn(server);\n        let (ws_stream, _) = connect_async(format!(\"ws://{}/chat\", addr)).await?;\n        let (mut sender, mut recv) = ws_stream.split();\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        assert_eq!(1, channel.0.read().await.len());\n\n        // ping\n        sender\n            .send(Message::Ping(b\"Hello, World!\".to_vec()))\n            .await?;\n        let msg = recv.next().await.unwrap()?;\n        assert!(msg.is_pong());\n        assert_eq!(b\"Hello, World!\".as_ref(), msg.into_data().as_slice());\n\n        // close\n        sender.send(Message::Close(None)).await?;\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        assert_eq!(0, channel.0.read().await.len());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn broadcast() -> Result<(), Box<dyn StdError>> {\n        let channel = SyncChannel::new();\n        let app = App::state(channel.clone()).end(route(\"/\")?);\n        let (addr, server) = app.run()?;\n        tokio::task::spawn(server);\n        let url = format!(\"ws://{}/chat\", addr);\n        for _ in 0..100 {\n            let url = url.clone();\n            tokio::task::spawn(async move {\n                if let Ok((ws_stream, _)) = connect_async(url).await {\n                    let (mut sender, mut recv) = ws_stream.split();\n                    if let Some(Ok(message)) = recv.next().await {\n                        assert!(sender.send(message).await.is_ok());\n                    }\n                    tokio::time::sleep(Duration::from_secs(1)).await;\n                    assert!(sender.send(Message::Close(None)).await.is_ok());\n                }\n            });\n        }\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        assert_eq!(100, channel.0.read().await.len());\n\n        let (ws_stream, _) = connect_async(url).await?;\n        let (mut sender, mut recv) = ws_stream.split();\n        assert!(sender\n            .send(Message::Text(\"Hello, World!\".to_string()))\n            .await\n            .is_ok());\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        assert_eq!(1, channel.0.read().await.len());\n\n        let mut counter = 0i32;\n        while let Some(item) = recv.next().await {\n            if let Ok(Message::Text(message)) = item {\n                assert_eq!(\"Hello, World!\", message);\n            }\n            counter += 1;\n            if counter == 101 {\n                break;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/Cargo.toml",
    "content": "[package]\nname = \"roa\"\nversion = \"0.6.1\"\nauthors = [\"Hexilee <i@hexilee.me>\"]\nedition = \"2018\"\nlicense = \"MIT\"\nreadme = \"./README.md\"\nrepository = \"https://github.com/Hexilee/roa\"\ndocumentation = \"https://docs.rs/roa\"\nhomepage = \"https://github.com/Hexilee/roa/wiki\"\ndescription = \"\"\"\nasync web framework inspired by koajs, lightweight but powerful.\n\"\"\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\"]\ncategories = [\"network-programming\", \"asynchronous\",\n              \"web-programming::http-server\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"docs\"]\nrustdoc-args = [\"--cfg\", \"feature=\\\"docs\\\"\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[badges]\ncodecov = { repository = \"Hexilee/roa\" }\n\n[dependencies]\ntracing = { version = \"0.1\", features = [\"log\"] }\nfutures = \"0.3\"\nbytesize = \"1.0\"\nasync-trait = \"0.1.51\"\nurl = \"2.2\"\npercent-encoding = \"2.1\"\nbytes = \"1.1\"\nheaders = \"0.3\"\ntokio = \"1.15\"\ntokio-util = { version = \"0.6.9\", features = [\"io\"] }\nonce_cell = \"1.8\"\nhyper = { version = \"0.14\", default-features = false, features = [\"stream\", \"server\", \"http1\", \"http2\"] }\nroa-core = { path = \"../roa-core\", version = \"0.6\" }\n\ncookie = { version = \"0.15\", features = [\"percent-encode\"], optional = true }\njsonwebtoken = { version = \"7.2\", optional = true }\nserde = { version = \"1\", optional = true }\nserde_json = { version = \"1.0\", optional = true }\nasync-compression = { version = \"0.3.8\", features = [\"all-algorithms\", \"futures-io\"], optional = true }\n\n# router\nradix_trie = { version = \"0.2.1\", optional = true }\nregex = { version = \"1.5\", optional = true }\n\n# body\naskama = { version = \"0.10\", optional = true }\ndoc-comment = { version = \"0.3.3\", optional = true }\nserde_urlencoded = { version = \"0.7\", optional = true }\nmime_guess = { version = \"2.0\", optional = true }\nmulter = { version = \"2.0\", optional = true }\nmime = { version = \"0.3\", optional = true }\n\n# websocket\ntokio-tungstenite = { version = \"0.15.0\", default-features = false, optional = true }\n\n\n# tls\nrustls = { version = \"0.20\", optional = true }\ntokio-rustls = { version = \"0.23\", optional = true }\nrustls-pemfile = { version = \"0.2\", optional = true }\n\n# jsonrpc\njsonrpc-v2 = { version = \"0.10\", default-features = false, features = [\"bytes-v10\"], optional = true }\n\n[dev-dependencies]\ntokio = { version = \"1.15\", features = [\"full\"] }\ntokio-native-tls = \"0.3\"\nhyper-tls = \"0.5\"\nreqwest = { version = \"0.11\", features = [\"json\", \"cookies\", \"gzip\", \"multipart\"] }\npretty_env_logger = \"0.4\"\nserde = { version = \"1\", features = [\"derive\"] }\ntest-case = \"1.2\"\nslab = \"0.4.5\"\nmultimap = \"0.8\"\nhyper = \"0.14\"\nmime = \"0.3\"\nencoding = \"0.2\"\naskama = \"0.10\"\nanyhow = \"1.0\"\n\n[features]\ndefault = [\"async_rt\"]\nfull = [\n    \"default\",\n    \"json\",\n    \"urlencoded\",\n    \"file\",\n    \"multipart\",\n    \"template\",\n    \"tls\",\n    \"router\",\n    \"jwt\",\n    \"cookies\",\n    \"compress\",\n    \"websocket\",\n    \"jsonrpc\",\n]\n\ndocs = [\"full\", \"roa-core/docs\"]\nruntime = [\"roa-core/runtime\"]\njson = [\"serde\", \"serde_json\"]\nmultipart = [\"multer\", \"mime\"]\nurlencoded = [\"serde\", \"serde_urlencoded\"]\nfile = [\"mime_guess\", \"tokio/fs\"]\ntemplate = [\"askama\"]\ntcp = [\"tokio/net\", \"tokio/time\"]\ntls = [\"rustls\", \"tokio-rustls\", \"rustls-pemfile\"]\ncookies = [\"cookie\"]\njwt = [\"jsonwebtoken\", \"serde\", \"serde_json\"]\nrouter = [\"radix_trie\", \"regex\", \"doc-comment\"]\nwebsocket = [\"tokio-tungstenite\"]\ncompress = [\"async-compression\"]\nasync_rt = [\"runtime\", \"tcp\"]\njsonrpc = [\"jsonrpc-v2\"]\n"
  },
  {
    "path": "roa/README.md",
    "content": "[![Build status](https://img.shields.io/travis/Hexilee/roa/master.svg)](https://travis-ci.org/Hexilee/roa)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa)\n[![Rust Docs](https://docs.rs/roa/badge.svg)](https://docs.rs/roa)\n[![Crate version](https://img.shields.io/crates/v/roa.svg)](https://crates.io/crates/roa)\n[![Download](https://img.shields.io/crates/d/roa.svg)](https://crates.io/crates/roa)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\n### Introduction\n\nRoa is an async web framework inspired by koajs, lightweight but powerful.\n\n### Application\n\nA Roa application is a structure composing and executing middlewares and an endpoint in a stack-like manner.\n\nThe obligatory hello world application:\n\n```rust,no_run\nuse roa::App;\nuse roa::preload::*;\nuse tracing::info;\nuse std::error::Error as StdError;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    let app = App::new().end(\"Hello, World\");\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n```\n\n#### Endpoint\n\nAn endpoint is a request handler.\n\nThere are some build-in endpoints in roa.\n\n- Functional endpoint\n\n    A normal functional endpoint is an async function with signature:\n    `async fn(&mut Context) -> Result`.\n    \n    ```rust\n    use roa::{App, Context, Result};\n    \n    async fn endpoint(ctx: &mut Context) -> Result {\n        Ok(())\n    }\n    \n    let app = App::new().end(endpoint);\n    ```\n  \n- Ok endpoint\n\n    `()` is an endpoint always return `Ok(())`\n    \n    ```rust\n    let app = roa::App::new().end(());\n    ```\n\n- Status endpoint\n\n    `Status` is an endpoint always return `Err(Status)`\n    \n    ```rust\n    use roa::{App, status};\n    use roa::http::StatusCode;\n    let app = App::new().end(status!(StatusCode::BAD_REQUEST));\n    ```\n\n- String endpoint\n\n    Write string to body.\n    \n    ```rust\n    use roa::App;\n    \n    let app = App::new().end(\"Hello, world\"); // static slice\n    let app = App::new().end(\"Hello, world\".to_owned()); // string\n    ```\n\n- Redirect endpoint\n\n    Redirect to an uri.\n    \n    ```rust\n    use roa::App;\n    use roa::http::Uri;\n    \n    let app = App::new().end(\"/target\".parse::<Uri>().unwrap());\n    ```\n\n\n#### Cascading\nLike koajs, middleware suspends and passes control to \"downstream\" by invoking `next.await`.\nThen control flows back \"upstream\" when `next.await` returns.\n\nThe following example responds with \"Hello World\",\nhowever first the request flows through the x-response-time and logging middleware to mark\nwhen the request started, then continue to yield control through the endpoint.\nWhen a middleware invokes next the function suspends and passes control to the next middleware or endpoint.\nAfter the endpoint is called,\nthe stack will unwind and each middleware is resumed to perform\nits upstream behaviour.\n\n```rust,no_run\nuse roa::{App, Context, Next};\nuse roa::preload::*;\nuse tracing::info;\nuse std::error::Error as StdError;\nuse std::time::Instant;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn StdError>> {\n    let app = App::new()\n        .gate(logger)\n        .gate(x_response_time)\n        .end(\"Hello, World\");\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n\nasync fn logger(ctx: &mut Context, next: Next<'_>) -> roa::Result {\n    next.await?;\n    let rt = ctx.load::<String>(\"x-response-time\").unwrap();\n    info!(\"{} {} - {}\", ctx.method(), ctx.uri(), rt.as_str());\n    Ok(())\n}\n\nasync fn x_response_time(ctx: &mut Context, next: Next<'_>) -> roa::Result {\n    let start = Instant::now();\n    next.await?;\n    let ms = start.elapsed().as_millis();\n    ctx.store(\"x-response-time\", format!(\"{}ms\", ms));\n    Ok(())\n}\n\n```\n\n### Status Handling\n\nYou can catch or straightly throw a status returned by next.\n\n```rust,no_run\nuse roa::{App, Context, Next, status};\nuse roa::preload::*;\nuse roa::http::StatusCode;\nuse tokio::task::spawn;\nuse tracing::info;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let app = App::new()\n        .gate(catch)\n        .gate(not_catch)\n        .end(status!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\"));\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    Ok(())\n}\n\nasync fn catch(_ctx: &mut Context, next: Next<'_>) -> roa::Result {\n    // catch\n    if let Err(status) = next.await {\n        // teapot is ok\n        if status.status_code != StatusCode::IM_A_TEAPOT {\n            return Err(status);\n        }\n    }\n    Ok(())\n}\n\nasync fn not_catch(ctx: &mut Context, next: Next<'_>) -> roa::Result {\n    next.await?; // just throw\n    unreachable!()\n}\n```\n\n#### status_handler\nApp has an status_handler to handle status thrown by the top middleware.\nThis is the status_handler:\n\n```rust,no_run\nuse roa::{Context, Status};\npub fn status_handler<S>(ctx: &mut Context<S>, status: Status) {\n    ctx.resp.status = status.status_code;\n    if status.expose {\n        ctx.resp.write(status.message);\n    } else {\n        tracing::error!(\"{}\", status);\n    }\n}\n```\n\n### Router.\nRoa provides a configurable and nestable router.\n\n```rust,no_run\nuse roa::preload::*;\nuse roa::router::{Router, get};\nuse roa::{App, Context};\nuse tokio::task::spawn;\nuse tracing::info;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let router = Router::new()\n        .on(\"/:id\", get(end)); // get dynamic \"/:id\"\n    let app = App::new()\n        .end(router.routes(\"/user\")?); // route with prefix \"/user\"\n    app.listen(\"127.0.0.1:8000\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;\n    \n    Ok(())\n}\n\nasync fn end(ctx: &mut Context) -> roa::Result {\n    // get \"/user/1\", then id == 1.\n    let id: u64 = ctx.must_param(\"id\")?.parse()?;\n    // do something\n    Ok(())\n}\n```\n\n### Query\n\nRoa provides a middleware `query_parser`.\n\n```rust,no_run\nuse roa::preload::*;\nuse roa::query::query_parser;\nuse roa::{App, Context};\nuse tokio::task::spawn;\nuse tracing::info;\n\nasync fn must(ctx: &mut Context) -> roa::Result {\n    // request \"/?id=1\", then id == 1.\n    let id: u64 = ctx.must_query(\"id\")?.parse()?;\n    Ok(())\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let app = App::new()\n        .gate(query_parser)\n        .end(must);\n    app.listen(\"127.0.0.1:8080\", |addr| {\n        info!(\"Server is listening on {}\", addr)\n    })?\n    .await?;     \n    Ok(())\n}\n```\n\n### Other modules\n\n- body: dealing with body more conveniently.\n- compress: supports transparent content compression.\n- cookie: cookies getter or setter.\n- cors: CORS support.\n- forward: \"X-Forwarded-*\" parser.\n- jwt: json web token support.\n- logger: a logger middleware.\n- tls: https supports.\n- websocket: websocket supports.\n"
  },
  {
    "path": "roa/src/body/file/content_disposition.rs",
    "content": "use std::convert::{TryFrom, TryInto};\nuse std::fmt::{self, Display, Formatter};\n\nuse percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};\n\nuse super::help::bug_report;\nuse crate::http::header::HeaderValue;\nuse crate::Status;\n\n// This encode set is used for HTTP header values and is defined at\n// https://tools.ietf.org/html/rfc5987#section-3.2\nconst 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\n/// Type of content-disposition, inline or attachment\n#[derive(Clone, Debug, PartialEq)]\npub enum DispositionType {\n    /// Inline implies default processing\n    Inline,\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\n/// A structure to generate value of \"Content-Disposition\"\npub struct ContentDisposition {\n    typ: DispositionType,\n    encoded_filename: Option<String>,\n}\n\nimpl ContentDisposition {\n    /// Construct by disposition type and optional filename.\n    #[inline]\n    pub(crate) fn new(typ: DispositionType, filename: Option<&str>) -> Self {\n        Self {\n            typ,\n            encoded_filename: filename\n                .map(|name| utf8_percent_encode(name, HTTP_VALUE).to_string()),\n        }\n    }\n}\n\nimpl TryFrom<ContentDisposition> for HeaderValue {\n    type Error = Status;\n    #[inline]\n    fn try_from(value: ContentDisposition) -> Result<Self, Self::Error> {\n        value\n            .to_string()\n            .try_into()\n            .map_err(|err| bug_report(format!(\"{}\\nNot a valid header value\", err)))\n    }\n}\n\nimpl Display for ContentDisposition {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        match &self.encoded_filename {\n            None => f.write_fmt(format_args!(\"{}\", self.typ)),\n            Some(name) => f.write_fmt(format_args!(\n                \"{}; filename={}; filename*=UTF-8''{}\",\n                self.typ, name, name\n            )),\n        }\n    }\n}\n\nimpl Display for DispositionType {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        match self {\n            DispositionType::Inline => f.write_str(\"inline\"),\n            DispositionType::Attachment => f.write_str(\"attachment\"),\n        }\n    }\n}\n"
  },
  {
    "path": "roa/src/body/file/help.rs",
    "content": "use crate::http::StatusCode;\nuse crate::Status;\n\nconst BUG_HELP: &str =\n    r\"This is a bug of roa::body::file, please report it to https://github.com/Hexilee/roa.\";\n\n#[inline]\npub fn bug_report(message: impl ToString) -> Status {\n    Status::new(\n        StatusCode::INTERNAL_SERVER_ERROR,\n        format!(\"{}\\n{}\", message.to_string(), BUG_HELP),\n        false,\n    )\n}\n"
  },
  {
    "path": "roa/src/body/file.rs",
    "content": "mod content_disposition;\nmod help;\n\nuse std::convert::TryInto;\nuse std::path::Path;\n\nuse content_disposition::ContentDisposition;\npub use content_disposition::DispositionType;\nuse tokio::fs::File;\n\nuse crate::{http, Context, Result, State};\n\n/// Write file to response body then set \"Content-Type\" and \"Context-Disposition\".\n#[inline]\npub async fn write_file<S: State>(\n    ctx: &mut Context<S>,\n    path: impl AsRef<Path>,\n    typ: DispositionType,\n) -> Result {\n    let path = path.as_ref();\n    ctx.resp.write_reader(File::open(path).await?);\n\n    if let Some(filename) = path.file_name() {\n        ctx.resp.headers.insert(\n            http::header::CONTENT_TYPE,\n            mime_guess::from_path(&filename)\n                .first_or_octet_stream()\n                .as_ref()\n                .parse()\n                .map_err(help::bug_report)?,\n        );\n\n        let name = filename.to_string_lossy();\n        let content_disposition = ContentDisposition::new(typ, Some(&name));\n        ctx.resp.headers.insert(\n            http::header::CONTENT_DISPOSITION,\n            content_disposition.try_into()?,\n        );\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "roa/src/body.rs",
    "content": "//! This module provides a context extension `PowerBody`.\n//!\n//! ### Read/write body in a easier way.\n//!\n//! The `roa_core` provides several methods to read/write body.\n//!\n//! ```rust\n//! use roa::{Context, Result};\n//! use tokio::io::AsyncReadExt;\n//! use tokio::fs::File;\n//!\n//! async fn get(ctx: &mut Context) -> Result {\n//!     let mut data = String::new();\n//!     // implements futures::AsyncRead.\n//!     ctx.req.reader().read_to_string(&mut data).await?;\n//!     println!(\"data: {}\", data);\n//!\n//!     // although body is empty now...\n//!     let stream = ctx.req.stream();\n//!     ctx.resp\n//!         // echo\n//!        .write_stream(stream)\n//!        // write object implementing futures::AsyncRead\n//!        .write_reader(File::open(\"assets/author.txt\").await?)\n//!        // write reader with specific chunk size\n//!        .write_chunk(File::open(\"assets/author.txt\").await?, 1024)\n//!        // write text\n//!        .write(\"I am Roa.\")\n//!        .write(b\"I am Roa.\".as_ref());\n//!     Ok(())\n//! }\n//! ```\n//!\n//! These methods are useful, but they do not deal with headers and (de)serialization.\n//!\n//! The `PowerBody` provides more powerful methods to handle it.\n//!\n//! ```rust\n//! use roa::{Context, Result};\n//! use roa::body::{PowerBody, DispositionType::*};\n//! use serde::{Serialize, Deserialize};\n//! use askama::Template;\n//! use tokio::fs::File;\n//!\n//! #[derive(Debug, Serialize, Deserialize, Template)]\n//! #[template(path = \"user.html\")]\n//! struct User {\n//!     id: u64,\n//!     name: String,\n//! }\n//!\n//! async fn get(ctx: &mut Context) -> Result {\n//!     // read as bytes.\n//!     let data = ctx.read().await?;\n//!\n//!     // deserialize as json.\n//!     let user: User = ctx.read_json().await?;\n//!\n//!     // deserialize as x-form-urlencoded.\n//!     let user: User = ctx.read_form().await?;\n//!\n//!     // serialize object and write it to body,\n//!     // set \"Content-Type\"\n//!     ctx.write_json(&user)?;\n//!\n//!     // open file and write it to body,\n//!     // set \"Content-Type\" and \"Content-Disposition\"\n//!     ctx.write_file(\"assets/welcome.html\", Inline).await?;\n//!\n//!     // write text,\n//!     // set \"Content-Type\"\n//!     ctx.write(\"Hello, World!\");\n//!\n//!     // write object implementing AsyncRead,\n//!     // set \"Content-Type\"\n//!     ctx.write_reader(File::open(\"assets/author.txt\").await?);\n//!\n//!     // render html template, based on [askama](https://github.com/djc/askama).\n//!     // set \"Content-Type\"\n//!     ctx.render(&user)?;\n//!     Ok(())\n//! }\n//! ```\n\n#[cfg(feature = \"template\")]\nuse askama::Template;\nuse bytes::Bytes;\nuse headers::{ContentLength, ContentType, HeaderMapExt};\nuse tokio::io::{AsyncRead, AsyncReadExt};\n\nuse crate::{async_trait, Context, Result, State};\n#[cfg(feature = \"file\")]\nmod file;\n#[cfg(feature = \"file\")]\nuse file::write_file;\n#[cfg(feature = \"file\")]\npub use file::DispositionType;\n#[cfg(feature = \"multipart\")]\npub use multer::Multipart;\n#[cfg(any(feature = \"json\", feature = \"urlencoded\"))]\nuse serde::de::DeserializeOwned;\n#[cfg(feature = \"json\")]\nuse serde::Serialize;\n\n/// A context extension to read/write body more simply.\n#[async_trait]\npub trait PowerBody {\n    /// read request body as Bytes.\n    async fn read(&mut self) -> Result<Vec<u8>>;\n\n    /// read request body as \"json\".\n    #[cfg(feature = \"json\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"json\")))]\n    async fn read_json<B>(&mut self) -> Result<B>\n    where\n        B: DeserializeOwned;\n\n    /// read request body as \"urlencoded form\".\n    #[cfg(feature = \"urlencoded\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"urlencoded\")))]\n    async fn read_form<B>(&mut self) -> Result<B>\n    where\n        B: DeserializeOwned;\n\n    /// read request body as \"multipart form\".\n    #[cfg(feature = \"multipart\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"multipart\")))]\n    async fn read_multipart(&mut self) -> Result<Multipart>;\n\n    /// write object to response body as \"application/json\"\n    #[cfg(feature = \"json\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"json\")))]\n    fn write_json<B>(&mut self, data: &B) -> Result\n    where\n        B: Serialize;\n\n    /// write object to response body as \"text/html; charset=utf-8\"\n    #[cfg(feature = \"template\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"template\")))]\n    fn render<B>(&mut self, data: &B) -> Result\n    where\n        B: Template;\n\n    /// write object to response body as \"text/plain\"\n    fn write<B>(&mut self, data: B)\n    where\n        B: Into<Bytes>;\n\n    /// write object to response body as \"application/octet-stream\"\n    fn write_reader<B>(&mut self, reader: B)\n    where\n        B: 'static + AsyncRead + Unpin + Sync + Send;\n\n    /// write object to response body as extension name of file\n    #[cfg(feature = \"file\")]\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"file\")))]\n    async fn write_file<P>(&mut self, path: P, typ: DispositionType) -> Result\n    where\n        P: Send + AsRef<std::path::Path>;\n}\n\n#[async_trait]\nimpl<S: State> PowerBody for Context<S> {\n    #[inline]\n    async fn read(&mut self) -> Result<Vec<u8>> {\n        let mut data = match self.req.headers.typed_get::<ContentLength>() {\n            Some(hint) => Vec::with_capacity(hint.0 as usize),\n            None => Vec::new(),\n        };\n        self.req.reader().read_to_end(&mut data).await?;\n        Ok(data)\n    }\n\n    #[cfg(feature = \"json\")]\n    #[inline]\n    async fn read_json<B>(&mut self) -> Result<B>\n    where\n        B: DeserializeOwned,\n    {\n        use crate::http::StatusCode;\n        use crate::status;\n\n        let data = self.read().await?;\n        serde_json::from_slice(&data).map_err(|err| status!(StatusCode::BAD_REQUEST, err))\n    }\n\n    #[cfg(feature = \"urlencoded\")]\n    #[inline]\n    async fn read_form<B>(&mut self) -> Result<B>\n    where\n        B: DeserializeOwned,\n    {\n        use crate::http::StatusCode;\n        use crate::status;\n        let data = self.read().await?;\n        serde_urlencoded::from_bytes(&data).map_err(|err| status!(StatusCode::BAD_REQUEST, err))\n    }\n\n    #[cfg(feature = \"multipart\")]\n    async fn read_multipart(&mut self) -> Result<Multipart> {\n        use headers::{ContentType, HeaderMapExt};\n\n        use crate::http::StatusCode;\n\n        // Verify that the request is 'Content-Type: multipart/*'.\n        let typ: mime::Mime = self\n            .req\n            .headers\n            .typed_get::<ContentType>()\n            .ok_or_else(|| crate::status!(StatusCode::BAD_REQUEST, \"fail to get content-type\"))?\n            .into();\n        let boundary = typ\n            .get_param(mime::BOUNDARY)\n            .ok_or_else(|| crate::status!(StatusCode::BAD_REQUEST, \"fail to get boundary\"))?\n            .as_str();\n        Ok(Multipart::new(self.req.stream(), boundary))\n    }\n\n    #[cfg(feature = \"json\")]\n    #[inline]\n    fn write_json<B>(&mut self, data: &B) -> Result\n    where\n        B: Serialize,\n    {\n        self.resp.write(serde_json::to_vec(data)?);\n        self.resp.headers.typed_insert(ContentType::json());\n        Ok(())\n    }\n\n    #[cfg(feature = \"template\")]\n    #[inline]\n    fn render<B>(&mut self, data: &B) -> Result\n    where\n        B: Template,\n    {\n        self.resp.write(data.render()?);\n        self.resp\n            .headers\n            .typed_insert::<ContentType>(mime::TEXT_HTML_UTF_8.into());\n        Ok(())\n    }\n\n    #[inline]\n    fn write<B>(&mut self, data: B)\n    where\n        B: Into<Bytes>,\n    {\n        self.resp.write(data);\n        self.resp.headers.typed_insert(ContentType::text());\n    }\n\n    #[inline]\n    fn write_reader<B>(&mut self, reader: B)\n    where\n        B: 'static + AsyncRead + Unpin + Sync + Send,\n    {\n        self.resp.write_reader(reader);\n        self.resp.headers.typed_insert(ContentType::octet_stream());\n    }\n\n    #[cfg(feature = \"file\")]\n    #[inline]\n    async fn write_file<P>(&mut self, path: P, typ: DispositionType) -> Result\n    where\n        P: Send + AsRef<std::path::Path>,\n    {\n        write_file(self, path, typ).await\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use std::error::Error;\n\n    use askama::Template;\n    use http::header::CONTENT_TYPE;\n    use http::StatusCode;\n    use serde::{Deserialize, Serialize};\n    use tokio::fs::File;\n    use tokio::task::spawn;\n\n    use super::PowerBody;\n    use crate::tcp::Listener;\n    use crate::{http, App, Context};\n\n    #[derive(Debug, Deserialize)]\n    struct UserDto {\n        id: u64,\n        name: String,\n    }\n\n    #[derive(Debug, Serialize, Hash, Eq, PartialEq, Clone, Template)]\n    #[template(path = \"user.html\")]\n    struct User<'a> {\n        id: u64,\n        name: &'a str,\n    }\n\n    impl PartialEq<UserDto> for User<'_> {\n        fn eq(&self, other: &UserDto) -> bool {\n            self.id == other.id && self.name == other.name\n        }\n    }\n\n    #[allow(dead_code)]\n    const USER: User = User {\n        id: 0,\n        name: \"Hexilee\",\n    };\n\n    #[cfg(feature = \"json\")]\n    #[tokio::test]\n    async fn read_json() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            let user: UserDto = ctx.read_json().await?;\n            assert_eq!(USER, user);\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .json(&USER)\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[cfg(feature = \"urlencoded\")]\n    #[tokio::test]\n    async fn read_form() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            let user: UserDto = ctx.read_form().await?;\n            assert_eq!(USER, user);\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .form(&USER)\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[cfg(feature = \"template\")]\n    #[tokio::test]\n    async fn render() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            ctx.render(&USER)\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert_eq!(\"text/html; charset=utf-8\", resp.headers()[CONTENT_TYPE]);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn write() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            ctx.write(\"Hello, World!\");\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert_eq!(\"text/plain\", resp.headers()[CONTENT_TYPE]);\n        assert_eq!(\"Hello, World!\", resp.text().await?);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn write_octet() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            ctx.write_reader(File::open(\"../assets/author.txt\").await?);\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert_eq!(\n            mime::APPLICATION_OCTET_STREAM.as_ref(),\n            resp.headers()[CONTENT_TYPE]\n        );\n        assert_eq!(\"Hexilee\", resp.text().await?);\n        Ok(())\n    }\n\n    #[cfg(feature = \"multipart\")]\n    mod multipart {\n        use std::error::Error as StdError;\n\n        use reqwest::multipart::{Form, Part};\n        use reqwest::Client;\n        use tokio::fs::read;\n\n        use crate::body::PowerBody;\n        use crate::http::header::CONTENT_TYPE;\n        use crate::http::StatusCode;\n        use crate::router::{post, Router};\n        use crate::tcp::Listener;\n        use crate::{throw, App, Context};\n\n        const FILE_PATH: &str = \"../assets/author.txt\";\n        const FILE_NAME: &str = \"author.txt\";\n        const FIELD_NAME: &str = \"file\";\n\n        async fn post_file(ctx: &mut Context) -> crate::Result {\n            let mut form = ctx.read_multipart().await?;\n            while let Some(field) = form.next_field().await? {\n                match (field.file_name(), field.name()) {\n                    (Some(filename), Some(name)) => {\n                        assert_eq!(FIELD_NAME, name);\n                        assert_eq!(FILE_NAME, filename);\n                        let content = field.bytes().await?;\n                        let expected_content = read(FILE_PATH).await?;\n                        assert_eq!(&expected_content, &content);\n                    }\n                    _ => throw!(\n                        StatusCode::BAD_REQUEST,\n                        format!(\"invalid field: {:?}\", field)\n                    ),\n                }\n            }\n            Ok(())\n        }\n\n        #[tokio::test]\n        async fn upload() -> Result<(), Box<dyn StdError>> {\n            let router = Router::new().on(\"/file\", post(post_file));\n            let app = App::new().end(router.routes(\"/\")?);\n            let (addr, server) = app.run()?;\n            tokio::task::spawn(server);\n\n            // client\n            let url = format!(\"http://{}/file\", addr);\n            let client = Client::new();\n            let form = Form::new().part(\n                FIELD_NAME,\n                Part::bytes(read(FILE_PATH).await?).file_name(FILE_NAME),\n            );\n            let boundary = form.boundary().to_string();\n            let resp = client\n                .post(&url)\n                .multipart(form)\n                .header(\n                    CONTENT_TYPE,\n                    format!(r#\"multipart/form-data; boundary=\"{}\"\"#, boundary),\n                )\n                .send()\n                .await?;\n            assert_eq!(StatusCode::OK, resp.status());\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "roa/src/compress.rs",
    "content": "//! This module provides a middleware `Compress`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::compress::{Compress, Level};\n//! use roa::body::DispositionType::*;\n//! use roa::{App, Context};\n//! use roa::preload::*;\n//! use std::error::Error;\n//!\n//! async fn end(ctx: &mut Context) -> roa::Result {\n//!     ctx.write_file(\"../assets/welcome.html\", Inline).await\n//! }\n//! # #[tokio::main]\n//! # async fn main() -> Result<(), Box<dyn Error>> {\n//! let mut app = App::new().gate(Compress(Level::Fastest)).end(end);\n//! let (addr, server) = app.run()?;\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n\nuse async_compression::tokio::bufread::{BrotliEncoder, GzipEncoder, ZlibEncoder, ZstdEncoder};\npub use async_compression::Level;\nuse tokio_util::io::StreamReader;\n\nuse crate::http::header::{HeaderMap, ACCEPT_ENCODING, CONTENT_ENCODING};\nuse crate::http::{HeaderValue, StatusCode};\nuse crate::{async_trait, status, Context, Middleware, Next, Result};\n\n/// A middleware to negotiate with client and compress response body automatically,\n/// supports gzip, deflate, brotli, zstd and identity.\n#[derive(Debug, Copy, Clone)]\npub struct Compress(pub Level);\n\n/// Encodings to use.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\nenum Encoding {\n    /// The Gzip encoding.\n    Gzip,\n    /// The Deflate encoding.\n    Deflate,\n    /// The Brotli encoding.\n    Brotli,\n    /// The Zstd encoding.\n    Zstd,\n    /// No encoding.\n    Identity,\n}\n\nimpl Encoding {\n    /// Parses a given string into its corresponding encoding.\n    fn parse(s: &str) -> Result<Option<Encoding>> {\n        match s {\n            \"gzip\" => Ok(Some(Encoding::Gzip)),\n            \"deflate\" => Ok(Some(Encoding::Deflate)),\n            \"br\" => Ok(Some(Encoding::Brotli)),\n            \"zstd\" => Ok(Some(Encoding::Zstd)),\n            \"identity\" => Ok(Some(Encoding::Identity)),\n            \"*\" => Ok(None),\n            _ => Err(status!(\n                StatusCode::BAD_REQUEST,\n                format!(\"unknown encoding: {}\", s),\n                true\n            )),\n        }\n    }\n\n    /// Converts the encoding into its' corresponding header value.\n    fn to_header_value(self) -> HeaderValue {\n        match self {\n            Encoding::Gzip => HeaderValue::from_str(\"gzip\").unwrap(),\n            Encoding::Deflate => HeaderValue::from_str(\"deflate\").unwrap(),\n            Encoding::Brotli => HeaderValue::from_str(\"br\").unwrap(),\n            Encoding::Zstd => HeaderValue::from_str(\"zstd\").unwrap(),\n            Encoding::Identity => HeaderValue::from_str(\"identity\").unwrap(),\n        }\n    }\n}\n\nfn select_encoding(headers: &HeaderMap) -> Result<Option<Encoding>> {\n    let mut preferred_encoding = None;\n    let mut max_qval = 0.0;\n\n    for (encoding, qval) in accept_encodings(headers)? {\n        if qval > max_qval {\n            preferred_encoding = encoding;\n            max_qval = qval;\n        }\n    }\n    Ok(preferred_encoding)\n}\n\n/// Parse a set of HTTP headers into a vector containing tuples of options containing encodings and their corresponding q-values.\n///\n/// If you're looking for more fine-grained control over what encoding to choose for the client, or if you don't support every [`Encoding`] listed, this is likely what you want.\n///\n/// Note that a result of `None` indicates there preference is expressed on which encoding to use.\n/// Either the `Accept-Encoding` header is not present, or `*` is set as the most preferred encoding.\nfn accept_encodings(headers: &HeaderMap) -> Result<Vec<(Option<Encoding>, f32)>> {\n    headers\n        .get_all(ACCEPT_ENCODING)\n        .iter()\n        .map(|hval| {\n            hval.to_str()\n                .map_err(|err| status!(StatusCode::BAD_REQUEST, err, true))\n        })\n        .collect::<Result<Vec<&str>>>()?\n        .iter()\n        .flat_map(|s| s.split(',').map(str::trim))\n        .filter_map(|v| {\n            let pair: Vec<&str> = v.splitn(2, \";q=\").collect();\n            if pair.is_empty() {\n                return None;\n            }\n\n            let encoding = match Encoding::parse(pair[0]) {\n                Ok(encoding) => encoding,\n                Err(_) => return None, // ignore unknown encodings\n            };\n\n            let qval = if pair.len() == 1 {\n                1.0\n            } else {\n                match pair[1].parse::<f32>() {\n                    Ok(f) => f,\n                    Err(err) => return Some(Err(status!(StatusCode::BAD_REQUEST, err, true))),\n                }\n            };\n            Some(Ok((encoding, qval)))\n        })\n        .collect::<Result<Vec<(Option<Encoding>, f32)>>>()\n}\n\nimpl Default for Compress {\n    fn default() -> Self {\n        Self(Level::Default)\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Middleware<'a, S> for Compress {\n    #[allow(clippy::trivially_copy_pass_by_ref)]\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        next.await?;\n        let level = self.0;\n        let best_encoding = select_encoding(&ctx.req.headers)?;\n        let body = std::mem::take(&mut ctx.resp.body);\n        let content_encoding = match best_encoding {\n            None | Some(Encoding::Gzip) => {\n                ctx.resp\n                    .write_reader(GzipEncoder::with_quality(StreamReader::new(body), level));\n                Encoding::Gzip.to_header_value()\n            }\n            Some(Encoding::Deflate) => {\n                ctx.resp\n                    .write_reader(ZlibEncoder::with_quality(StreamReader::new(body), level));\n                Encoding::Deflate.to_header_value()\n            }\n            Some(Encoding::Brotli) => {\n                ctx.resp\n                    .write_reader(BrotliEncoder::with_quality(StreamReader::new(body), level));\n                Encoding::Brotli.to_header_value()\n            }\n            Some(Encoding::Zstd) => {\n                ctx.resp\n                    .write_reader(ZstdEncoder::with_quality(StreamReader::new(body), level));\n                Encoding::Zstd.to_header_value()\n            }\n            Some(Encoding::Identity) => {\n                ctx.resp.body = body;\n                Encoding::Identity.to_header_value()\n            }\n        };\n        ctx.resp.headers.append(CONTENT_ENCODING, content_encoding);\n        Ok(())\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\", feature = \"file\"))]\nmod tests {\n    use std::io;\n    use std::pin::Pin;\n    use std::task::{self, Poll};\n\n    use bytes::Bytes;\n    use futures::Stream;\n    use tokio::task::spawn;\n\n    use crate::body::DispositionType::*;\n    use crate::compress::{Compress, Level};\n    use crate::http::header::ACCEPT_ENCODING;\n    use crate::http::StatusCode;\n    use crate::preload::*;\n    use crate::{async_trait, App, Context, Middleware, Next};\n\n    struct Consumer<S> {\n        counter: usize,\n        stream: S,\n        assert_counter: usize,\n    }\n    impl<S> Stream for Consumer<S>\n    where\n        S: 'static + Send + Send + Unpin + Stream<Item = io::Result<Bytes>>,\n    {\n        type Item = io::Result<Bytes>;\n        fn poll_next(\n            mut self: Pin<&mut Self>,\n            cx: &mut task::Context<'_>,\n        ) -> Poll<Option<Self::Item>> {\n            match Pin::new(&mut self.stream).poll_next(cx) {\n                Poll::Ready(Some(Ok(bytes))) => {\n                    self.counter += bytes.len();\n                    Poll::Ready(Some(Ok(bytes)))\n                }\n                Poll::Ready(None) => {\n                    assert_eq!(self.assert_counter, self.counter);\n                    Poll::Ready(None)\n                }\n                poll => poll,\n            }\n        }\n    }\n\n    struct Assert(usize);\n\n    #[async_trait(?Send)]\n    impl<'a, S> Middleware<'a, S> for Assert {\n        async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> crate::Result {\n            next.await?;\n            let body = std::mem::take(&mut ctx.resp.body);\n            ctx.resp.write_stream(Consumer {\n                counter: 0,\n                stream: body,\n                assert_counter: self.0,\n            });\n            Ok(())\n        }\n    }\n\n    async fn end(ctx: &mut Context) -> crate::Result {\n        ctx.write_file(\"../assets/welcome.html\", Inline).await\n    }\n\n    #[tokio::test]\n    async fn compress() -> Result<(), Box<dyn std::error::Error>> {\n        let app = App::new()\n            .gate(Assert(202)) // compressed to 202 bytes\n            .gate(Compress(Level::Fastest))\n            .gate(Assert(236)) // the size of assets/welcome.html is 236 bytes.\n            .end(end);\n        let (addr, server) = app.run()?;\n        spawn(server);\n        let client = reqwest::Client::builder().gzip(true).build()?;\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(ACCEPT_ENCODING, \"gzip\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert_eq!(236, resp.text().await?.len());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/cookie.rs",
    "content": "//! This module provides a middleware `cookie_parser` and context extensions `CookieGetter` and `CookieSetter`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::cookie::cookie_parser;\n//! use roa::preload::*;\n//! use roa::{App, Context};\n//! use std::error::Error;\n//!\n//! async fn end(ctx: &mut Context) -> roa::Result {\n//!     assert_eq!(\"Hexilee\", ctx.must_cookie(\"name\")?.value());\n//!     Ok(())\n//! }\n//! # #[tokio::main]\n//! # async fn main() -> Result<(), Box<dyn Error>> {\n//! let app = App::new().gate(cookie_parser).end(end);\n//! let (addr, server) = app.run()?;\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n\nuse std::sync::Arc;\n\npub use cookie::Cookie;\nuse percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};\n\nuse crate::http::{header, StatusCode};\nuse crate::{throw, Context, Next, Result};\n\n/// A scope to store and load variables in Context::storage.\nstruct CookieScope;\n\n/// A context extension.\n/// This extension must be used in downstream of middleware `cookier_parser`,\n/// otherwise you cannot get expected cookie.\n///\n/// Percent-encoded cookies will be decoded.\n/// ### Example\n///\n/// ```rust\n/// use roa::cookie::cookie_parser;\n/// use roa::preload::*;\n/// use roa::{App, Context};\n/// use std::error::Error;\n///\n/// async fn end(ctx: &mut Context) -> roa::Result {\n///     assert_eq!(\"Hexilee\", ctx.must_cookie(\"name\")?.value());\n///     Ok(())\n/// }\n/// # #[tokio::main]\n/// # async fn main() -> Result<(), Box<dyn Error>> {\n/// let app = App::new().gate(cookie_parser).end(end);\n/// let (addr, server) = app.run()?;\n/// // server.await\n/// Ok(())\n/// # }\n/// ```\npub trait CookieGetter {\n    /// Must get a cookie, throw 401 UNAUTHORIZED if it not exists.\n    fn must_cookie(&mut self, name: &str) -> Result<Arc<Cookie<'static>>>;\n\n    /// Try to get a cookie, return `None` if it not exists.\n    ///\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa::cookie::cookie_parser;\n    /// use roa::preload::*;\n    /// use roa::{App, Context};\n    /// use std::error::Error;\n    ///\n    /// async fn end(ctx: &mut Context) -> roa::Result {\n    ///     assert!(ctx.cookie(\"name\").is_none());\n    ///     Ok(())\n    /// }\n    /// # #[tokio::main]\n    /// # async fn main() -> Result<(), Box<dyn Error>> {\n    /// let app = App::new().gate(cookie_parser).end(end);\n    /// let (addr, server) = app.run()?;\n    /// // server.await\n    /// Ok(())\n    /// # }\n    /// ```\n    fn cookie(&self, name: &str) -> Option<Arc<Cookie<'static>>>;\n}\n\n/// An extension to set cookie.\npub trait CookieSetter {\n    /// Set a cookie in pecent encoding, should not return Err.\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa::cookie::{cookie_parser, Cookie};\n    /// use roa::preload::*;\n    /// use roa::{App, Context};\n    /// use std::error::Error;\n    ///\n    /// async fn end(ctx: &mut Context) -> roa::Result {\n    ///     ctx.set_cookie(Cookie::new(\"name\", \"Hexilee\"));\n    ///     Ok(())\n    /// }\n    /// # #[tokio::main]\n    /// # async fn main() -> Result<(), Box<dyn Error>> {\n    /// let app = App::new().gate(cookie_parser).end(end);\n    /// let (addr, server) = app.run()?;\n    /// // server.await\n    /// Ok(())\n    /// # }\n    /// ```\n    fn set_cookie(&mut self, cookie: Cookie<'_>) -> Result;\n}\n\n/// A middleware to parse cookie.\n#[inline]\npub async fn cookie_parser<S>(ctx: &mut Context<S>, next: Next<'_>) -> Result {\n    if let Some(cookies) = ctx.get(header::COOKIE) {\n        for cookie in cookies\n            .split(';')\n            .map(|cookie| cookie.trim())\n            .map(Cookie::parse_encoded)\n            .filter_map(|cookie| cookie.ok())\n            .map(|cookie| cookie.into_owned())\n            .collect::<Vec<_>>()\n            .into_iter()\n        {\n            let name = cookie.name().to_string();\n            ctx.store_scoped(CookieScope, name, cookie);\n        }\n    }\n    next.await\n}\n\nimpl<S> CookieGetter for Context<S> {\n    #[inline]\n    fn must_cookie(&mut self, name: &str) -> Result<Arc<Cookie<'static>>> {\n        match self.cookie(name) {\n            Some(value) => Ok(value),\n            None => {\n                self.resp.headers.insert(\n                    header::WWW_AUTHENTICATE,\n                    format!(\n                        r#\"Cookie name=\"{}\"\"#,\n                        utf8_percent_encode(name, NON_ALPHANUMERIC)\n                    )\n                    .parse()?,\n                );\n                throw!(StatusCode::UNAUTHORIZED)\n            }\n        }\n    }\n\n    #[inline]\n    fn cookie(&self, name: &str) -> Option<Arc<Cookie<'static>>> {\n        Some(self.load_scoped::<CookieScope, Cookie>(name)?.value())\n    }\n}\n\nimpl<S> CookieSetter for Context<S> {\n    #[inline]\n    fn set_cookie(&mut self, cookie: Cookie<'_>) -> Result {\n        let cookie_value = cookie.encoded().to_string();\n        self.resp\n            .headers\n            .append(header::SET_COOKIE, cookie_value.parse()?);\n        Ok(())\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use tokio::task::spawn;\n\n    use crate::cookie::{cookie_parser, Cookie};\n    use crate::http::header::{COOKIE, WWW_AUTHENTICATE};\n    use crate::http::StatusCode;\n    use crate::preload::*;\n    use crate::{App, Context};\n\n    async fn must(ctx: &mut Context) -> crate::Result {\n        assert_eq!(\"Hexi Lee\", ctx.must_cookie(\"nick name\")?.value());\n        Ok(())\n    }\n\n    async fn none(ctx: &mut Context) -> crate::Result {\n        assert!(ctx.cookie(\"nick name\").is_none());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn parser() -> Result<(), Box<dyn std::error::Error>> {\n        // downstream of `cookie_parser`\n        let (addr, server) = App::new().gate(cookie_parser).end(must).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(COOKIE, \"nick%20name=Hexi%20Lee\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n\n        // miss `cookie_parser`\n        let (addr, server) = App::new().end(must).run()?;\n        spawn(server);\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(COOKIE, \"nick%20name=Hexi%20Lee\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn cookie() -> Result<(), Box<dyn std::error::Error>> {\n        // miss cookie\n        let (addr, server) = App::new().end(none).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n\n        let (addr, server) = App::new().gate(cookie_parser).end(must).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(\n            r#\"Cookie name=\"nick%20name\"\"#,\n            resp.headers()\n                .get(WWW_AUTHENTICATE)\n                .unwrap()\n                .to_str()\n                .unwrap()\n        );\n\n        // string value\n        let (addr, server) = App::new().gate(cookie_parser).end(must).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(COOKIE, \"nick%20name=Hexi%20Lee\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn cookie_action() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(\"bar baz\", ctx.must_cookie(\"bar baz\")?.value());\n            assert_eq!(\"bar foo\", ctx.must_cookie(\"foo baz\")?.value());\n            Ok(())\n        }\n\n        let (addr, server) = App::new().gate(cookie_parser).end(test).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(COOKIE, \"bar%20baz=bar%20baz; foo%20baz=bar%20foo\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn set_cookie() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            ctx.set_cookie(Cookie::new(\"bar baz\", \"bar baz\"))?;\n            ctx.set_cookie(Cookie::new(\"bar foo\", \"foo baz\"))?;\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        let cookies: Vec<reqwest::cookie::Cookie> = resp.cookies().collect();\n        assert_eq!(2, cookies.len());\n        assert_eq!((\"bar%20baz\"), cookies[0].name());\n        assert_eq!((\"bar%20baz\"), cookies[0].value());\n        assert_eq!((\"bar%20foo\"), cookies[1].name());\n        assert_eq!((\"foo%20baz\"), cookies[1].value());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/cors.rs",
    "content": "//! This module provides a middleware `Cors`.\n\nuse std::collections::HashSet;\nuse std::convert::TryInto;\nuse std::fmt::Debug;\nuse std::iter::FromIterator;\nuse std::time::Duration;\n\nuse headers::{\n    AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,\n    AccessControlAllowOrigin, AccessControlExposeHeaders, AccessControlMaxAge,\n    AccessControlRequestHeaders, AccessControlRequestMethod, Header, HeaderMapExt,\n};\nuse roa_core::Status;\n\nuse crate::http::header::{HeaderName, HeaderValue, ORIGIN, VARY};\nuse crate::http::{Method, StatusCode};\nuse crate::{async_trait, Context, Middleware, Next, Result};\n\n/// A middleware to deal with Cross-Origin Resource Sharing (CORS).\n///\n/// ### Default\n///\n/// The default Cors middleware will satisfy all needs of a request.\n///\n/// Build a default Cors middleware:\n///\n/// ```rust\n/// use roa::cors::Cors;\n///\n/// let default_cors = Cors::new();\n/// ```\n///\n/// ### Config\n///\n/// You can also configure it:\n///\n/// ```rust\n/// use roa::cors::Cors;\n/// use roa::http::header::{CONTENT_DISPOSITION, AUTHORIZATION, WWW_AUTHENTICATE};\n/// use roa::http::Method;\n///\n/// let cors = Cors::builder()\n///     .allow_credentials(true)\n///     .max_age(86400)\n///     .allow_origin(\"https://github.com\")\n///     .allow_methods(vec![Method::GET, Method::POST])\n///     .allow_method(Method::PUT)\n///     .expose_headers(vec![CONTENT_DISPOSITION])\n///     .expose_header(WWW_AUTHENTICATE)\n///     .allow_headers(vec![AUTHORIZATION])\n///     .allow_header(CONTENT_DISPOSITION)\n///     .build();\n/// ```\n#[derive(Debug, Default)]\npub struct Cors {\n    allow_origin: Option<AccessControlAllowOrigin>,\n    allow_methods: Option<AccessControlAllowMethods>,\n    expose_headers: Option<AccessControlExposeHeaders>,\n    allow_headers: Option<AccessControlAllowHeaders>,\n    max_age: Option<AccessControlMaxAge>,\n    credentials: Option<AccessControlAllowCredentials>,\n}\n\n/// Builder of Cors.\n#[derive(Clone, Debug, Default)]\npub struct Builder {\n    credentials: bool,\n    allowed_headers: HashSet<HeaderName>,\n    exposed_headers: HashSet<HeaderName>,\n    max_age: Option<u64>,\n    methods: HashSet<Method>,\n    origins: Option<HeaderValue>,\n}\n\nimpl Cors {\n    /// Construct default Cors.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Get builder.\n    pub fn builder() -> Builder {\n        Builder::default()\n    }\n}\n\nimpl Builder {\n    /// Sets whether to add the `Access-Control-Allow-Credentials` header.\n    pub fn allow_credentials(mut self, allow: bool) -> Self {\n        self.credentials = allow;\n        self\n    }\n\n    /// Adds a method to the existing list of allowed request methods.\n    pub fn allow_method(mut self, method: Method) -> Self {\n        self.methods.insert(method);\n        self\n    }\n\n    /// Adds multiple methods to the existing list of allowed request methods.\n    pub fn allow_methods(mut self, methods: impl IntoIterator<Item = Method>) -> Self {\n        self.methods.extend(methods);\n        self\n    }\n\n    /// Adds a header to the list of allowed request headers.\n    ///\n    /// # Panics\n    ///\n    /// Panics if header is not a valid `http::header::HeaderName`.\n    pub fn allow_header<H>(mut self, header: H) -> Self\n    where\n        H: TryInto<HeaderName>,\n        H::Error: Debug,\n    {\n        self.allowed_headers\n            .insert(header.try_into().expect(\"invalid header\"));\n        self\n    }\n\n    /// Adds multiple headers to the list of allowed request headers.\n    ///\n    /// # Panics\n    ///\n    /// Panics if any of the headers are not a valid `http::header::HeaderName`.\n    pub fn allow_headers<I>(mut self, headers: I) -> Self\n    where\n        I: IntoIterator,\n        I::Item: TryInto<HeaderName>,\n        <I::Item as TryInto<HeaderName>>::Error: Debug,\n    {\n        let iter = headers\n            .into_iter()\n            .map(|h| h.try_into().expect(\"invalid header\"));\n        self.allowed_headers.extend(iter);\n        self\n    }\n\n    /// Adds a header to the list of exposed headers.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the provided argument is not a valid `http::header::HeaderName`.\n    pub fn expose_header<H>(mut self, header: H) -> Self\n    where\n        H: TryInto<HeaderName>,\n        H::Error: Debug,\n    {\n        self.exposed_headers\n            .insert(header.try_into().expect(\"illegal Header\"));\n        self\n    }\n\n    /// Adds multiple headers to the list of exposed headers.\n    ///\n    /// # Panics\n    ///\n    /// Panics if any of the headers are not a valid `http::header::HeaderName`.\n    pub fn expose_headers<I>(mut self, headers: I) -> Self\n    where\n        I: IntoIterator,\n        I::Item: TryInto<HeaderName>,\n        <I::Item as TryInto<HeaderName>>::Error: Debug,\n    {\n        let iter = headers\n            .into_iter()\n            .map(|h| h.try_into().expect(\"illegal Header\"));\n        self.exposed_headers.extend(iter);\n        self\n    }\n\n    /// Add an origin to the existing list of allowed `Origin`s.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the provided argument is not a valid `HeaderValue`.\n    pub fn allow_origin<H>(mut self, origin: H) -> Self\n    where\n        H: TryInto<HeaderValue>,\n        H::Error: Debug,\n    {\n        self.origins = Some(origin.try_into().expect(\"invalid origin\"));\n        self\n    }\n\n    /// Sets the `Access-Control-Max-Age` header.\n    pub fn max_age(mut self, seconds: u64) -> Self {\n        self.max_age = Some(seconds);\n        self\n    }\n\n    /// Builds the `Cors` wrapper from the configured settings.\n    ///\n    /// This step isn't *required*, as the `Builder` itself can be passed\n    /// to `Filter::with`. This just allows constructing once, thus not needing\n    /// to pay the cost of \"building\" every time.\n    pub fn build(self) -> Cors {\n        let Builder {\n            allowed_headers,\n            credentials,\n            exposed_headers,\n            max_age,\n            origins,\n            methods,\n        } = self;\n        let mut cors = Cors::default();\n        if !allowed_headers.is_empty() {\n            cors.allow_headers = Some(AccessControlAllowHeaders::from_iter(allowed_headers))\n        }\n\n        if credentials {\n            cors.credentials = Some(AccessControlAllowCredentials)\n        }\n\n        if !exposed_headers.is_empty() {\n            cors.expose_headers = Some(AccessControlExposeHeaders::from_iter(exposed_headers))\n        }\n\n        if let Some(age) = max_age {\n            cors.max_age = Some(Duration::from_secs(age).into())\n        }\n\n        if origins.is_some() {\n            cors.allow_origin = Some(\n                AccessControlAllowOrigin::decode(&mut origins.iter()).expect(\"invalid origins\"),\n            );\n        }\n\n        if !methods.is_empty() {\n            cors.allow_methods = Some(AccessControlAllowMethods::from_iter(methods))\n        }\n\n        cors\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Middleware<'a, S> for Cors {\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        // Always set Vary header\n        // https://github.com/rs/cors/issues/10\n        ctx.resp.headers.append(VARY, ORIGIN.into());\n\n        let origin = match ctx.req.headers.get(ORIGIN) {\n            // If there is no Origin header, skip this middleware.\n            None => return next.await,\n            Some(origin) => AccessControlAllowOrigin::decode(&mut Some(origin).into_iter())\n                .map_err(|err| {\n                    Status::new(\n                        StatusCode::BAD_REQUEST,\n                        format!(\"invalid origin: {}\", err),\n                        true,\n                    )\n                })?,\n        };\n\n        // If Options::allow_origin is None, `Access-Control-Allow-Origin` will be set to `Origin`.\n        let allow_origin = self.allow_origin.clone().unwrap_or(origin);\n\n        let credentials = self.credentials.clone();\n        let insert_origin_and_credentials = move |ctx: &mut Context<S>| {\n            // Set \"Access-Control-Allow-Origin\"\n            ctx.resp.headers.typed_insert(allow_origin);\n\n            // Try to set \"Access-Control-Allow-Credentials\"\n            if let Some(credentials) = credentials {\n                ctx.resp.headers.typed_insert(credentials);\n            }\n        };\n\n        if ctx.method() != Method::OPTIONS {\n            // Simple Request\n\n            insert_origin_and_credentials(ctx);\n\n            // Set \"Access-Control-Expose-Headers\"\n            if let Some(ref exposed_headers) = self.expose_headers {\n                ctx.resp.headers.typed_insert(exposed_headers.clone());\n            }\n            next.await\n        } else {\n            // Preflight Request\n\n            let request_method = match ctx.req.headers.typed_get::<AccessControlRequestMethod>() {\n                // If there is no Origin header or if parsing failed, skip this middleware.\n                None => return next.await,\n                Some(request_method) => request_method,\n            };\n\n            // If Options::allow_methods is None, `Access-Control-Allow-Methods` will be set to `Access-Control-Request-Method`.\n            let allow_methods = match self.allow_methods {\n                Some(ref origin) => origin.clone(),\n                None => AccessControlAllowMethods::from_iter(Some(request_method.into())),\n            };\n\n            // Try to set \"Access-Control-Allow-Methods\"\n            ctx.resp.headers.typed_insert(allow_methods);\n\n            insert_origin_and_credentials(ctx);\n\n            // Set \"Access-Control-Max-Age\"\n            if let Some(ref max_age) = self.max_age {\n                ctx.resp.headers.typed_insert(max_age.clone());\n            }\n\n            // If allow_headers is None, try to assign `Access-Control-Request-Headers` to `Access-Control-Allow-Headers`.\n            let allow_headers = self.allow_headers.clone().or_else(|| {\n                ctx.req\n                    .headers\n                    .typed_get::<AccessControlRequestHeaders>()\n                    .map(|headers| headers.iter().collect())\n            });\n            if let Some(headers) = allow_headers {\n                ctx.resp.headers.typed_insert(headers);\n            };\n\n            ctx.resp.status = StatusCode::NO_CONTENT;\n            Ok(())\n        }\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use headers::{\n        AccessControlAllowCredentials, AccessControlAllowOrigin, AccessControlExposeHeaders,\n        HeaderMapExt, HeaderName,\n    };\n    use tokio::task::spawn;\n\n    use super::Cors;\n    use crate::http::header::{\n        ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS,\n        ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_MAX_AGE,\n        ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AUTHORIZATION,\n        CONTENT_DISPOSITION, CONTENT_TYPE, ORIGIN, VARY, WWW_AUTHENTICATE,\n    };\n    use crate::http::{HeaderValue, Method, StatusCode};\n    use crate::preload::*;\n    use crate::{App, Context};\n\n    async fn end(ctx: &mut Context) -> crate::Result {\n        ctx.resp.write(\"Hello, World\");\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn default_cors() -> Result<(), Box<dyn std::error::Error>> {\n        let (addr, server) = App::new().gate(Cors::new()).end(end).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n\n        // No origin\n        let resp = client.get(&format!(\"http://{}\", addr)).send().await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert!(resp\n            .headers()\n            .typed_get::<AccessControlAllowOrigin>()\n            .is_none());\n        assert_eq!(\n            HeaderValue::from_name(ORIGIN),\n            resp.headers().get(VARY).unwrap()\n        );\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // invalid origin\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(ORIGIN, \"github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n\n        // simple request\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n\n        let allow_origin = resp\n            .headers()\n            .typed_get::<AccessControlAllowOrigin>()\n            .unwrap();\n        let origin = allow_origin.origin().unwrap();\n        assert_eq!(\"http\", origin.scheme());\n        assert_eq!(\"github.com\", origin.hostname());\n        assert!(origin.port().is_none());\n        assert!(resp\n            .headers()\n            .typed_get::<AccessControlAllowCredentials>()\n            .is_none());\n\n        assert!(resp\n            .headers()\n            .typed_get::<AccessControlExposeHeaders>()\n            .is_none());\n\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // options, no Access-Control-Request-Method\n        let resp = client\n            .request(Method::OPTIONS, &format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert!(resp.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN).is_none());\n        assert_eq!(\n            HeaderValue::from_name(ORIGIN),\n            resp.headers().get(VARY).unwrap()\n        );\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // options, contains Access-Control-Request-Method\n        let resp = client\n            .request(Method::OPTIONS, &format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.com\")\n            .header(ACCESS_CONTROL_REQUEST_METHOD, \"POST\")\n            .header(\n                ACCESS_CONTROL_REQUEST_HEADERS,\n                HeaderValue::from_name(CONTENT_TYPE),\n            )\n            .send()\n            .await?;\n        assert_eq!(StatusCode::NO_CONTENT, resp.status());\n        assert_eq!(\n            \"http://github.com\",\n            resp.headers()\n                .get(ACCESS_CONTROL_ALLOW_ORIGIN)\n                .unwrap()\n                .to_str()?\n        );\n        assert!(resp\n            .headers()\n            .get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n            .is_none());\n\n        assert!(resp.headers().get(ACCESS_CONTROL_MAX_AGE).is_none());\n\n        assert_eq!(\n            \"POST\",\n            resp.headers()\n                .get(ACCESS_CONTROL_ALLOW_METHODS)\n                .unwrap()\n                .to_str()?\n        );\n\n        assert_eq!(\n            HeaderValue::from_name(CONTENT_TYPE),\n            resp.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).unwrap()\n        );\n        assert_eq!(\"\", resp.text().await?);\n        //\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn configured_cors() -> Result<(), Box<dyn std::error::Error>> {\n        let configured_cors = Cors::builder()\n            .allow_credentials(true)\n            .max_age(86400)\n            .allow_origin(\"https://github.com\")\n            .allow_methods(vec![Method::GET, Method::POST])\n            .allow_method(Method::PUT)\n            .expose_headers(vec![CONTENT_DISPOSITION])\n            .expose_header(WWW_AUTHENTICATE)\n            .allow_headers(vec![AUTHORIZATION])\n            .allow_header(CONTENT_TYPE)\n            .build();\n        let (addr, server) = App::new().gate(configured_cors).end(end).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n\n        // No origin\n        let resp = client.get(&format!(\"http://{}\", addr)).send().await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert!(resp\n            .headers()\n            .typed_get::<AccessControlAllowOrigin>()\n            .is_none());\n        assert_eq!(\n            HeaderValue::from_name(ORIGIN),\n            resp.headers().get(VARY).unwrap()\n        );\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // invalid origin\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(ORIGIN, \"github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n\n        // simple request\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.io\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n\n        let allow_origin = resp\n            .headers()\n            .typed_get::<AccessControlAllowOrigin>()\n            .unwrap();\n        let origin = allow_origin.origin().unwrap();\n        assert_eq!(\"https\", origin.scheme());\n        assert_eq!(\"github.com\", origin.hostname());\n        assert!(origin.port().is_none());\n        assert!(resp\n            .headers()\n            .typed_get::<AccessControlAllowCredentials>()\n            .is_some());\n\n        let expose_headers = resp\n            .headers()\n            .typed_get::<AccessControlExposeHeaders>()\n            .unwrap();\n\n        let headers = expose_headers.iter().collect::<Vec<HeaderName>>();\n        assert!(headers.contains(&CONTENT_DISPOSITION));\n        assert!(headers.contains(&WWW_AUTHENTICATE));\n\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // options, no Access-Control-Request-Method\n        let resp = client\n            .request(Method::OPTIONS, &format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        assert!(resp.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN).is_none());\n        assert_eq!(\n            HeaderValue::from_name(ORIGIN),\n            resp.headers().get(VARY).unwrap()\n        );\n        assert_eq!(\"Hello, World\", resp.text().await?);\n\n        // options, contains Access-Control-Request-Method\n        let resp = client\n            .request(Method::OPTIONS, &format!(\"http://{}\", addr))\n            .header(ORIGIN, \"http://github.io\")\n            .header(ACCESS_CONTROL_REQUEST_METHOD, \"POST\")\n            .header(\n                ACCESS_CONTROL_REQUEST_HEADERS,\n                HeaderValue::from_name(CONTENT_TYPE),\n            )\n            .send()\n            .await?;\n        assert_eq!(StatusCode::NO_CONTENT, resp.status());\n        assert_eq!(\n            \"https://github.com\",\n            resp.headers()\n                .get(ACCESS_CONTROL_ALLOW_ORIGIN)\n                .unwrap()\n                .to_str()?\n        );\n        assert_eq!(\n            \"true\",\n            resp.headers()\n                .get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n                .unwrap()\n                .to_str()?\n        );\n\n        assert_eq!(\"86400\", resp.headers().get(ACCESS_CONTROL_MAX_AGE).unwrap());\n\n        let allow_methods = resp\n            .headers()\n            .get(ACCESS_CONTROL_ALLOW_METHODS)\n            .unwrap()\n            .to_str()?;\n        assert!(allow_methods.contains(\"POST\"));\n        assert!(allow_methods.contains(\"GET\"));\n        assert!(allow_methods.contains(\"PUT\"));\n\n        let allow_headers = resp\n            .headers()\n            .get(ACCESS_CONTROL_ALLOW_HEADERS)\n            .unwrap()\n            .to_str()?;\n        assert!(allow_headers.contains(CONTENT_TYPE.as_str()));\n        assert!(allow_headers.contains(AUTHORIZATION.as_str()));\n        assert_eq!(\"\", resp.text().await?);\n        //\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/forward.rs",
    "content": "//! This module provides a context extension `Forward`,\n//! which is used to parse `X-Forwarded-*` headers.\n\nuse std::net::IpAddr;\n\nuse crate::http::header::HOST;\nuse crate::{Context, State};\n\n/// A context extension `Forward` used to parse `X-Forwarded-*` request headers.\npub trait Forward {\n    /// Get true host.\n    /// - If \"x-forwarded-host\" is set and valid, use it.\n    /// - Else if \"host\" is set and valid, use it.\n    /// - Else throw Err(400 BAD REQUEST).\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa::{Context, Result};\n    /// use roa::forward::Forward;\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     if let Some(host) = ctx.host() {\n    ///         println!(\"host: {}\", host);\n    ///     }\n    ///     Ok(())\n    /// }\n    /// ```\n    fn host(&self) -> Option<&str>;\n\n    /// Get true client ip.\n    /// - If \"x-forwarded-for\" is set and valid, use the first ip.\n    /// - Else use the ip of `Context::remote_addr()`.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa::{Context, Result};\n    /// use roa::forward::Forward;\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     println!(\"client ip: {}\", ctx.client_ip());\n    ///     Ok(())\n    /// }\n    /// ```\n    fn client_ip(&self) -> IpAddr;\n\n    /// Get true forwarded ips.\n    /// - If \"x-forwarded-for\" is set and valid, use it.\n    /// - Else return an empty vector.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa::{Context, Result};\n    /// use roa::forward::Forward;\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     println!(\"forwarded ips: {:?}\", ctx.forwarded_ips());\n    ///     Ok(())\n    /// }\n    /// ```\n    fn forwarded_ips(&self) -> Vec<IpAddr>;\n\n    /// Try to get forwarded proto.\n    /// - If \"x-forwarded-proto\" is not set, return None.\n    /// - If \"x-forwarded-proto\" is set but fails to string, return Some(Err(400 BAD REQUEST)).\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa::{Context, Result};\n    /// use roa::forward::Forward;\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     if let Some(proto) = ctx.forwarded_proto() {\n    ///         println!(\"forwarded proto: {}\", proto);\n    ///     }\n    ///     Ok(())\n    /// }\n    /// ```\n    fn forwarded_proto(&self) -> Option<&str>;\n}\n\nimpl<S: State> Forward for Context<S> {\n    #[inline]\n    fn host(&self) -> Option<&str> {\n        self.get(\"x-forwarded-host\").or_else(|| self.get(HOST))\n    }\n\n    #[inline]\n    fn client_ip(&self) -> IpAddr {\n        let addrs = self.forwarded_ips();\n        if addrs.is_empty() {\n            self.remote_addr.ip()\n        } else {\n            addrs[0]\n        }\n    }\n\n    #[inline]\n    fn forwarded_ips(&self) -> Vec<IpAddr> {\n        let mut addrs = Vec::new();\n        if let Some(value) = self.get(\"x-forwarded-for\") {\n            for addr_str in value.split(',') {\n                if let Ok(addr) = addr_str.trim().parse() {\n                    addrs.push(addr)\n                }\n            }\n        }\n        addrs\n    }\n\n    #[inline]\n    fn forwarded_proto(&self) -> Option<&str> {\n        self.get(\"x-forwarded-proto\")\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use tokio::task::spawn;\n\n    use super::Forward;\n    use crate::http::header::HOST;\n    use crate::http::{HeaderValue, StatusCode};\n    use crate::preload::*;\n    use crate::{App, Context};\n\n    #[tokio::test]\n    async fn host() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(Some(\"github.com\"), ctx.host());\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(HOST, HeaderValue::from_static(\"github.com\"))\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(HOST, \"google.com\")\n            .header(\"x-forwarded-host\", \"github.com\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn host_err() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            ctx.req.headers.remove(HOST);\n            assert_eq!(None, ctx.host());\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn client_ip() -> Result<(), Box<dyn std::error::Error>> {\n        async fn remote_addr(ctx: &mut Context) -> crate::Result {\n            assert_eq!(ctx.remote_addr.ip(), ctx.client_ip());\n            Ok(())\n        }\n        let (addr, server) = App::new().end(remote_addr).run()?;\n        spawn(server);\n        reqwest::get(&format!(\"http://{}\", addr)).await?;\n\n        async fn forward_addr(ctx: &mut Context) -> crate::Result {\n            assert_eq!(\"192.168.0.1\", ctx.client_ip().to_string());\n            Ok(())\n        }\n        let (addr, server) = App::new().end(forward_addr).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        client\n            .get(&format!(\"http://{}\", addr))\n            .header(\"x-forwarded-for\", \"192.168.0.1, 8.8.8.8\")\n            .send()\n            .await?;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn forwarded_proto() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(Some(\"https\"), ctx.forwarded_proto());\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let client = reqwest::Client::new();\n        client\n            .get(&format!(\"http://{}\", addr))\n            .header(\"x-forwarded-proto\", \"https\")\n            .send()\n            .await?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/jsonrpc.rs",
    "content": "//!\n//! ## roa::jsonrpc\n//!\n//! This module provides a json rpc endpoint.\n//!\n//! ### Example\n//!\n//! ```rust,no_run\n//! use roa::App;\n//! use roa::jsonrpc::{RpcEndpoint, Data, Error, Params, Server};\n//! use roa::tcp::Listener;\n//! use tracing::info;\n//!\n//! #[derive(serde::Deserialize)]\n//! struct TwoNums {\n//!     a: usize,\n//!     b: usize,\n//! }\n//!\n//! async fn add(Params(params): Params<TwoNums>) -> Result<usize, Error> {\n//!     Ok(params.a + params.b)\n//! }\n//!\n//! async fn sub(Params(params): Params<(usize, usize)>) -> Result<usize, Error> {\n//!     Ok(params.0 - params.1)\n//! }\n//!\n//! async fn message(data: Data<String>) -> Result<String, Error> {\n//!     Ok(String::from(&*data))\n//! }\n//!\n//! #[tokio::main]\n//! async fn main() -> anyhow::Result<()> {\n//!     let rpc = Server::new()\n//!         .with_data(Data::new(String::from(\"Hello!\")))\n//!         .with_method(\"sub\", sub)\n//!         .with_method(\"message\", message)\n//!         .finish_unwrapped();\n//!\n//!     let app = App::new().end(RpcEndpoint(rpc));\n//!     app.listen(\"127.0.0.1:8000\", |addr| {\n//!         info!(\"Server is listening on {}\", addr)\n//!     })?\n//!     .await?;\n//!     Ok(())\n//! }\n//! ```\n\nuse bytes::Bytes;\n#[doc(no_inline)]\npub use jsonrpc_v2::*;\n\nuse crate::body::PowerBody;\nuse crate::{async_trait, Context, Endpoint, Result, State};\n\n/// A wrapper for [`jsonrpc_v2::Server`], implemented [`roa::Endpoint`].\n///\n/// [`jsonrpc_v2::Server`]: https://docs.rs/jsonrpc-v2/0.10.1/jsonrpc_v2/struct.Server.html\n/// [`roa::Endpoint`]: https://docs.rs/roa/0.6.0/roa/trait.Endpoint.html\npub struct RpcEndpoint<R>(pub Server<R>);\n\n#[async_trait(? Send)]\nimpl<'a, S, R> Endpoint<'a, S> for RpcEndpoint<R>\nwhere\n    S: State,\n    R: Router + Sync + Send + 'static,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        let data = ctx.read().await?;\n        let resp = self.0.handle(Bytes::from(data)).await;\n        ctx.write_json(&resp)\n    }\n}\n"
  },
  {
    "path": "roa/src/jwt.rs",
    "content": "//! This module provides middleware `JwtGuard` and a context extension `JwtVerifier`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::jwt::{guard, DecodingKey};\n//! use roa::{App, Context};\n//! use roa::http::header::AUTHORIZATION;\n//! use roa::http::StatusCode;\n//! use roa::preload::*;\n//! use tokio::task::spawn;\n//! use jsonwebtoken::{encode, Header, EncodingKey};\n//! use serde::{Deserialize, Serialize};\n//! use std::time::{Duration, SystemTime, UNIX_EPOCH};\n//!\n//! #[derive(Debug, Serialize, Deserialize)]\n//! struct User {\n//!     sub: String,\n//!     company: String,\n//!     exp: u64,\n//!     id: u64,\n//!     name: String,\n//! }\n//!\n//! const SECRET: &[u8] = b\"123456\";\n//!\n//! async fn test(ctx: &mut Context) -> roa::Result {\n//!     let user: User = ctx.claims()?;\n//!     assert_eq!(0, user.id);\n//!     assert_eq!(\"Hexilee\", &user.name);\n//!     Ok(())\n//! }\n//!\n//! #[tokio::main]\n//! async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//!     let (addr, server) = App::new()\n//!         .gate(guard(DecodingKey::from_secret(SECRET)))\n//!         .end(test).run()?;\n//!     spawn(server);\n//!     let mut user = User {\n//!         sub: \"user\".to_string(),\n//!         company: \"None\".to_string(),\n//!         exp: (SystemTime::now() + Duration::from_secs(86400))\n//!             .duration_since(UNIX_EPOCH)?\n//!             .as_secs(),\n//!         id: 0,\n//!         name: \"Hexilee\".to_string(),\n//!     };\n//!\n//!     let client = reqwest::Client::new();\n//!     let resp = client\n//!         .get(&format!(\"http://{}\", addr))\n//!         .header(\n//!             AUTHORIZATION,\n//!             format!(\n//!                 \"Bearer {}\",\n//!                 encode(\n//!                     &Header::default(),\n//!                     &user,\n//!                     &EncodingKey::from_secret(SECRET)\n//!                 )?\n//!             ),\n//!         )\n//!         .send()\n//!         .await?;\n//!     assert_eq!(StatusCode::OK, resp.status());\n//!     Ok(())\n//! }\n//! ```\n\nuse headers::authorization::Bearer;\nuse headers::{Authorization, HeaderMapExt};\nuse jsonwebtoken::decode;\npub use jsonwebtoken::{DecodingKey, Validation};\nuse serde::de::DeserializeOwned;\nuse serde_json::Value;\n\nuse crate::http::header::{HeaderValue, WWW_AUTHENTICATE};\nuse crate::http::StatusCode;\nuse crate::{async_trait, throw, Context, Middleware, Next, Result, Status};\n\n/// A private scope.\nstruct JwtScope;\n\nstatic INVALID_TOKEN: HeaderValue =\n    HeaderValue::from_static(r#\"Bearer realm=\"<jwt>\", error=\"invalid_token\"\"#);\n\n/// A function to set value of WWW_AUTHENTICATE.\n#[inline]\nfn set_www_authenticate<S>(ctx: &mut Context<S>) {\n    ctx.resp\n        .headers\n        .insert(WWW_AUTHENTICATE, INVALID_TOKEN.clone());\n}\n\n/// Throw a internal server error.\n#[inline]\nfn guard_not_set() -> Status {\n    Status::new(\n        StatusCode::INTERNAL_SERVER_ERROR,\n        \"middleware `JwtGuard` is not set correctly\",\n        false,\n    )\n}\n\n/// A context extension.\n/// This extension must be used in downstream of middleware `guard` or `guard_by`,\n/// otherwise you cannot get expected claims.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa::{Context, Result};\n/// use roa::jwt::JwtVerifier;\n/// use serde_json::Value;\n///\n/// async fn get(ctx: &mut Context) -> Result {\n///     let claims: Value = ctx.claims()?;\n///     Ok(())\n/// }\n/// ```\npub trait JwtVerifier<S> {\n    /// Deserialize claims from token.\n    fn claims<C>(&self) -> Result<C>\n    where\n        C: 'static + DeserializeOwned;\n\n    /// Verify token and deserialize claims with a validation.\n    /// Use this method if this validation is different from that one of `JwtGuard`.\n    fn verify<C>(&mut self, validation: &Validation) -> Result<C>\n    where\n        C: 'static + DeserializeOwned;\n}\n\n/// Guard by default validation.\npub fn guard(secret: DecodingKey) -> JwtGuard {\n    JwtGuard::new(secret, Validation::default())\n}\n\n/// A middleware to deny unauthorized requests.\n///\n/// The json web token should be deliver by request header \"authorization\",\n/// in format of `Authorization: Bearer <token>`.\n///\n/// If request fails to pass verification, return 401 UNAUTHORIZED and set response header \"WWW-Authenticate\".\n#[derive(Debug, Clone, PartialEq)]\npub struct JwtGuard {\n    secret: DecodingKey<'static>,\n    validation: Validation,\n}\n\nimpl JwtGuard {\n    /// Construct guard.\n    pub fn new(secret: DecodingKey, validation: Validation) -> Self {\n        Self {\n            secret: secret.into_static(),\n            validation,\n        }\n    }\n\n    /// Verify token.\n    #[inline]\n    fn verify<S>(&self, ctx: &Context<S>) -> Option<(Bearer, Value)> {\n        let bearer = ctx.req.headers.typed_get::<Authorization<Bearer>>()?.0;\n        let value = decode::<Value>(bearer.token(), &self.secret, &self.validation)\n            .ok()?\n            .claims;\n        Some((bearer, value))\n    }\n}\n\n#[async_trait(? Send)]\nimpl<'a, S> Middleware<'a, S> for JwtGuard {\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        match self.verify(ctx) {\n            None => {\n                set_www_authenticate(ctx);\n                throw!(StatusCode::UNAUTHORIZED)\n            }\n            Some((bearer, value)) => {\n                ctx.store_scoped(JwtScope, \"secret\", self.secret.clone());\n                ctx.store_scoped(JwtScope, \"token\", bearer);\n                ctx.store_scoped(JwtScope, \"value\", value);\n                next.await\n            }\n        }\n    }\n}\n\nimpl<S> JwtVerifier<S> for Context<S> {\n    #[inline]\n    fn claims<C>(&self) -> Result<C>\n    where\n        C: 'static + DeserializeOwned,\n    {\n        let value = self.load_scoped::<JwtScope, Value>(\"value\");\n        match value {\n            Some(claims) => Ok(serde_json::from_value((*claims).clone())?),\n            None => Err(guard_not_set()),\n        }\n    }\n\n    #[inline]\n    fn verify<C>(&mut self, validation: &Validation) -> Result<C>\n    where\n        C: 'static + DeserializeOwned,\n    {\n        let secret = self.load_scoped::<JwtScope, DecodingKey<'static>>(\"secret\");\n        let token = self.load_scoped::<JwtScope, Bearer>(\"token\");\n        match (secret, token) {\n            (Some(secret), Some(token)) => match decode(token.token(), &secret, validation) {\n                Ok(data) => Ok(data.claims),\n                Err(_) => {\n                    set_www_authenticate(self);\n                    throw!(StatusCode::UNAUTHORIZED)\n                }\n            },\n            _ => Err(guard_not_set()),\n        }\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use std::time::{Duration, SystemTime, UNIX_EPOCH};\n\n    use jsonwebtoken::{encode, EncodingKey, Header};\n    use serde::{Deserialize, Serialize};\n    use tokio::task::spawn;\n\n    use super::{guard, DecodingKey, INVALID_TOKEN};\n    use crate::http::header::{AUTHORIZATION, WWW_AUTHENTICATE};\n    use crate::http::StatusCode;\n    use crate::preload::*;\n    use crate::{App, Context};\n\n    #[derive(Debug, Serialize, Deserialize)]\n    struct User {\n        sub: String,\n        company: String,\n        exp: u64,\n        id: u64,\n        name: String,\n    }\n\n    const SECRET: &[u8] = b\"123456\";\n\n    #[tokio::test]\n    async fn claims() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            let user: User = ctx.claims()?;\n            assert_eq!(0, user.id);\n            assert_eq!(\"Hexilee\", &user.name);\n            Ok(())\n        }\n        let (addr, server) = App::new()\n            .gate(guard(DecodingKey::from_secret(SECRET)))\n            .end(test)\n            .run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(&INVALID_TOKEN, &resp.headers()[WWW_AUTHENTICATE]);\n\n        // non-string header value\n        let client = reqwest::Client::new();\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(AUTHORIZATION, [255].as_ref())\n            .send()\n            .await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(&INVALID_TOKEN, &resp.headers()[WWW_AUTHENTICATE]);\n\n        // non-Bearer header value\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(AUTHORIZATION, \"Basic hahaha\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(&INVALID_TOKEN, &resp.headers()[WWW_AUTHENTICATE]);\n\n        // invalid token\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(AUTHORIZATION, \"Bearer hahaha\")\n            .send()\n            .await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(&INVALID_TOKEN, &resp.headers()[WWW_AUTHENTICATE]);\n\n        // expired token\n        let mut user = User {\n            sub: \"user\".to_string(),\n            company: \"None\".to_string(),\n            exp: (SystemTime::now() - Duration::from_secs(1))\n                .duration_since(UNIX_EPOCH)?\n                .as_secs(), // one second ago\n            id: 0,\n            name: \"Hexilee\".to_string(),\n        };\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(\n                AUTHORIZATION,\n                format!(\n                    \"Bearer {}\",\n                    encode(&Header::default(), &user, &EncodingKey::from_secret(SECRET),)?\n                ),\n            )\n            .send()\n            .await?;\n        assert_eq!(StatusCode::UNAUTHORIZED, resp.status());\n        assert_eq!(&INVALID_TOKEN, &resp.headers()[WWW_AUTHENTICATE]);\n\n        user.exp = (SystemTime::now() + Duration::from_millis(60))\n            .duration_since(UNIX_EPOCH)?\n            .as_secs(); // one hour later\n        let resp = client\n            .get(&format!(\"http://{}\", addr))\n            .header(\n                AUTHORIZATION,\n                format!(\n                    \"Bearer {}\",\n                    encode(&Header::default(), &user, &EncodingKey::from_secret(SECRET),)?\n                ),\n            )\n            .send()\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn jwt_verify_not_set() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            let _: User = ctx.claims()?;\n            Ok(())\n        }\n        let (addr, server) = App::new().end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::INTERNAL_SERVER_ERROR, resp.status());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/lib.rs",
    "content": "#![cfg_attr(feature = \"docs\", feature(doc_cfg))]\n#![cfg_attr(feature = \"docs\", doc = include_str!(\"../README.md\"))]\n#![cfg_attr(feature = \"docs\", warn(missing_docs))]\n\npub use roa_core::*;\n\n#[cfg(feature = \"router\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"router\")))]\npub mod router;\n\n#[cfg(feature = \"tcp\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"tcp\")))]\npub mod tcp;\n\n#[cfg(feature = \"tls\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"tls\")))]\npub mod tls;\n\n#[cfg(feature = \"websocket\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"websocket\")))]\npub mod websocket;\n\n#[cfg(feature = \"cookies\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"cookies\")))]\npub mod cookie;\n\n#[cfg(feature = \"jwt\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"jwt\")))]\npub mod jwt;\n\n#[cfg(feature = \"compress\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"compress\")))]\npub mod compress;\n\n#[cfg(feature = \"jsonrpc\")]\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"jsonrpc\")))]\npub mod jsonrpc;\n\npub mod body;\npub mod cors;\npub mod forward;\npub mod logger;\npub mod query;\npub mod stream;\n\n/// Reexport all extension traits.\npub mod preload {\n    pub use crate::body::PowerBody;\n    #[cfg(feature = \"cookies\")]\n    pub use crate::cookie::{CookieGetter, CookieSetter};\n    pub use crate::forward::Forward;\n    #[cfg(feature = \"jwt\")]\n    pub use crate::jwt::JwtVerifier;\n    pub use crate::query::Query;\n    #[cfg(feature = \"router\")]\n    pub use crate::router::RouterParam;\n    #[cfg(feature = \"tcp\")]\n    #[doc(no_inline)]\n    pub use crate::tcp::Listener;\n    #[cfg(all(feature = \"tcp\", feature = \"tls\"))]\n    #[doc(no_inline)]\n    pub use crate::tls::TlsListener;\n}\n"
  },
  {
    "path": "roa/src/logger.rs",
    "content": "//! This module provides a middleware `logger`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::logger::logger;\n//! use roa::preload::*;\n//! use roa::App;\n//! use roa::http::StatusCode;\n//! use tokio::task::spawn;\n//!\n//! #[tokio::main]\n//! async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//!     pretty_env_logger::init();\n//!     let app = App::new()\n//!         .gate(logger)\n//!         .end(\"Hello, World\");\n//!     let (addr, server) = app.run()?;\n//!     spawn(server);\n//!     let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n//!     assert_eq!(StatusCode::OK, resp.status());\n//!     Ok(())\n//! }\n//! ```\n\nuse std::pin::Pin;\nuse std::time::Instant;\nuse std::{io, mem};\n\nuse bytes::Bytes;\nuse bytesize::ByteSize;\nuse futures::task::{self, Poll};\nuse futures::{Future, Stream};\nuse roa_core::http::{Method, StatusCode};\nuse tracing::{error, info};\n\nuse crate::http::Uri;\nuse crate::{Context, Executor, JoinHandle, Next, Result};\n\n/// A finite-state machine to log success information in each successful response.\nenum StreamLogger<S> {\n    /// Polling state, as a body stream.\n    Polling { stream: S, task: LogTask },\n\n    /// Logging state, as a logger future.\n    Logging(JoinHandle<()>),\n\n    /// Complete, as a empty stream.\n    Complete,\n}\n\n/// A task structure to log when polling is complete.\n#[derive(Clone)]\nstruct LogTask {\n    counter: u64,\n    method: Method,\n    status_code: StatusCode,\n    uri: Uri,\n    start: Instant,\n    exec: Executor,\n}\n\nimpl LogTask {\n    #[inline]\n    fn log(&self) -> JoinHandle<()> {\n        let LogTask {\n            counter,\n            method,\n            status_code,\n            uri,\n            start,\n            exec,\n        } = self.clone();\n        exec.spawn_blocking(move || {\n            info!(\n                \"<-- {} {} {}ms {} {}\",\n                method,\n                uri,\n                start.elapsed().as_millis(),\n                ByteSize(counter),\n                status_code,\n            )\n        })\n    }\n}\n\nimpl<S> Stream for StreamLogger<S>\nwhere\n    S: 'static + Send + Send + Unpin + Stream<Item = io::Result<Bytes>>,\n{\n    type Item = io::Result<Bytes>;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {\n        match &mut *self {\n            StreamLogger::Polling { stream, task } => {\n                match futures::ready!(Pin::new(stream).poll_next(cx)) {\n                    Some(Ok(bytes)) => {\n                        task.counter += bytes.len() as u64;\n                        Poll::Ready(Some(Ok(bytes)))\n                    }\n                    None => {\n                        let handler = task.log();\n                        *self = StreamLogger::Logging(handler);\n                        self.poll_next(cx)\n                    }\n                    err => Poll::Ready(err),\n                }\n            }\n\n            StreamLogger::Logging(handler) => {\n                futures::ready!(Pin::new(handler).poll(cx));\n                *self = StreamLogger::Complete;\n                self.poll_next(cx)\n            }\n\n            StreamLogger::Complete => Poll::Ready(None),\n        }\n    }\n}\n\n/// A middleware to log information about request and response.\n///\n/// Based on crate `log`, the log level must be greater than `INFO` to log all information,\n/// and should be greater than `ERROR` when you need error information only.\npub async fn logger<S>(ctx: &mut Context<S>, next: Next<'_>) -> Result {\n    info!(\"--> {} {}\", ctx.method(), ctx.uri().path());\n    let start = Instant::now();\n    let mut result = next.await;\n\n    let method = ctx.method().clone();\n    let uri = ctx.uri().clone();\n    let exec = ctx.exec.clone();\n\n    match &mut result {\n        Err(status) => {\n            let status_code = status.status_code;\n            let message = if status.expose {\n                status.message.clone()\n            } else {\n                // set expose to true; then root status_handler won't log this status.\n                status.expose = true;\n\n                // take unexposed message\n                mem::take(&mut status.message)\n            };\n            ctx.exec\n                .spawn_blocking(move || {\n                    error!(\"<-- {} {} {}\\n{}\", method, uri, status_code, message,);\n                })\n                .await\n        }\n        Ok(_) => {\n            let status_code = ctx.status();\n            // logging when body polling complete.\n            let logger = StreamLogger::Polling {\n                stream: mem::take(&mut ctx.resp.body),\n                task: LogTask {\n                    counter: 0,\n                    method,\n                    uri,\n                    status_code,\n                    start,\n                    exec,\n                },\n            };\n            ctx.resp.write_stream(logger);\n        }\n    }\n    result\n}\n"
  },
  {
    "path": "roa/src/query.rs",
    "content": "//! This module provides a middleware `query_parser` and a context extension `Query`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::query::query_parser;\n//! use roa::{App, Context};\n//! use roa::http::StatusCode;\n//! use roa::preload::*;\n//! use tokio::task::spawn;\n//!\n//! async fn must(ctx: &mut Context) -> roa::Result {\n//!     assert_eq!(\"Hexilee\", &*ctx.must_query(\"name\")?);\n//!     Ok(())\n//! }\n//!\n//! #[tokio::main]\n//! async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//!     let app = App::new()\n//!         .gate(query_parser)\n//!         .end(must);\n//!     let (addr, server) = app.run()?;\n//!     spawn(server);\n//!     let resp = reqwest::get(&format!(\"http://{}?name=Hexilee\", addr)).await?;\n//!     assert_eq!(StatusCode::OK, resp.status());\n//!     Ok(())\n//! }\n//! ```\n\nuse url::form_urlencoded::parse;\n\nuse crate::http::StatusCode;\nuse crate::{Context, Next, Result, Status, Variable};\n\n/// A scope to store and load variables in Context::storage.\nstruct QueryScope;\n\n/// A context extension.\n/// This extension must be used in downstream of middleware `query_parser`,\n/// otherwise you cannot get expected query variable.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa::query::query_parser;\n/// use roa::{App, Context};\n/// use roa::http::StatusCode;\n/// use roa::preload::*;\n/// use tokio::task::spawn;\n///\n/// async fn must(ctx: &mut Context) -> roa::Result {\n///     assert_eq!(\"Hexilee\", &*ctx.must_query(\"name\")?);\n///     Ok(())\n/// }\n///\n/// #[tokio::main]\n/// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n///     // downstream of `query_parser`\n///     let app = App::new()\n///         .gate(query_parser)\n///         .end(must);\n///     let (addr, server) = app.run()?;\n///     spawn(server);\n///     let resp = reqwest::get(&format!(\"http://{}?name=Hexilee\", addr)).await?;\n///     assert_eq!(StatusCode::OK, resp.status());\n///\n///     // miss `query_parser`\n///     let app = App::new().end(must);\n///     let (addr, server) = app.run()?;\n///     spawn(server);\n///     let resp = reqwest::get(&format!(\"http://{}?name=Hexilee\", addr)).await?;\n///     assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n///     Ok(())\n/// }\n/// ```\npub trait Query {\n    /// Must get a variable, throw 400 BAD_REQUEST if it not exists.\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa::query::query_parser;\n    /// use roa::{App, Context};\n    /// use roa::http::StatusCode;\n    /// use roa::preload::*;\n    /// use tokio::task::spawn;\n    ///\n    /// async fn must(ctx: &mut Context) -> roa::Result {\n    ///     assert_eq!(\"Hexilee\", &*ctx.must_query(\"name\")?);\n    ///     Ok(())\n    /// }\n    ///\n    /// #[tokio::main]\n    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    ///     // downstream of `query_parser`\n    ///     let app = App::new()\n    ///         .gate(query_parser)\n    ///         .end(must);\n    ///     let (addr, server) = app.run()?;\n    ///     spawn(server);\n    ///     let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    ///     assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n    ///     Ok(())\n    /// }\n    /// ```\n    fn must_query<'a>(&self, name: &'a str) -> Result<Variable<'a, String>>;\n\n    /// Query a variable, return `None` if it not exists.\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa::query::query_parser;\n    /// use roa::{App, Context};\n    /// use roa::http::StatusCode;\n    /// use roa::preload::*;\n    /// use tokio::task::spawn;\n    ///\n    /// async fn test(ctx: &mut Context) -> roa::Result {\n    ///     assert!(ctx.query(\"name\").is_none());\n    ///     Ok(())\n    /// }\n    ///\n    /// #[tokio::main]\n    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    ///     // downstream of `query_parser`\n    ///     let app = App::new()\n    ///         .gate(query_parser)\n    ///         .end(test);\n    ///     let (addr, server) = app.run()?;\n    ///     spawn(server);\n    ///     let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    ///     assert_eq!(StatusCode::OK, resp.status());\n    ///     Ok(())\n    /// }\n    /// ```\n    fn query<'a>(&self, name: &'a str) -> Option<Variable<'a, String>>;\n}\n\n/// A middleware to parse query.\n#[inline]\npub async fn query_parser<S>(ctx: &mut Context<S>, next: Next<'_>) -> Result {\n    let query_string = ctx.uri().query().unwrap_or(\"\");\n    let pairs: Vec<(String, String)> = parse(query_string.as_bytes()).into_owned().collect();\n    for (key, value) in pairs {\n        ctx.store_scoped(QueryScope, key, value);\n    }\n    next.await\n}\n\nimpl<S> Query for Context<S> {\n    #[inline]\n    fn must_query<'a>(&self, name: &'a str) -> Result<Variable<'a, String>> {\n        self.query(name).ok_or_else(|| {\n            Status::new(\n                StatusCode::BAD_REQUEST,\n                format!(\"query `{}` is required\", name),\n                true,\n            )\n        })\n    }\n    #[inline]\n    fn query<'a>(&self, name: &'a str) -> Option<Variable<'a, String>> {\n        self.load_scoped::<QueryScope, String>(name)\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use tokio::task::spawn;\n\n    use crate::http::StatusCode;\n    use crate::preload::*;\n    use crate::query::query_parser;\n    use crate::{App, Context};\n\n    #[tokio::test]\n    async fn query() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(\"Hexilee\", &*ctx.must_query(\"name\")?);\n            Ok(())\n        }\n\n        // miss key\n        let (addr, server) = App::new().gate(query_parser).end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}/\", addr)).await?;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n\n        // string value\n        let (addr, server) = App::new().gate(query_parser).end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}?name=Hexilee\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn query_parse() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(120, ctx.must_query(\"age\")?.parse::<u64>()?);\n            Ok(())\n        }\n        // invalid int value\n        let (addr, server) = App::new().gate(query_parser).end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}?age=Hexilee\", addr)).await?;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n\n        let (addr, server) = App::new().gate(query_parser).end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}?age=120\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn query_action() -> Result<(), Box<dyn std::error::Error>> {\n        async fn test(ctx: &mut Context) -> crate::Result {\n            assert_eq!(\"Hexilee\", &*ctx.must_query(\"name\")?);\n            assert_eq!(\"rust\", &*ctx.must_query(\"lang\")?);\n            Ok(())\n        }\n        let (addr, server) = App::new().gate(query_parser).end(test).run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}?name=Hexilee&lang=rust\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/router/endpoints/dispatcher.rs",
    "content": "use std::collections::HashMap;\n\nuse doc_comment::doc_comment;\n\nuse super::method_not_allowed;\nuse crate::http::Method;\nuse crate::{async_trait, Context, Endpoint, Result};\n\nmacro_rules! impl_http_methods {\n    ($end:ident, $method:expr) => {\n        doc_comment! {\n        concat!(\"Method to add or override endpoint on \", stringify!($method), \".\n\nYou can use it as follow:\n\n```rust\nuse roa::{App, Context, Result};\nuse roa::router::get;\n\nasync fn foo(ctx: &mut Context) -> Result {\n    Ok(())\n}\n\nasync fn bar(ctx: &mut Context) -> Result {\n    Ok(())\n}\n\nlet app = App::new().end(get(foo).\", stringify!($end), \"(bar));\n```\"),\n            pub fn $end(mut self, endpoint: impl for<'a> Endpoint<'a, S>) -> Self {\n                self.0.insert($method, Box::new(endpoint));\n                self\n            }\n        }\n    };\n}\n\nmacro_rules! impl_http_functions {\n    ($end:ident, $method:expr) => {\n        doc_comment! {\n        concat!(\"Function to construct dispatcher with \", stringify!($method), \" and an endpoint.\n\nYou can use it as follow:\n\n```rust\nuse roa::{App, Context, Result};\nuse roa::router::\", stringify!($end), \";\n\nasync fn end(ctx: &mut Context) -> Result {\n    Ok(())\n}\n\nlet app = App::new().end(\", stringify!($end), \"(end));\n```\"),\n            pub fn $end<S>(endpoint: impl for<'a> Endpoint<'a, S>) -> Dispatcher<S> {\n                    Dispatcher::<S>::default().$end(endpoint)\n            }\n        }\n    };\n}\n\n/// An endpoint wrapper to dispatch requests by http method.\npub struct Dispatcher<S>(HashMap<Method, Box<dyn for<'a> Endpoint<'a, S>>>);\n\nimpl_http_functions!(get, Method::GET);\nimpl_http_functions!(post, Method::POST);\nimpl_http_functions!(put, Method::PUT);\nimpl_http_functions!(patch, Method::PATCH);\nimpl_http_functions!(options, Method::OPTIONS);\nimpl_http_functions!(delete, Method::DELETE);\nimpl_http_functions!(head, Method::HEAD);\nimpl_http_functions!(trace, Method::TRACE);\nimpl_http_functions!(connect, Method::CONNECT);\n\nimpl<S> Dispatcher<S> {\n    impl_http_methods!(get, Method::GET);\n    impl_http_methods!(post, Method::POST);\n    impl_http_methods!(put, Method::PUT);\n    impl_http_methods!(patch, Method::PATCH);\n    impl_http_methods!(options, Method::OPTIONS);\n    impl_http_methods!(delete, Method::DELETE);\n    impl_http_methods!(head, Method::HEAD);\n    impl_http_methods!(trace, Method::TRACE);\n    impl_http_methods!(connect, Method::CONNECT);\n}\n\n/// Empty dispatcher.\nimpl<S> Default for Dispatcher<S> {\n    fn default() -> Self {\n        Self(HashMap::new())\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for Dispatcher<S>\nwhere\n    S: 'static,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result<()> {\n        match self.0.get(ctx.method()) {\n            Some(endpoint) => endpoint.call(ctx).await,\n            None => method_not_allowed(ctx.method()),\n        }\n    }\n}\n"
  },
  {
    "path": "roa/src/router/endpoints/guard.rs",
    "content": "use std::collections::HashSet;\nuse std::iter::FromIterator;\n\nuse super::method_not_allowed;\nuse crate::http::Method;\nuse crate::{async_trait, Context, Endpoint, Result};\n\n/// Methods allowed in `Guard`.\nconst ALL_METHODS: [Method; 9] = [\n    Method::GET,\n    Method::POST,\n    Method::PUT,\n    Method::PATCH,\n    Method::OPTIONS,\n    Method::DELETE,\n    Method::HEAD,\n    Method::TRACE,\n    Method::CONNECT,\n];\n\n/// An endpoint wrapper to guard endpoint by http method.\npub struct Guard<E> {\n    white_list: HashSet<Method>,\n    endpoint: E,\n}\n\n/// Initialize hash set.\nfn hash_set(methods: impl AsRef<[Method]>) -> HashSet<Method> {\n    HashSet::from_iter(methods.as_ref().to_vec())\n}\n\n/// A function to construct guard by white list.\n///\n/// Only requests with http method in list can access this endpoint, otherwise will get a 405 METHOD NOT ALLOWED.\n///\n/// ```\n/// use roa::{App, Context, Result};\n/// use roa::http::Method;\n/// use roa::router::allow;\n///\n/// async fn foo(ctx: &mut Context) -> Result {\n///     Ok(())\n/// }\n///\n/// let app = App::new().end(allow([Method::GET, Method::POST], foo));\n/// ```\npub fn allow<E>(methods: impl AsRef<[Method]>, endpoint: E) -> Guard<E> {\n    Guard {\n        endpoint,\n        white_list: hash_set(methods),\n    }\n}\n\n/// A function to construct guard by black list.\n///\n/// Only requests with http method not in list can access this endpoint, otherwise will get a 405 METHOD NOT ALLOWED.\n///\n/// ```\n/// use roa::{App, Context, Result};\n/// use roa::http::Method;\n/// use roa::router::deny;\n///\n/// async fn foo(ctx: &mut Context) -> Result {\n///     Ok(())\n/// }\n///\n/// let app = App::new().end(deny([Method::PUT, Method::DELETE], foo));\n/// ```\npub fn deny<E>(methods: impl AsRef<[Method]>, endpoint: E) -> Guard<E> {\n    let white_list = hash_set(ALL_METHODS);\n    let black_list = &white_list & &hash_set(methods);\n    Guard {\n        endpoint,\n        white_list: &white_list ^ &black_list,\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S, E> Endpoint<'a, S> for Guard<E>\nwhere\n    E: Endpoint<'a, S>,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        if self.white_list.contains(ctx.method()) {\n            self.endpoint.call(ctx).await\n        } else {\n            method_not_allowed(ctx.method())\n        }\n    }\n}\n"
  },
  {
    "path": "roa/src/router/endpoints.rs",
    "content": "mod dispatcher;\nmod guard;\n\nuse crate::http::{Method, StatusCode};\nuse crate::{throw, Result};\n\n#[inline]\nfn method_not_allowed(method: &Method) -> Result {\n    throw!(\n        StatusCode::METHOD_NOT_ALLOWED,\n        format!(\"Method {} not allowed\", method)\n    )\n}\n\npub use dispatcher::{connect, delete, get, head, options, patch, post, put, trace, Dispatcher};\npub use guard::{allow, deny, Guard};\n"
  },
  {
    "path": "roa/src/router/err.rs",
    "content": "use std::fmt::{self, Display, Formatter};\n\nuse roa_core::http;\n\n/// Error occurring in building route table.\n#[derive(Debug)]\npub enum RouterError {\n    /// Dynamic paths miss variable.\n    MissingVariable(String),\n\n    /// Variables, methods or paths conflict.\n    Conflict(Conflict),\n}\n\n/// Router conflict.\n#[derive(Debug, Eq, PartialEq)]\npub enum Conflict {\n    Path(String),\n    Method(String, http::Method),\n    Variable {\n        paths: (String, String),\n        var_name: String,\n    },\n}\n\nimpl Display for Conflict {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {\n        match self {\n            Conflict::Path(path) => f.write_str(&format!(\"conflict path: `{}`\", path)),\n            Conflict::Method(path, method) => f.write_str(&format!(\n                \"conflict method: `{}` on `{}` is already set\",\n                method, path\n            )),\n            Conflict::Variable { paths, var_name } => f.write_str(&format!(\n                \"conflict variable `{}`: between `{}` and `{}`\",\n                var_name, paths.0, paths.1\n            )),\n        }\n    }\n}\n\nimpl Display for RouterError {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {\n        match self {\n            RouterError::Conflict(conflict) => f.write_str(&format!(\"Conflict! {}\", conflict)),\n            RouterError::MissingVariable(path) => {\n                f.write_str(&format!(\"missing variable on path {}\", path))\n            }\n        }\n    }\n}\n\nimpl From<Conflict> for RouterError {\n    fn from(conflict: Conflict) -> Self {\n        RouterError::Conflict(conflict)\n    }\n}\n\nimpl std::error::Error for Conflict {}\nimpl std::error::Error for RouterError {}\n\n#[cfg(test)]\nmod tests {\n    use roa_core::http;\n\n    use super::{Conflict, RouterError};\n\n    #[test]\n    fn conflict_to_string() {\n        assert_eq!(\n            \"conflict path: `/`\",\n            Conflict::Path(\"/\".to_string()).to_string()\n        );\n        assert_eq!(\n            \"conflict method: `GET` on `/` is already set\",\n            Conflict::Method(\"/\".to_string(), http::Method::GET).to_string()\n        );\n        assert_eq!(\n            \"conflict variable `id`: between `/:id` and `/user/:id`\",\n            Conflict::Variable {\n                paths: (\"/:id\".to_string(), \"/user/:id\".to_string()),\n                var_name: \"id\".to_string()\n            }\n            .to_string()\n        );\n    }\n\n    #[test]\n    fn err_to_string() {\n        assert_eq!(\n            \"Conflict! conflict path: `/`\",\n            RouterError::Conflict(Conflict::Path(\"/\".to_string())).to_string()\n        );\n        assert_eq!(\n            \"missing variable on path /:\",\n            RouterError::MissingVariable(\"/:\".to_string()).to_string()\n        );\n    }\n}\n"
  },
  {
    "path": "roa/src/router/path.rs",
    "content": "use std::collections::HashSet;\nuse std::convert::AsRef;\nuse std::str::FromStr;\n\nuse regex::{escape, Captures, Regex};\n\nuse super::{Conflict, RouterError};\n\n/// Match pattern *{variable}\nconst WILDCARD: &str = r\"\\*\\{(?P<var>\\w*)\\}\";\n\n/// Match pattern /:variable/\nconst VARIABLE: &str = r\"/:(?P<var>\\w*)/\";\n\n/// {/path path/ /path/} => /path/\npub fn standardize_path(raw_path: &str) -> String {\n    format!(\"/{}/\", raw_path.trim_matches('/'))\n}\n\n/// Join multiple segments.\npub fn join_path<'a>(paths: impl 'a + AsRef<[&'a str]>) -> String {\n    paths\n        .as_ref()\n        .iter()\n        .map(|path| path.trim_matches('/'))\n        .filter(|path| !path.is_empty())\n        .collect::<Vec<&str>>()\n        .join(\"/\")\n}\n\n/// Build pattern.\nfn must_build(pattern: &str) -> Regex {\n    Regex::new(pattern).unwrap_or_else(|err| {\n        panic!(\n            r#\"{}\n                regex pattern {} is invalid, this is a bug of roa::router::path.\n                please report it to https://github.com/Hexilee/roa\"#,\n            err, pattern\n        )\n    })\n}\n\n/// Parsed path.\n#[derive(Clone)]\npub enum Path {\n    Static(String),\n    Dynamic(RegexPath),\n}\n\n/// Dynamic path.\n#[derive(Clone)]\npub struct RegexPath {\n    pub raw: String,\n    pub vars: HashSet<String>,\n    pub re: Regex,\n}\n\nimpl FromStr for Path {\n    type Err = RouterError;\n    fn from_str(raw_path: &str) -> Result<Self, Self::Err> {\n        let path = standardize_path(raw_path);\n        Ok(match path_to_regexp(&path)? {\n            None => Path::Static(path),\n            Some((pattern, vars)) => Path::Dynamic(RegexPath {\n                raw: path,\n                vars,\n                re: must_build(&format!(r\"^{}$\", pattern)),\n            }),\n        })\n    }\n}\n\nfn path_to_regexp(path: &str) -> Result<Option<(String, HashSet<String>)>, RouterError> {\n    let mut pattern = escape(path);\n    let mut vars = HashSet::new();\n    let wildcard_re = must_build(WILDCARD);\n    let variable_re = must_build(VARIABLE);\n    let wildcards: Vec<Captures> = wildcard_re.captures_iter(path).collect();\n    let variable_template = path.replace('/', \"//\"); // to match continuous variables like /:year/:month/:day/\n    let variables: Vec<Captures> = variable_re.captures_iter(&variable_template).collect();\n    if wildcards.is_empty() && variables.is_empty() {\n        Ok(None)\n    } else {\n        // detect variable conflicts.\n        let try_add_variable = |set: &mut HashSet<String>, variable: String| {\n            if set.insert(variable.clone()) {\n                Ok(())\n            } else {\n                Err(Conflict::Variable {\n                    paths: (path.to_string(), path.to_string()),\n                    var_name: variable,\n                })\n            }\n        };\n\n        // match wildcard patterns\n        for cap in wildcards {\n            let variable = &cap[\"var\"];\n            if variable.is_empty() {\n                return Err(RouterError::MissingVariable(path.to_string()));\n            }\n            let var = escape(variable);\n            pattern = pattern.replace(\n                &escape(&format!(r\"*{{{}}}\", variable)),\n                &format!(r\"(?P<{}>\\S+)\", &var),\n            );\n            try_add_variable(&mut vars, var)?;\n        }\n\n        // match segment variable patterns\n        for cap in variables {\n            let variable = &cap[\"var\"];\n            if variable.is_empty() {\n                return Err(RouterError::MissingVariable(path.to_string()));\n            }\n            let var = escape(variable);\n            pattern = pattern.replace(\n                &escape(&format!(r\":{}\", variable)),\n                &format!(r\"(?P<{}>[^\\s/]+)\", &var),\n            );\n            try_add_variable(&mut vars, var)?;\n        }\n        Ok(Some((pattern, vars)))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use test_case::test_case;\n\n    use super::{must_build, path_to_regexp, Path, VARIABLE, WILDCARD};\n\n    #[test_case(\"/:id/\"; \"pure dynamic\")]\n    #[test_case(\"/user/:id/\"; \"static prefix\")]\n    #[test_case(\"/user/:id/name\"; \"static prefix and suffix\")]\n    fn var_regex_match(path: &str) {\n        let re = must_build(VARIABLE);\n        let cap = re.captures(path);\n        assert!(cap.is_some());\n        assert_eq!(\"id\", &cap.unwrap()[\"var\"]);\n    }\n\n    #[test_case(\"/-:id/\"; \"invalid prefix\")]\n    #[test_case(\"/:i-d/\"; \"invalid variable name\")]\n    #[test_case(\"/:id-/\"; \"invalid suffix\")]\n    fn var_regex_mismatch(path: &str) {\n        let re = must_build(VARIABLE);\n        let cap = re.captures(path);\n        assert!(cap.is_none());\n    }\n\n    #[test_case(\"*{id}\"; \"pure dynamic\")]\n    #[test_case(\"user-*{id}\"; \"static prefix\")]\n    #[test_case(\"user-*{id}-name\"; \"static prefix and suffix\")]\n    fn wildcard_regex_match(path: &str) {\n        let re = must_build(WILDCARD);\n        let cap = re.captures(path);\n        assert!(cap.is_some());\n        assert_eq!(\"id\", &cap.unwrap()[\"var\"]);\n    }\n\n    #[test_case(\"*\"; \"no variable\")]\n    #[test_case(\"*{-id}\"; \"invalid variable name\")]\n    fn wildcard_regex_mismatch(path: &str) {\n        let re = must_build(WILDCARD);\n        let cap = re.captures(path);\n        assert!(cap.is_none());\n    }\n\n    #[test_case(r\"/:id/\" => r\"/(?P<id>[^\\s/]+)/\"; \"single variable\")]\n    #[test_case(r\"/:year/:month/:day/\" => r\"/(?P<year>[^\\s/]+)/(?P<month>[^\\s/]+)/(?P<day>[^\\s/]+)/\"; \"multiple variable\")]\n    #[test_case(r\"*{id}\" => r\"(?P<id>\\S+)\"; \"single wildcard\")]\n    #[test_case(r\"*{year}_*{month}_*{day}\" => r\"(?P<year>\\S+)_(?P<month>\\S+)_(?P<day>\\S+)\"; \"multiple wildcard\")]\n    fn path_to_regexp_dynamic_pattern(path: &str) -> String {\n        path_to_regexp(path).unwrap().unwrap().0\n    }\n\n    #[test_case(r\"/id/\")]\n    #[test_case(r\"/user/post/\")]\n    fn path_to_regexp_static(path: &str) {\n        assert!(path_to_regexp(path).unwrap().is_none())\n    }\n\n    #[test_case(r\"/:/\"; \"missing variable name\")]\n    #[test_case(r\"*{}\"; \"wildcard missing variable name\")]\n    #[test_case(r\"/:id/:id/\"; \"conflict variable\")]\n    #[test_case(r\"*{id}-*{id}\"; \"wildcard conflict variable\")]\n    #[test_case(r\"/:id/*{id}\"; \"mix conflict variable\")]\n    fn path_to_regexp_err(path: &str) {\n        assert!(path_to_regexp(path).is_err())\n    }\n\n    fn path_match(pattern: &str, path: &str) {\n        let pattern: Path = pattern.parse().unwrap();\n        match pattern {\n            Path::Static(pattern) => panic!(\"`{}` should be dynamic\", pattern),\n            Path::Dynamic(re) => assert!(re.re.is_match(path)),\n        }\n    }\n\n    fn path_not_match(pattern: &str, path: &str) {\n        let pattern: Path = pattern.parse().unwrap();\n        match pattern {\n            Path::Static(pattern) => panic!(\"`{}` should be dynamic\", pattern),\n            Path::Dynamic(re) => {\n                println!(\"regex: {}\", re.re.to_string());\n                assert!(!re.re.is_match(path))\n            }\n        }\n    }\n\n    #[test_case(r\"/user/1/\")]\n    #[test_case(r\"/user/65535/\")]\n    fn single_variable_path_match(path: &str) {\n        path_match(r\"/user/:id\", path)\n    }\n\n    #[test_case(r\"/2000/01/01/\")]\n    #[test_case(r\"/2020/02/20/\")]\n    fn multiple_variable_path_match(path: &str) {\n        path_match(r\"/:year/:month/:day\", path)\n    }\n\n    #[test_case(r\"/usr/include/boost/boost.h/\")]\n    #[test_case(r\"/usr/include/uv/uv.h/\")]\n    fn segment_wildcard_path_match(path: &str) {\n        path_match(r\"/usr/include/*{dir}/*{file}.h\", path)\n    }\n\n    #[test_case(r\"/srv/static/app/index.html/\")]\n    #[test_case(r\"/srv/static/../../index.html/\")]\n    fn full_wildcard_path_match(path: &str) {\n        path_match(r\"/srv/static/*{path}/\", path)\n    }\n\n    #[test_case(r\"/srv/app/index.html/\")]\n    #[test_case(r\"/srv/../../index.html/\")]\n    fn variable_path_not_match(path: &str) {\n        path_not_match(r\"/srv/:path/\", path)\n    }\n\n    #[should_panic]\n    #[test]\n    fn must_build_fails() {\n        must_build(r\"{\");\n    }\n}\n"
  },
  {
    "path": "roa/src/router.rs",
    "content": "//! This module provides a context extension `RouterParam` and\n//! many endpoint wrappers like `Router`, `Dispatcher` and `Guard`.\n//!\n//! ### Example\n//!\n//! ```rust\n//! use roa::router::{Router, RouterParam, get, allow};\n//! use roa::{App, Context, Status, MiddlewareExt, Next};\n//! use roa::http::{StatusCode, Method};\n//! use roa::tcp::Listener;\n//! use tokio::task::spawn;\n//!\n//!\n//! async fn gate(_ctx: &mut Context, next: Next<'_>) -> Result<(), Status> {\n//!     next.await\n//! }\n//!\n//! async fn query(ctx: &mut Context) -> Result<(), Status> {\n//!     Ok(())\n//! }\n//!\n//! async fn create(ctx: &mut Context) -> Result<(), Status> {\n//!     Ok(())\n//! }\n//!\n//! async fn graphql(ctx: &mut Context) -> Result<(), Status> {\n//!     Ok(())\n//! }\n//!\n//! #[tokio::main]\n//! async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//!     let router = Router::new()\n//!         .gate(gate)\n//!         .on(\"/restful\", get(query).post(create))\n//!         .on(\"/graphql\", allow([Method::GET, Method::POST], graphql));\n//!     let app = App::new()\n//!         .end(router.routes(\"/api\")?);\n//!     let (addr, server) = app.run()?;\n//!     spawn(server);\n//!     let resp = reqwest::get(&format!(\"http://{}/api/restful\", addr)).await?;\n//!     assert_eq!(StatusCode::OK, resp.status());\n//!\n//!     let resp = reqwest::get(&format!(\"http://{}/restful\", addr)).await?;\n//!     assert_eq!(StatusCode::NOT_FOUND, resp.status());\n//!     Ok(())\n//! }\n//! ```\n//!\n\nmod endpoints;\nmod err;\nmod path;\n\nuse std::convert::AsRef;\nuse std::result::Result as StdResult;\n\n#[doc(inline)]\npub use endpoints::*;\nuse err::Conflict;\n#[doc(inline)]\npub use err::RouterError;\nuse path::{join_path, standardize_path, Path, RegexPath};\nuse percent_encoding::percent_decode_str;\nuse radix_trie::Trie;\n\nuse crate::http::StatusCode;\nuse crate::{\n    async_trait, throw, Boxed, Context, Endpoint, EndpointExt, Middleware, MiddlewareExt, Result,\n    Shared, Status, Variable,\n};\n\n/// A private scope to store and load variables in Context::storage.\nstruct RouterScope;\n\n/// A context extension.\n/// This extension must be used in `Router`,\n/// otherwise you cannot get expected router parameters.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa::router::{Router, RouterParam};\n/// use roa::{App, Context, Status};\n/// use roa::http::StatusCode;\n/// use roa::tcp::Listener;\n/// use tokio::task::spawn;\n///\n/// async fn test(ctx: &mut Context) -> Result<(), Status> {\n///     let id: u64 = ctx.must_param(\"id\")?.parse()?;\n///     assert_eq!(0, id);\n///     Ok(())\n/// }\n///\n/// #[tokio::main]\n/// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n///     let router = Router::new().on(\"/:id\", test);\n///     let app = App::new().end(router.routes(\"/user\")?);\n///     let (addr, server) = app.run()?;\n///     spawn(server);\n///     let resp = reqwest::get(&format!(\"http://{}/user/0\", addr)).await?;\n///     assert_eq!(StatusCode::OK, resp.status());\n///     Ok(())\n/// }\n///\n///\n/// ```\npub trait RouterParam {\n    /// Must get a router parameter, throw 500 INTERNAL SERVER ERROR if it not exists.\n    fn must_param<'a>(&self, name: &'a str) -> Result<Variable<'a, String>>;\n\n    /// Try to get a router parameter, return `None` if it not exists.\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa::router::{Router, RouterParam};\n    /// use roa::{App, Context, Status};\n    /// use roa::http::StatusCode;\n    /// use roa::tcp::Listener;\n    /// use tokio::task::spawn;\n    ///\n    /// async fn test(ctx: &mut Context) -> Result<(), Status> {\n    ///     assert!(ctx.param(\"name\").is_none());\n    ///     Ok(())\n    /// }\n    ///\n    /// #[tokio::main]\n    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    ///     let router = Router::new().on(\"/:id\", test);\n    ///     let app = App::new().end(router.routes(\"/user\")?);\n    ///     let (addr, server) = app.run()?;\n    ///     spawn(server);\n    ///     let resp = reqwest::get(&format!(\"http://{}/user/0\", addr)).await?;\n    ///     assert_eq!(StatusCode::OK, resp.status());\n    ///     Ok(())\n    /// }\n    ///\n    ///\n    /// ```\n    fn param<'a>(&self, name: &'a str) -> Option<Variable<'a, String>>;\n}\n\n/// A builder of `RouteTable`.\npub struct Router<S> {\n    middleware: Shared<S>,\n    endpoints: Vec<(String, Boxed<S>)>,\n}\n\n/// An endpoint to route request by uri path.\npub struct RouteTable<S> {\n    static_route: Trie<String, Boxed<S>>,\n    dynamic_route: Vec<(RegexPath, Boxed<S>)>,\n}\n\nimpl<S> Router<S>\nwhere\n    S: 'static,\n{\n    /// Construct a new router.\n    pub fn new() -> Self {\n        Self {\n            middleware: ().shared(),\n            endpoints: Vec::new(),\n        }\n    }\n\n    /// Register a new endpoint.\n    pub fn on(mut self, path: &'static str, endpoint: impl for<'a> Endpoint<'a, S>) -> Self {\n        self.endpoints\n            .push((path.to_string(), self.register(endpoint)));\n        self\n    }\n\n    /// Chain an endpoint to Router::middleware.\n    fn register(&self, endpoint: impl for<'a> Endpoint<'a, S>) -> Boxed<S> {\n        self.middleware.clone().end(endpoint).boxed()\n    }\n\n    /// Include another router with prefix.\n    pub fn include(mut self, prefix: &'static str, router: Router<S>) -> Self {\n        for (path, endpoint) in router.endpoints {\n            self.endpoints\n                .push((join_path([prefix, path.as_str()]), self.register(endpoint)))\n        }\n        self\n    }\n\n    /// Chain a middleware to Router::middleware.\n    pub fn gate(self, next: impl for<'a> Middleware<'a, S>) -> Router<S> {\n        let Self {\n            middleware,\n            endpoints,\n        } = self;\n        Self {\n            middleware: middleware.chain(next).shared(),\n            endpoints,\n        }\n    }\n\n    /// Build RouteTable with path prefix.\n    pub fn routes(self, prefix: &'static str) -> StdResult<RouteTable<S>, RouterError> {\n        let mut route_table = RouteTable::default();\n        for (raw_path, endpoint) in self.endpoints {\n            route_table.insert(join_path([prefix, raw_path.as_str()]), endpoint)?;\n        }\n        Ok(route_table)\n    }\n}\n\nimpl<S> RouteTable<S>\nwhere\n    S: 'static,\n{\n    fn new() -> Self {\n        Self {\n            static_route: Trie::new(),\n            dynamic_route: Vec::new(),\n        }\n    }\n\n    /// Insert endpoint to table.\n    fn insert(\n        &mut self,\n        raw_path: impl AsRef<str>,\n        endpoint: Boxed<S>,\n    ) -> StdResult<(), RouterError> {\n        match raw_path.as_ref().parse()? {\n            Path::Static(path) => {\n                if self.static_route.insert(path.clone(), endpoint).is_some() {\n                    return Err(Conflict::Path(path).into());\n                }\n            }\n            Path::Dynamic(regex_path) => self.dynamic_route.push((regex_path, endpoint)),\n        }\n        Ok(())\n    }\n}\n\nimpl<S> Default for Router<S>\nwhere\n    S: 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<S> Default for RouteTable<S>\nwhere\n    S: 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for RouteTable<S>\nwhere\n    S: 'static,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        let uri = ctx.uri();\n        // standardize path\n        let path = standardize_path(&percent_decode_str(uri.path()).decode_utf8().map_err(\n            |err| {\n                Status::new(\n                    StatusCode::BAD_REQUEST,\n                    format!(\"{}\\npath `{}` is not a valid utf-8 string\", err, uri.path()),\n                    true,\n                )\n            },\n        )?);\n\n        // search static routes\n        if let Some(end) = self.static_route.get(&path) {\n            return end.call(ctx).await;\n        }\n\n        // search dynamic routes\n        for (regexp_path, end) in self.dynamic_route.iter() {\n            if let Some(cap) = regexp_path.re.captures(&path) {\n                for var in regexp_path.vars.iter() {\n                    ctx.store_scoped(RouterScope, var.to_string(), cap[var.as_str()].to_string());\n                }\n                return end.call(ctx).await;\n            }\n        }\n\n        // 404 NOT FOUND\n        throw!(StatusCode::NOT_FOUND)\n    }\n}\n\nimpl<S> RouterParam for Context<S> {\n    #[inline]\n    fn must_param<'a>(&self, name: &'a str) -> Result<Variable<'a, String>> {\n        self.param(name).ok_or_else(|| {\n            Status::new(\n                StatusCode::INTERNAL_SERVER_ERROR,\n                format!(\"router variable `{}` is required\", name),\n                false,\n            )\n        })\n    }\n    #[inline]\n    fn param<'a>(&self, name: &'a str) -> Option<Variable<'a, String>> {\n        self.load_scoped::<RouterScope, String>(name)\n    }\n}\n\n#[cfg(all(test, feature = \"tcp\"))]\nmod tests {\n    use encoding::EncoderTrap;\n    use percent_encoding::NON_ALPHANUMERIC;\n    use tokio::task::spawn;\n\n    use super::Router;\n    use crate::http::StatusCode;\n    use crate::tcp::Listener;\n    use crate::{App, Context, Next, Status};\n\n    async fn gate(ctx: &mut Context, next: Next<'_>) -> Result<(), Status> {\n        ctx.store(\"id\", \"0\".to_string());\n        next.await\n    }\n\n    async fn test(ctx: &mut Context) -> Result<(), Status> {\n        let id: u64 = ctx.load::<String>(\"id\").unwrap().parse()?;\n        assert_eq!(0, id);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn gate_test() -> Result<(), Box<dyn std::error::Error>> {\n        let router = Router::new().gate(gate).on(\"/\", test);\n        let app = App::new().end(router.routes(\"/route\")?);\n        let (addr, server) = app.run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}/route\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn route() -> Result<(), Box<dyn std::error::Error>> {\n        let user_router = Router::new().on(\"/\", test);\n        let router = Router::new().gate(gate).include(\"/user\", user_router);\n        let app = App::new().end(router.routes(\"/route\")?);\n        let (addr, server) = app.run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}/route/user\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n\n    #[test]\n    fn conflict_path() -> Result<(), Box<dyn std::error::Error>> {\n        let evil_router = Router::new().on(\"/endpoint\", test);\n        let router = Router::new()\n            .on(\"/route/endpoint\", test)\n            .include(\"/route\", evil_router);\n        let ret = router.routes(\"/\");\n        assert!(ret.is_err());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn route_not_found() -> Result<(), Box<dyn std::error::Error>> {\n        let app = App::new().end(Router::default().routes(\"/\")?);\n        let (addr, server) = app.run()?;\n        spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::NOT_FOUND, resp.status());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn non_utf8_uri() -> Result<(), Box<dyn std::error::Error>> {\n        let app = App::new().end(Router::default().routes(\"/\")?);\n        let (addr, server) = app.run()?;\n        spawn(server);\n        let gbk_path = encoding::label::encoding_from_whatwg_label(\"gbk\")\n            .unwrap()\n            .encode(\"路由\", EncoderTrap::Strict)\n            .unwrap();\n        let encoded_path =\n            percent_encoding::percent_encode(&gbk_path, NON_ALPHANUMERIC).to_string();\n        let uri = format!(\"http://{}/{}\", addr, encoded_path);\n        let resp = reqwest::get(&uri).await?;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n        assert!(resp\n            .text()\n            .await?\n            .ends_with(\"path `/%C2%B7%D3%C9` is not a valid utf-8 string\"));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/stream.rs",
    "content": "//! this module provides a stream adaptor `AsyncStream`\n\nuse std::io;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures::io::{AsyncRead as Read, AsyncWrite as Write};\nuse futures::ready;\nuse tokio::io::{AsyncRead, AsyncWrite, ReadBuf};\nuse tracing::{instrument, trace};\n\n/// A adaptor between futures::io::{AsyncRead, AsyncWrite} and tokio::io::{AsyncRead, AsyncWrite}.\npub struct AsyncStream<IO>(pub IO);\n\nimpl<IO> AsyncRead for AsyncStream<IO>\nwhere\n    IO: Unpin + Read,\n{\n    #[inline]\n    fn poll_read(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        let read_size = ready!(Pin::new(&mut self.0).poll_read(cx, buf.initialize_unfilled()))?;\n        buf.advance(read_size);\n        Poll::Ready(Ok(()))\n    }\n}\n\nimpl<IO> AsyncWrite for AsyncStream<IO>\nwhere\n    IO: Unpin + Write,\n{\n    #[inline]\n    fn poll_write(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        Pin::new(&mut self.0).poll_write(cx, buf)\n    }\n\n    #[inline]\n    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.0).poll_flush(cx)\n    }\n\n    #[inline]\n    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.0).poll_close(cx)\n    }\n}\n\nimpl<IO> Read for AsyncStream<IO>\nwhere\n    IO: Unpin + AsyncRead,\n{\n    #[inline]\n    #[instrument(skip(self, cx, buf))]\n    fn poll_read(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &mut [u8],\n    ) -> Poll<io::Result<usize>> {\n        let mut read_buf = ReadBuf::new(buf);\n        ready!(Pin::new(&mut self.0).poll_read(cx, &mut read_buf))?;\n        trace!(\"read {} bytes\", read_buf.filled().len());\n        Poll::Ready(Ok(read_buf.filled().len()))\n    }\n}\n\nimpl<IO> Write for AsyncStream<IO>\nwhere\n    IO: Unpin + AsyncWrite,\n{\n    #[inline]\n    #[instrument(skip(self, cx, buf))]\n    fn poll_write(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        let size = ready!(Pin::new(&mut self.0).poll_write(cx, buf))?;\n        trace!(\"wrote {} bytes\", size);\n        Poll::Ready(Ok(size))\n    }\n\n    #[inline]\n    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.0).poll_flush(cx)\n    }\n\n    #[inline]\n    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.0).poll_shutdown(cx)\n    }\n}\n"
  },
  {
    "path": "roa/src/tcp/incoming.rs",
    "content": "use std::convert::TryInto;\nuse std::future::Future;\nuse std::mem::transmute;\nuse std::net::{SocketAddr, TcpListener as StdListener, ToSocketAddrs};\nuse std::pin::Pin;\nuse std::task::{self, Poll};\nuse std::time::Duration;\nuse std::{fmt, io, matches};\n\nuse roa_core::{Accept, AddrStream};\nuse tokio::net::{TcpListener, TcpStream};\nuse tokio::time::{sleep, Sleep};\nuse tracing::{debug, error, trace};\n\n/// A stream of connections from binding to an address.\n/// As an implementation of roa_core::Accept.\n#[must_use = \"streams do nothing unless polled\"]\npub struct TcpIncoming {\n    addr: SocketAddr,\n    listener: Box<TcpListener>,\n    sleep_on_errors: bool,\n    tcp_nodelay: bool,\n    timeout: Option<Pin<Box<Sleep>>>,\n    accept: Option<Pin<BoxedAccept<'static>>>,\n}\n\ntype BoxedAccept<'a> =\n    Box<dyn 'a + Future<Output = io::Result<(TcpStream, SocketAddr)>> + Send + Sync>;\n\nimpl TcpIncoming {\n    /// Creates a new `TcpIncoming` binding to provided socket address.\n    pub fn bind(addr: impl ToSocketAddrs) -> io::Result<Self> {\n        let listener = StdListener::bind(addr)?;\n        TcpIncoming::from_std(listener)\n    }\n\n    /// Creates a new `TcpIncoming` from std TcpListener.\n    pub fn from_std(listener: StdListener) -> io::Result<Self> {\n        let addr = listener.local_addr()?;\n        listener.set_nonblocking(true)?;\n        Ok(TcpIncoming {\n            listener: Box::new(listener.try_into()?),\n            addr,\n            sleep_on_errors: true,\n            tcp_nodelay: false,\n            timeout: None,\n            accept: None,\n        })\n    }\n\n    /// Get the local address bound to this listener.\n    pub fn local_addr(&self) -> SocketAddr {\n        self.addr\n    }\n\n    /// Set the value of `TCP_NODELAY` option for accepted connections.\n    pub fn set_nodelay(&mut self, enabled: bool) -> &mut Self {\n        self.tcp_nodelay = enabled;\n        self\n    }\n\n    /// Set whether to sleep on accept errors.\n    ///\n    /// A possible scenario is that the process has hit the max open files\n    /// allowed, and so trying to accept a new connection will fail with\n    /// `EMFILE`. In some cases, it's preferable to just wait for some time, if\n    /// the application will likely close some files (or connections), and try\n    /// to accept the connection again. If this option is `true`, the error\n    /// will be logged at the `error` level, since it is still a big deal,\n    /// and then the listener will sleep for 1 second.\n    ///\n    /// In other cases, hitting the max open files should be treat similarly\n    /// to being out-of-memory, and simply error (and shutdown). Setting\n    /// this option to `false` will allow that.\n    ///\n    /// Default is `true`.\n    pub fn set_sleep_on_errors(&mut self, val: bool) {\n        self.sleep_on_errors = val;\n    }\n\n    /// Poll TcpStream.\n    fn poll_stream(\n        &mut self,\n        cx: &mut task::Context<'_>,\n    ) -> Poll<io::Result<(TcpStream, SocketAddr)>> {\n        // Check if a previous timeout is active that was set by IO errors.\n        if let Some(ref mut to) = self.timeout {\n            futures::ready!(Pin::new(to).poll(cx));\n        }\n        self.timeout = None;\n\n        loop {\n            if self.accept.is_none() {\n                let accept: Pin<BoxedAccept<'_>> = Box::pin(self.listener.accept());\n                self.accept = Some(unsafe { transmute(accept) });\n            }\n\n            if let Some(f) = &mut self.accept {\n                match futures::ready!(f.as_mut().poll(cx)) {\n                    Ok((socket, addr)) => {\n                        if let Err(e) = socket.set_nodelay(self.tcp_nodelay) {\n                            trace!(\"error trying to set TCP nodelay: {}\", e);\n                        }\n                        self.accept = None;\n                        return Poll::Ready(Ok((socket, addr)));\n                    }\n                    Err(e) => {\n                        // Connection errors can be ignored directly, continue by\n                        // accepting the next request.\n                        if is_connection_error(&e) {\n                            debug!(\"accepted connection already errored: {}\", e);\n                            continue;\n                        }\n\n                        if self.sleep_on_errors {\n                            error!(\"accept error: {}\", e);\n\n                            // Sleep 1s.\n                            let mut timeout = Box::pin(sleep(Duration::from_secs(1)));\n\n                            match timeout.as_mut().poll(cx) {\n                                Poll::Ready(()) => {\n                                    // Wow, it's been a second already? Ok then...\n                                    continue;\n                                }\n                                Poll::Pending => {\n                                    self.timeout = Some(timeout);\n                                    return Poll::Pending;\n                                }\n                            }\n                        } else {\n                            return Poll::Ready(Err(e));\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nimpl Accept for TcpIncoming {\n    type Conn = AddrStream<TcpStream>;\n    type Error = io::Error;\n\n    #[inline]\n    fn poll_accept(\n        mut self: Pin<&mut Self>,\n        cx: &mut task::Context<'_>,\n    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {\n        let (stream, addr) = futures::ready!(self.poll_stream(cx))?;\n        Poll::Ready(Some(Ok(AddrStream::new(addr, stream))))\n    }\n}\n\nimpl Drop for TcpIncoming {\n    fn drop(&mut self) {\n        self.accept = None;\n    }\n}\n\n/// This function defines errors that are per-connection. Which basically\n/// means that if we get this error from `accept()` system call it means\n/// next connection might be ready to be accepted.\n///\n/// All other errors will incur a timeout before next `accept()` is performed.\n/// The timeout is useful to handle resource exhaustion errors like ENFILE\n/// and EMFILE. Otherwise, could enter into tight loop.\nfn is_connection_error(e: &io::Error) -> bool {\n    matches!(\n        e.kind(),\n        io::ErrorKind::ConnectionRefused\n            | io::ErrorKind::ConnectionAborted\n            | io::ErrorKind::ConnectionReset\n    )\n}\n\nimpl fmt::Debug for TcpIncoming {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"TcpIncoming\")\n            .field(\"addr\", &self.addr)\n            .field(\"sleep_on_errors\", &self.sleep_on_errors)\n            .field(\"tcp_nodelay\", &self.tcp_nodelay)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "roa/src/tcp/listener.rs",
    "content": "use std::net::{SocketAddr, ToSocketAddrs};\nuse std::sync::Arc;\n\nuse roa_core::{App, Endpoint, Executor, Server, State};\n\nuse super::TcpIncoming;\n\n/// An app extension.\npub trait Listener {\n    /// http server\n    type Server;\n\n    /// Listen on a socket addr, return a server and the real addr it binds.\n    fn bind(self, addr: impl ToSocketAddrs) -> std::io::Result<(SocketAddr, Self::Server)>;\n\n    /// Listen on a socket addr, return a server, and pass real addr to the callback.\n    fn listen(\n        self,\n        addr: impl ToSocketAddrs,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server>;\n\n    /// Listen on an unused port of 127.0.0.1, return a server and the real addr it binds.\n    /// ### Example\n    /// ```rust\n    /// use roa::{App, Context, Status};\n    /// use roa::tcp::Listener;\n    /// use roa::http::StatusCode;\n    /// use tokio::task::spawn;\n    /// use std::time::Instant;\n    ///\n    /// async fn end(_ctx: &mut Context) -> Result<(), Status> {\n    ///     Ok(())\n    /// }\n    ///\n    /// #[tokio::main]\n    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    ///     let (addr, server) = App::new().end(end).run()?;\n    ///     spawn(server);\n    ///     let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    ///     assert_eq!(StatusCode::OK, resp.status());\n    ///     Ok(())\n    /// }\n    /// ```\n    fn run(self) -> std::io::Result<(SocketAddr, Self::Server)>;\n}\n\nimpl<S, E> Listener for App<S, Arc<E>>\nwhere\n    S: State,\n    E: for<'a> Endpoint<'a, S>,\n{\n    type Server = Server<TcpIncoming, Self, Executor>;\n    fn bind(self, addr: impl ToSocketAddrs) -> std::io::Result<(SocketAddr, Self::Server)> {\n        let incoming = TcpIncoming::bind(addr)?;\n        let local_addr = incoming.local_addr();\n        Ok((local_addr, self.accept(incoming)))\n    }\n\n    fn listen(\n        self,\n        addr: impl ToSocketAddrs,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server> {\n        let (addr, server) = self.bind(addr)?;\n        callback(addr);\n        Ok(server)\n    }\n\n    fn run(self) -> std::io::Result<(SocketAddr, Self::Server)> {\n        self.bind(\"127.0.0.1:0\")\n    }\n}\n"
  },
  {
    "path": "roa/src/tcp.rs",
    "content": "//! This module provides an acceptor implementing `roa_core::Accept` and an app extension.\n//!\n//! ### TcpIncoming\n//!\n//! ```\n//! use roa::{App, Context, Result};\n//! use roa::tcp::TcpIncoming;\n//! use std::io;\n//!\n//! async fn end(_ctx: &mut Context) -> Result {\n//!     Ok(())\n//! }\n//! # #[tokio::main]\n//! # async fn main() -> io::Result<()> {\n//! let app = App::new().end(end);\n//! let incoming = TcpIncoming::bind(\"127.0.0.1:0\")?;\n//! let server = app.accept(incoming);\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n//!\n//! ### Listener\n//!\n//! ```\n//! use roa::{App, Context, Result};\n//! use roa::tcp::Listener;\n//! use std::io;\n//!\n//! async fn end(_ctx: &mut Context) -> Result {\n//!     Ok(())\n//! }\n//! # #[tokio::main]\n//! # async fn main() -> io::Result<()> {\n//! let app = App::new().end(end);\n//! let (addr, server) = app.bind(\"127.0.0.1:0\")?;\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n\nmod incoming;\nmod listener;\n\n#[doc(inline)]\npub use incoming::TcpIncoming;\n#[doc(inline)]\npub use listener::Listener;\n"
  },
  {
    "path": "roa/src/tls/incoming.rs",
    "content": "use std::io;\nuse std::ops::{Deref, DerefMut};\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::{self, Context, Poll};\n\nuse futures::Future;\nuse tokio::io::{AsyncRead, AsyncWrite, ReadBuf};\nuse tokio_rustls::server::TlsStream;\nuse tokio_rustls::TlsAcceptor;\n\nuse super::ServerConfig;\nuse crate::{Accept, AddrStream};\n\n/// A stream of connections based on another stream.\n/// As an implementation of roa_core::Accept.\npub struct TlsIncoming<I> {\n    incoming: I,\n    acceptor: TlsAcceptor,\n}\n\ntype AcceptFuture<IO> =\n    dyn 'static + Sync + Send + Unpin + Future<Output = io::Result<TlsStream<IO>>>;\n\n/// A finite-state machine to do tls handshake.\npub enum WrapTlsStream<IO> {\n    /// Handshaking state.\n    Handshaking(Box<AcceptFuture<IO>>),\n    /// Streaming state.\n    Streaming(Box<TlsStream<IO>>),\n}\n\nuse WrapTlsStream::*;\n\nimpl<IO> WrapTlsStream<IO> {\n    #[inline]\n    fn poll_handshake(\n        handshake: &mut AcceptFuture<IO>,\n        cx: &mut Context<'_>,\n    ) -> Poll<io::Result<Self>> {\n        let stream = futures::ready!(Pin::new(handshake).poll(cx))?;\n        Poll::Ready(Ok(Streaming(Box::new(stream))))\n    }\n}\n\nimpl<IO> AsyncRead for WrapTlsStream<IO>\nwhere\n    IO: 'static + Unpin + AsyncRead + AsyncWrite,\n{\n    fn poll_read(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        match &mut *self {\n            Streaming(stream) => Pin::new(stream).poll_read(cx, buf),\n            Handshaking(handshake) => {\n                *self = futures::ready!(Self::poll_handshake(handshake, cx))?;\n                self.poll_read(cx, buf)\n            }\n        }\n    }\n}\n\nimpl<IO> AsyncWrite for WrapTlsStream<IO>\nwhere\n    IO: 'static + Unpin + AsyncRead + AsyncWrite,\n{\n    fn poll_write(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        match &mut *self {\n            Streaming(stream) => Pin::new(stream).poll_write(cx, buf),\n            Handshaking(handshake) => {\n                *self = futures::ready!(Self::poll_handshake(handshake, cx))?;\n                self.poll_write(cx, buf)\n            }\n        }\n    }\n\n    fn poll_write_vectored(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        bufs: &[io::IoSlice<'_>],\n    ) -> Poll<io::Result<usize>> {\n        match &mut *self {\n            Streaming(stream) => Pin::new(stream).poll_write_vectored(cx, bufs),\n            Handshaking(handshake) => {\n                *self = futures::ready!(Self::poll_handshake(handshake, cx))?;\n                self.poll_write_vectored(cx, bufs)\n            }\n        }\n    }\n\n    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        match &mut *self {\n            Streaming(stream) => Pin::new(stream).poll_flush(cx),\n            Handshaking(handshake) => {\n                *self = futures::ready!(Self::poll_handshake(handshake, cx))?;\n                self.poll_flush(cx)\n            }\n        }\n    }\n\n    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        match &mut *self {\n            Streaming(stream) => Pin::new(stream).poll_shutdown(cx),\n            Handshaking(handshake) => {\n                *self = futures::ready!(Self::poll_handshake(handshake, cx))?;\n                self.poll_shutdown(cx)\n            }\n        }\n    }\n}\n\nimpl<I> TlsIncoming<I> {\n    /// Construct from inner incoming.\n    pub fn new(incoming: I, config: ServerConfig) -> Self {\n        Self {\n            incoming,\n            acceptor: Arc::new(config).into(),\n        }\n    }\n}\n\nimpl<I> Deref for TlsIncoming<I> {\n    type Target = I;\n    fn deref(&self) -> &Self::Target {\n        &self.incoming\n    }\n}\n\nimpl<I> DerefMut for TlsIncoming<I> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.incoming\n    }\n}\n\nimpl<I, IO> Accept for TlsIncoming<I>\nwhere\n    IO: 'static + Send + Sync + Unpin + AsyncRead + AsyncWrite,\n    I: Unpin + Accept<Conn = AddrStream<IO>>,\n{\n    type Conn = AddrStream<WrapTlsStream<IO>>;\n    type Error = I::Error;\n\n    #[inline]\n    fn poll_accept(\n        mut self: Pin<&mut Self>,\n        cx: &mut task::Context<'_>,\n    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {\n        Poll::Ready(\n            match futures::ready!(Pin::new(&mut self.incoming).poll_accept(cx)) {\n                Some(Ok(AddrStream {\n                    stream,\n                    remote_addr,\n                })) => {\n                    let accept_future = self.acceptor.accept(stream);\n                    Some(Ok(AddrStream::new(\n                        remote_addr,\n                        Handshaking(Box::new(accept_future)),\n                    )))\n                }\n                Some(Err(err)) => Some(Err(err)),\n                None => None,\n            },\n        )\n    }\n}\n"
  },
  {
    "path": "roa/src/tls/listener.rs",
    "content": "use std::io;\nuse std::net::{SocketAddr, ToSocketAddrs};\nuse std::sync::Arc;\n\nuse super::{ServerConfig, TlsIncoming};\nuse crate::tcp::TcpIncoming;\nuse crate::{App, Endpoint, Executor, Server, State};\n\nimpl TlsIncoming<TcpIncoming> {\n    /// Bind a socket addr.\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"tls\")))]\n    pub fn bind(addr: impl ToSocketAddrs, config: ServerConfig) -> io::Result<Self> {\n        Ok(Self::new(TcpIncoming::bind(addr)?, config))\n    }\n}\n\n/// An app extension.\n#[cfg_attr(feature = \"docs\", doc(cfg(feature = \"tls\")))]\npub trait TlsListener {\n    /// http server\n    type Server;\n\n    /// Listen on a socket addr, return a server and the real addr it binds.\n    fn bind_tls(\n        self,\n        addr: impl ToSocketAddrs,\n        config: ServerConfig,\n    ) -> std::io::Result<(SocketAddr, Self::Server)>;\n\n    /// Listen on a socket addr, return a server, and pass real addr to the callback.\n    fn listen_tls(\n        self,\n        addr: impl ToSocketAddrs,\n        config: ServerConfig,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server>;\n\n    /// Listen on an unused port of 127.0.0.1, return a server and the real addr it binds.\n    /// ### Example\n    /// ```rust\n    /// use roa::{App, Context, Status};\n    /// use roa::tls::{TlsIncoming, ServerConfig, TlsListener, Certificate, PrivateKey};\n    /// use roa::tls::pemfile::{certs, rsa_private_keys};\n    /// use roa_core::http::StatusCode;\n    /// use tokio::task::spawn;\n    /// use std::time::Instant;\n    /// use std::fs::File;\n    /// use std::io::BufReader;\n    ///\n    /// async fn end(_ctx: &mut Context) -> Result<(), Status> {\n    ///     Ok(())\n    /// }\n    /// # #[tokio::main]\n    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    /// let mut cert_file = BufReader::new(File::open(\"../assets/cert.pem\")?);\n    /// let mut key_file = BufReader::new(File::open(\"../assets/key.pem\")?);\n    /// let cert_chain = certs(&mut cert_file)?.into_iter().map(Certificate).collect();\n    ///\n    /// let config = ServerConfig::builder()\n    ///     .with_safe_defaults()\n    ///     .with_no_client_auth()\n    ///     .with_single_cert(cert_chain, PrivateKey(rsa_private_keys(&mut key_file)?.remove(0)))?;\n    ///\n    /// let server = App::new().end(end).listen_tls(\"127.0.0.1:8000\", config, |addr| {\n    ///     println!(\"Server is listening on https://localhost:{}\", addr.port());\n    /// })?;\n    /// // server.await\n    /// Ok(())\n    /// # }\n    /// ```\n    fn run_tls(self, config: ServerConfig) -> std::io::Result<(SocketAddr, Self::Server)>;\n}\n\nimpl<S, E> TlsListener for App<S, Arc<E>>\nwhere\n    S: State,\n    E: for<'a> Endpoint<'a, S>,\n{\n    type Server = Server<TlsIncoming<TcpIncoming>, Self, Executor>;\n    fn bind_tls(\n        self,\n        addr: impl ToSocketAddrs,\n        config: ServerConfig,\n    ) -> std::io::Result<(SocketAddr, Self::Server)> {\n        let incoming = TlsIncoming::bind(addr, config)?;\n        let local_addr = incoming.local_addr();\n        Ok((local_addr, self.accept(incoming)))\n    }\n\n    fn listen_tls(\n        self,\n        addr: impl ToSocketAddrs,\n        config: ServerConfig,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server> {\n        let (addr, server) = self.bind_tls(addr, config)?;\n        callback(addr);\n        Ok(server)\n    }\n\n    fn run_tls(self, config: ServerConfig) -> std::io::Result<(SocketAddr, Self::Server)> {\n        self.bind_tls(\"127.0.0.1:0\", config)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::fs::File;\n    use std::io::{self, BufReader};\n\n    use futures::{AsyncReadExt, TryStreamExt};\n    use hyper::client::{Client, HttpConnector};\n    use hyper::Body;\n    use hyper_tls::{native_tls, HttpsConnector};\n    use tokio::task::spawn;\n    use tokio_native_tls::TlsConnector;\n\n    use crate::http::StatusCode;\n    use crate::tls::pemfile::{certs, rsa_private_keys};\n    use crate::tls::{Certificate, PrivateKey, ServerConfig, TlsListener};\n    use crate::{App, Context, Status};\n\n    async fn end(ctx: &mut Context) -> Result<(), Status> {\n        ctx.resp.write(\"Hello, World!\");\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn run_tls() -> Result<(), Box<dyn std::error::Error>> {\n        let mut cert_file = BufReader::new(File::open(\"../assets/cert.pem\")?);\n        let mut key_file = BufReader::new(File::open(\"../assets/key.pem\")?);\n        let cert_chain = certs(&mut cert_file)?\n            .into_iter()\n            .map(Certificate)\n            .collect();\n\n        let config = ServerConfig::builder()\n            .with_safe_defaults()\n            .with_no_client_auth()\n            .with_single_cert(\n                cert_chain,\n                PrivateKey(rsa_private_keys(&mut key_file)?.remove(0)),\n            )?;\n\n        let app = App::new().end(end);\n        let (addr, server) = app.run_tls(config)?;\n        spawn(server);\n\n        let native_tls_connector = native_tls::TlsConnector::builder()\n            .danger_accept_invalid_hostnames(true)\n            .danger_accept_invalid_certs(true)\n            .build()?;\n        let tls_connector = TlsConnector::from(native_tls_connector);\n        let mut http_connector = HttpConnector::new();\n        http_connector.enforce_http(false);\n        let https_connector = HttpsConnector::from((http_connector, tls_connector));\n        let client = Client::builder().build::<_, Body>(https_connector);\n        let resp = client\n            .get(format!(\"https://localhost:{}\", addr.port()).parse()?)\n            .await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        let mut text = String::new();\n        resp.into_body()\n            .map_err(|err| io::Error::new(io::ErrorKind::Other, err))\n            .into_async_read()\n            .read_to_string(&mut text)\n            .await?;\n        assert_eq!(\"Hello, World!\", text);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa/src/tls.rs",
    "content": "//! This module provides an acceptor implementing `roa_core::Accept` and an app extension.\n//!\n//! ### TlsIncoming\n//!\n//! ```rust\n//! use roa::{App, Context, Status};\n//! use roa::tls::{TlsIncoming, ServerConfig, Certificate, PrivateKey};\n//! use roa::tls::pemfile::{certs, rsa_private_keys};\n//! use std::fs::File;\n//! use std::io::BufReader;\n//!\n//! async fn end(_ctx: &mut Context) -> Result<(), Status> {\n//!     Ok(())\n//! }\n//!\n//! # #[tokio::main]\n//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//! let mut cert_file = BufReader::new(File::open(\"../assets/cert.pem\")?);\n//! let mut key_file = BufReader::new(File::open(\"../assets/key.pem\")?);\n//! let cert_chain = certs(&mut cert_file)?.into_iter().map(Certificate).collect();\n//!\n//! let config = ServerConfig::builder()\n//!     .with_safe_defaults()\n//!     .with_no_client_auth()\n//!     .with_single_cert(cert_chain, PrivateKey(rsa_private_keys(&mut key_file)?.remove(0)))?;\n//!\n//! let incoming = TlsIncoming::bind(\"127.0.0.1:0\", config)?;\n//! let server = App::new().end(end).accept(incoming);\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n//!\n//! ### TlsListener\n//!\n//! ```rust\n//! use roa::{App, Context, Status};\n//! use roa::tls::{ServerConfig, TlsListener, Certificate, PrivateKey};\n//! use roa::tls::pemfile::{certs, rsa_private_keys};\n//! use std::fs::File;\n//! use std::io::BufReader;\n//!\n//! async fn end(_ctx: &mut Context) -> Result<(), Status> {\n//!     Ok(())\n//! }\n//!\n//! # #[tokio::main]\n//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {\n//! let mut cert_file = BufReader::new(File::open(\"../assets/cert.pem\")?);\n//! let mut key_file = BufReader::new(File::open(\"../assets/key.pem\")?);\n//! let cert_chain = certs(&mut cert_file)?.into_iter().map(Certificate).collect();\n//!\n//! let config = ServerConfig::builder()\n//!     .with_safe_defaults()\n//!     .with_no_client_auth()\n//!     .with_single_cert(cert_chain, PrivateKey(rsa_private_keys(&mut key_file)?.remove(0)))?;\n//! let (addr, server) = App::new().end(end).bind_tls(\"127.0.0.1:0\", config)?;\n//! // server.await\n//! Ok(())\n//! # }\n//! ```\n\n#[doc(no_inline)]\npub use rustls::*;\n#[doc(no_inline)]\npub use rustls_pemfile as pemfile;\n\nmod incoming;\n\n#[cfg(feature = \"tcp\")]\nmod listener;\n\n#[doc(inline)]\npub use incoming::TlsIncoming;\n#[doc(inline)]\n#[cfg(feature = \"tcp\")]\npub use listener::TlsListener;\n"
  },
  {
    "path": "roa/src/websocket.rs",
    "content": "//! This module provides a websocket endpoint.\n//!\n//! ### Example\n//! ```\n//! use futures::StreamExt;\n//! use roa::router::{Router, RouterError};\n//! use roa::websocket::Websocket;\n//! use roa::{App, Context};\n//! use roa::http::Method;\n//!\n//! # fn main() -> Result<(), RouterError> {\n//! let router = Router::new().on(\"/chat\", Websocket::new(|_ctx, stream| async move {\n//!     let (write, read) = stream.split();\n//!     // echo\n//!     if let Err(err) = read.forward(write).await {\n//!         println!(\"forward err: {}\", err);\n//!     }\n//! }));\n//! let app = App::new().end(router.routes(\"/\")?);\n//! Ok(())\n//! # }\n//! ```\n\nuse std::future::Future;\nuse std::marker::PhantomData;\nuse std::sync::Arc;\n\nuse headers::{\n    Connection, HeaderMapExt, SecWebsocketAccept, SecWebsocketKey, SecWebsocketVersion, Upgrade,\n};\nuse hyper::upgrade::{self, Upgraded};\npub use tokio_tungstenite::tungstenite;\npub use tokio_tungstenite::tungstenite::protocol::{Message, WebSocketConfig};\nuse tokio_tungstenite::WebSocketStream;\n\nuse crate::http::header::UPGRADE;\nuse crate::http::StatusCode;\nuse crate::{async_trait, throw, Context, Endpoint, State, Status};\n\n/// An alias for WebSocketStream<Upgraded>.\npub type SocketStream = WebSocketStream<Upgraded>;\n\n/// The Websocket middleware.\n///\n/// ### Example\n/// ```\n/// use futures::StreamExt;\n/// use roa::router::{Router, RouterError};\n/// use roa::websocket::Websocket;\n/// use roa::{App, Context};\n/// use roa::http::Method;\n///\n/// # fn main() -> Result<(), RouterError> {\n/// let router = Router::new().on(\"/chat\", Websocket::new(|_ctx, stream| async move {\n///     let (write, read) = stream.split();\n///     // echo\n///     if let Err(err) = read.forward(write).await {\n///         println!(\"forward err: {}\", err);\n///     }\n/// }));\n/// let app = App::new().end(router.routes(\"/\")?);\n/// Ok(())\n/// # }\n/// ```\n///\n/// ### Parameter\n///\n/// - Context<S>\n///\n/// The context is the same with roa context,\n/// however, neither read body from request or write anything to response is unavailing.\n///\n/// - SocketStream\n///\n/// The websocket stream, implementing `Stream` and `Sink`.\n///\n/// ### Return\n///\n/// Must be `()`, as roa cannot deal with errors occurring in websocket.\npub struct Websocket<F, S, Fut>\nwhere\n    F: Fn(Context<S>, SocketStream) -> Fut,\n{\n    task: Arc<F>,\n    config: Option<WebSocketConfig>,\n    _s: PhantomData<S>,\n    _fut: PhantomData<Fut>,\n}\n\nunsafe impl<F, S, Fut> Send for Websocket<F, S, Fut> where\n    F: Sync + Send + Fn(Context<S>, SocketStream) -> Fut\n{\n}\nunsafe impl<F, S, Fut> Sync for Websocket<F, S, Fut> where\n    F: Sync + Send + Fn(Context<S>, SocketStream) -> Fut\n{\n}\n\nimpl<F, S, Fut> Websocket<F, S, Fut>\nwhere\n    F: Fn(Context<S>, SocketStream) -> Fut,\n{\n    fn config(config: Option<WebSocketConfig>, task: F) -> Self {\n        Self {\n            task: Arc::new(task),\n            config,\n            _s: PhantomData::default(),\n            _fut: PhantomData::default(),\n        }\n    }\n\n    /// Construct a websocket middleware by task closure.\n    pub fn new(task: F) -> Self {\n        Self::config(None, task)\n    }\n\n    /// Construct a websocket middleware with config.\n    /// ### Example\n    /// ```\n    /// use futures::StreamExt;\n    /// use roa::router::{Router, RouterError};\n    /// use roa::websocket::{Websocket, WebSocketConfig};\n    /// use roa::{App, Context};\n    /// use roa::http::Method;\n    ///\n    /// # fn main() -> Result<(), RouterError> {\n    /// let router = Router::new().on(\"/chat\", Websocket::with_config(\n    ///     WebSocketConfig::default(),\n    ///     |_ctx, stream| async move {\n    ///         let (write, read) = stream.split();\n    ///         // echo\n    ///         if let Err(err) = read.forward(write).await {\n    ///             println!(\"forward err: {}\", err);\n    ///         }\n    ///     })\n    /// );\n    /// let app = App::new().end(router.routes(\"/\")?);\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn with_config(config: WebSocketConfig, task: F) -> Self {\n        Self::config(Some(config), task)\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, F, S, Fut> Endpoint<'a, S> for Websocket<F, S, Fut>\nwhere\n    S: State,\n    F: 'static + Sync + Send + Fn(Context<S>, SocketStream) -> Fut,\n    Fut: 'static + Send + Future<Output = ()>,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result<(), Status> {\n        let header_map = &ctx.req.headers;\n        let key = header_map\n            .typed_get::<Upgrade>()\n            .filter(|upgrade| upgrade == &Upgrade::websocket())\n            .and(header_map.typed_get::<Connection>())\n            .filter(|connection| connection.contains(UPGRADE))\n            .and(header_map.typed_get::<SecWebsocketVersion>())\n            .filter(|version| version == &SecWebsocketVersion::V13)\n            .and(header_map.typed_get::<SecWebsocketKey>());\n\n        match key {\n            None => throw!(StatusCode::BAD_REQUEST, \"invalid websocket upgrade request\"),\n            Some(key) => {\n                let raw_req = ctx.req.take_raw();\n                let context = ctx.clone();\n                let task = self.task.clone();\n                let config = self.config;\n                // Setup a future that will eventually receive the upgraded\n                // connection and talk a new protocol, and spawn the future\n                // into the runtime.\n                //\n                // Note: This can't possibly be fulfilled until the 101 response\n                // is returned below, so it's better to spawn this future instead\n                // waiting for it to complete to then return a response.\n                ctx.exec.spawn(async move {\n                    match upgrade::on(raw_req).await {\n                        Err(err) => tracing::error!(\"websocket upgrade error: {}\", err),\n                        Ok(upgraded) => {\n                            let websocket = WebSocketStream::from_raw_socket(\n                                upgraded,\n                                tungstenite::protocol::Role::Server,\n                                config,\n                            )\n                            .await;\n                            task(context, websocket).await\n                        }\n                    }\n                });\n                ctx.resp.status = StatusCode::SWITCHING_PROTOCOLS;\n                ctx.resp.headers.typed_insert(Connection::upgrade());\n                ctx.resp.headers.typed_insert(Upgrade::websocket());\n                ctx.resp.headers.typed_insert(SecWebsocketAccept::from(key));\n                Ok(())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "roa/templates/user.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>User Homepage</title>\n</head>\n<body>\n<h4>{{name}}</h4>\n<p>{{id}}</p>\n</body>\n</html>"
  },
  {
    "path": "roa-async-std/Cargo.toml",
    "content": "[package]\nauthors = [\"Hexilee <i@hexilee.me>\"]\ncategories = [\n  \"network-programming\",\n  \"asynchronous\",\n  \"web-programming::http-server\",\n]\ndescription = \"tokio-based runtime and acceptor\"\ndocumentation = \"https://docs.rs/roa-tokio\"\nedition = \"2018\"\nhomepage = \"https://github.com/Hexilee/roa/wiki\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\"]\nlicense = \"MIT\"\nname = \"roa-async-std\"\nreadme = \"./README.md\"\nrepository = \"https://github.com/Hexilee/roa\"\nversion = \"0.6.0\"\n\n[package.metadata.docs.rs]\nfeatures = [\"docs\"]\nrustdoc-args = [\"--cfg\", \"feature=\\\"docs\\\"\"]\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nfutures = \"0.3\"\ntracing = \"0.1\"\nroa = {path = \"../roa\", version = \"0.6.0\", default-features = false}\nasync-std = {version = \"1.10\", features = [\"unstable\"]}\nfutures-timer = \"3.0\"\n\n[dev-dependencies]\nreqwest = \"0.11\"\nroa = {path = \"../roa\", version = \"0.6.0\"}\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"]}\ntokio = { version = \"1.15\", features = [\"full\"] }\nasync-std = {version = \"1.10\", features = [\"attributes\", \"unstable\"]}\n\n[features]\ndocs = [\"roa/docs\"]\n"
  },
  {
    "path": "roa-async-std/README.md",
    "content": "[![Stable Test](https://github.com/Hexilee/roa/workflows/Stable%20Test/badge.svg)](https://github.com/Hexilee/roa/actions)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa)\n[![Rust Docs](https://docs.rs/roa-async-std/badge.svg)](https://docs.rs/roa-async-std)\n[![Crate version](https://img.shields.io/crates/v/roa-async-std.svg)](https://crates.io/crates/roa-async-std)\n[![Download](https://img.shields.io/crates/d/roa-async-std.svg)](https://crates.io/crates/roa-async-std)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\nThis crate provides async-std-based runtime and acceptor for roa.\n\n```rust,no_run\nuse roa::http::StatusCode;\nuse roa::{App, Context};\nuse roa_async_std::{Listener, Exec};\nuse std::error::Error;\n\nasync fn end(_ctx: &mut Context) -> roa::Result {\n    Ok(())\n}\n\n#[async_std::main]\nasync fn main() -> Result<(), Box<dyn Error>> {\n    let (addr, server) = App::with_exec((), Exec).end(end).run()?;\n    println!(\"server is listening on {}\", addr);\n    server.await?;\n    Ok(())\n}\n```\n"
  },
  {
    "path": "roa-async-std/src/lib.rs",
    "content": "#![cfg_attr(feature = \"docs\", doc = include_str!(\"../README.md\"))]\n#![cfg_attr(feature = \"docs\", warn(missing_docs))]\n\nmod listener;\nmod net;\nmod runtime;\n\n#[doc(inline)]\npub use listener::Listener;\n#[doc(inline)]\npub use net::TcpIncoming;\n#[doc(inline)]\npub use runtime::Exec;\n"
  },
  {
    "path": "roa-async-std/src/listener.rs",
    "content": "use std::net::{SocketAddr, ToSocketAddrs};\nuse std::sync::Arc;\n\nuse roa::{App, Endpoint, Executor, Server, State};\n\nuse super::TcpIncoming;\n\n/// An app extension.\npub trait Listener {\n    /// http server\n    type Server;\n\n    /// Listen on a socket addr, return a server and the real addr it binds.\n    fn bind(self, addr: impl ToSocketAddrs) -> std::io::Result<(SocketAddr, Self::Server)>;\n\n    /// Listen on a socket addr, return a server, and pass real addr to the callback.\n    fn listen(\n        self,\n        addr: impl ToSocketAddrs,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server>;\n\n    /// Listen on an unused port of 127.0.0.1, return a server and the real addr it binds.\n    /// ### Example\n    /// ```rust,no_run\n    /// use roa::{App, Context, Status};\n    /// use roa_async_std::{Exec, Listener};\n    /// use roa::http::StatusCode;\n    /// use async_std::task::spawn;\n    /// use std::time::Instant;\n    ///\n    /// async fn end(_ctx: &mut Context) -> Result<(), Status> {\n    ///     Ok(())\n    /// }\n    ///\n    /// #[async_std::main]\n    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {\n    ///     let (_, server) = App::with_exec((), Exec).end(end).run()?;\n    ///     server.await?;\n    ///     Ok(())\n    /// }\n    /// ```\n    fn run(self) -> std::io::Result<(SocketAddr, Self::Server)>;\n}\n\nimpl<S, E> Listener for App<S, Arc<E>>\nwhere\n    S: State,\n    E: for<'a> Endpoint<'a, S>,\n{\n    type Server = Server<TcpIncoming, Self, Executor>;\n    fn bind(self, addr: impl ToSocketAddrs) -> std::io::Result<(SocketAddr, Self::Server)> {\n        let incoming = TcpIncoming::bind(addr)?;\n        let local_addr = incoming.local_addr();\n        Ok((local_addr, self.accept(incoming)))\n    }\n\n    fn listen(\n        self,\n        addr: impl ToSocketAddrs,\n        callback: impl Fn(SocketAddr),\n    ) -> std::io::Result<Self::Server> {\n        let (addr, server) = self.bind(addr)?;\n        callback(addr);\n        Ok(server)\n    }\n\n    fn run(self) -> std::io::Result<(SocketAddr, Self::Server)> {\n        self.bind(\"127.0.0.1:0\")\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::error::Error;\n\n    use roa::http::StatusCode;\n    use roa::App;\n\n    use super::Listener;\n    use crate::Exec;\n\n    #[tokio::test]\n    async fn incoming() -> Result<(), Box<dyn Error>> {\n        let (addr, server) = App::with_exec((), Exec).end(()).run()?;\n        tokio::task::spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-async-std/src/net.rs",
    "content": "use std::future::Future;\nuse std::mem::transmute;\nuse std::net::{TcpListener as StdListener, ToSocketAddrs};\nuse std::pin::Pin;\nuse std::task::{self, Poll};\nuse std::time::Duration;\nuse std::{fmt, io, matches};\n\nuse async_std::net::{SocketAddr, TcpListener, TcpStream};\nuse futures_timer::Delay;\nuse roa::stream::AsyncStream;\nuse roa::{Accept, AddrStream};\nuse tracing::{debug, error, trace};\n\n/// A stream of connections from binding to an address.\n/// As an implementation of roa_core::Accept.\n#[must_use = \"streams do nothing unless polled\"]\npub struct TcpIncoming {\n    addr: SocketAddr,\n    listener: Box<TcpListener>,\n    sleep_on_errors: bool,\n    tcp_nodelay: bool,\n    timeout: Option<Pin<Box<Delay>>>,\n    accept: Option<Pin<BoxedAccept<'static>>>,\n}\n\ntype BoxedAccept<'a> =\n    Box<dyn 'a + Future<Output = io::Result<(TcpStream, SocketAddr)>> + Send + Sync>;\n\nimpl TcpIncoming {\n    /// Creates a new `TcpIncoming` binding to provided socket address.\n    pub fn bind(addr: impl ToSocketAddrs) -> io::Result<Self> {\n        let listener = StdListener::bind(addr)?;\n        TcpIncoming::from_std(listener)\n    }\n\n    /// Creates a new `TcpIncoming` from std TcpListener.\n    pub fn from_std(listener: StdListener) -> io::Result<Self> {\n        let addr = listener.local_addr()?;\n        Ok(TcpIncoming {\n            listener: Box::new(listener.into()),\n            addr,\n            sleep_on_errors: true,\n            tcp_nodelay: false,\n            timeout: None,\n            accept: None,\n        })\n    }\n\n    /// Get the local address bound to this listener.\n    pub fn local_addr(&self) -> SocketAddr {\n        self.addr\n    }\n\n    /// Set the value of `TCP_NODELAY` option for accepted connections.\n    pub fn set_nodelay(&mut self, enabled: bool) -> &mut Self {\n        self.tcp_nodelay = enabled;\n        self\n    }\n\n    /// Set whether to sleep on accept errors.\n    ///\n    /// A possible scenario is that the process has hit the max open files\n    /// allowed, and so trying to accept a new connection will fail with\n    /// `EMFILE`. In some cases, it's preferable to just wait for some time, if\n    /// the application will likely close some files (or connections), and try\n    /// to accept the connection again. If this option is `true`, the error\n    /// will be logged at the `error` level, since it is still a big deal,\n    /// and then the listener will sleep for 1 second.\n    ///\n    /// In other cases, hitting the max open files should be treat similarly\n    /// to being out-of-memory, and simply error (and shutdown). Setting\n    /// this option to `false` will allow that.\n    ///\n    /// Default is `true`.\n    pub fn set_sleep_on_errors(&mut self, val: bool) {\n        self.sleep_on_errors = val;\n    }\n\n    /// Poll TcpStream.\n    fn poll_stream(\n        &mut self,\n        cx: &mut task::Context<'_>,\n    ) -> Poll<io::Result<(TcpStream, SocketAddr)>> {\n        // Check if a previous timeout is active that was set by IO errors.\n        if let Some(ref mut to) = self.timeout {\n            futures::ready!(Pin::new(to).poll(cx));\n        }\n        self.timeout = None;\n\n        loop {\n            if self.accept.is_none() {\n                let accept: Pin<BoxedAccept<'_>> = Box::pin(self.listener.accept());\n                self.accept = Some(unsafe { transmute(accept) });\n            }\n\n            if let Some(f) = &mut self.accept {\n                match futures::ready!(f.as_mut().poll(cx)) {\n                    Ok((socket, addr)) => {\n                        if let Err(e) = socket.set_nodelay(self.tcp_nodelay) {\n                            trace!(\"error trying to set TCP nodelay: {}\", e);\n                        }\n                        self.accept = None;\n                        return Poll::Ready(Ok((socket, addr)));\n                    }\n                    Err(e) => {\n                        // Connection errors can be ignored directly, continue by\n                        // accepting the next request.\n                        if is_connection_error(&e) {\n                            debug!(\"accepted connection already errored: {}\", e);\n                            continue;\n                        }\n\n                        if self.sleep_on_errors {\n                            error!(\"accept error: {}\", e);\n\n                            // Sleep 1s.\n                            let mut timeout = Box::pin(Delay::new(Duration::from_secs(1)));\n\n                            match timeout.as_mut().poll(cx) {\n                                Poll::Ready(()) => {\n                                    // Wow, it's been a second already? Ok then...\n                                    continue;\n                                }\n                                Poll::Pending => {\n                                    self.timeout = Some(timeout);\n                                    return Poll::Pending;\n                                }\n                            }\n                        } else {\n                            return Poll::Ready(Err(e));\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nimpl Accept for TcpIncoming {\n    type Conn = AddrStream<AsyncStream<TcpStream>>;\n    type Error = io::Error;\n\n    #[inline]\n    fn poll_accept(\n        mut self: Pin<&mut Self>,\n        cx: &mut task::Context<'_>,\n    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {\n        let (stream, addr) = futures::ready!(self.poll_stream(cx))?;\n        Poll::Ready(Some(Ok(AddrStream::new(addr, AsyncStream(stream)))))\n    }\n}\n\nimpl Drop for TcpIncoming {\n    fn drop(&mut self) {\n        self.accept = None;\n    }\n}\n\n/// This function defines errors that are per-connection. Which basically\n/// means that if we get this error from `accept()` system call it means\n/// next connection might be ready to be accepted.\n///\n/// All other errors will incur a timeout before next `accept()` is performed.\n/// The timeout is useful to handle resource exhaustion errors like ENFILE\n/// and EMFILE. Otherwise, could enter into tight loop.\nfn is_connection_error(e: &io::Error) -> bool {\n    matches!(\n        e.kind(),\n        io::ErrorKind::ConnectionRefused\n            | io::ErrorKind::ConnectionAborted\n            | io::ErrorKind::ConnectionReset\n    )\n}\n\nimpl fmt::Debug for TcpIncoming {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"TcpIncoming\")\n            .field(\"addr\", &self.addr)\n            .field(\"sleep_on_errors\", &self.sleep_on_errors)\n            .field(\"tcp_nodelay\", &self.tcp_nodelay)\n            .finish()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::error::Error;\n\n    use roa::http::StatusCode;\n    use roa::App;\n    use tracing_subscriber::{fmt, EnvFilter};\n\n    use super::TcpIncoming;\n    use crate::Exec;\n\n    #[tokio::test]\n    async fn incoming() -> Result<(), Box<dyn Error>> {\n        fmt().with_env_filter(EnvFilter::from_default_env()).init();\n        let app = App::with_exec((), Exec).end(());\n        let incoming = TcpIncoming::bind(\"127.0.0.1:0\")?;\n        let addr = incoming.local_addr();\n        tokio::task::spawn(app.accept(incoming));\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-async-std/src/runtime.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\n\nuse roa::Spawn;\n\n/// Future Object\npub type FutureObj = Pin<Box<dyn 'static + Send + Future<Output = ()>>>;\n\n/// Blocking task Object\npub type BlockingObj = Box<dyn 'static + Send + FnOnce()>;\n\n/// Tokio-based executor.\n///\n/// ```\n/// use roa::App;\n/// use roa_async_std::Exec;\n///\n/// let app = App::with_exec((), Exec);\n/// ```\npub struct Exec;\n\nimpl Spawn for Exec {\n    #[inline]\n    fn spawn(&self, fut: FutureObj) {\n        async_std::task::spawn(fut);\n    }\n\n    #[inline]\n    fn spawn_blocking(&self, task: BlockingObj) {\n        async_std::task::spawn_blocking(task);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::error::Error;\n\n    use roa::http::StatusCode;\n    use roa::tcp::Listener;\n    use roa::App;\n\n    use super::Exec;\n\n    #[tokio::test]\n    async fn exec() -> Result<(), Box<dyn Error>> {\n        let app = App::with_exec((), Exec).end(());\n        let (addr, server) = app.bind(\"127.0.0.1:0\")?;\n        tokio::spawn(server);\n        let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n        assert_eq!(StatusCode::OK, resp.status());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/Cargo.toml",
    "content": "[package]\nname = \"roa-core\"\nversion = \"0.6.1\"\nauthors = [\"Hexilee <i@hexilee.me>\"]\nedition = \"2018\"\nlicense = \"MIT\"\nreadme = \"./README.md\"\nrepository = \"https://github.com/Hexilee/roa\"\ndocumentation = \"https://docs.rs/roa-core\"\nhomepage = \"https://github.com/Hexilee/roa/wiki\"\ndescription = \"core components of roa web framework\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\"]\ncategories = [\"network-programming\", \"asynchronous\",\n              \"web-programming::http-server\"]\n\n\n[package.metadata.docs.rs]\nfeatures = [\"docs\"]\nrustdoc-args = [\"--cfg\", \"feature=\\\"docs\\\"\"]\n\n[badges]\ncodecov = { repository = \"Hexilee/roa\" }\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nfutures = \"0.3\"\nbytes = \"1.1\"\nhttp = \"0.2\"\nhyper = { version = \"0.14\", default-features = false, features = [\"stream\", \"server\", \"http1\", \"http2\"] }\ntracing = \"0.1\"\ntokio = \"1.15\"\ntokio-util = { version = \"0.6.9\", features = [\"io\"] }\nasync-trait = \"0.1.51\"\ncrossbeam-queue = \"0.3\"\n\n[dev-dependencies]\ntokio = { version = \"1.15\", features = [\"fs\", \"macros\", \"rt\"] }\n\n[features]\nruntime = [\"tokio/rt\"]\ndocs = [\"runtime\"]\n"
  },
  {
    "path": "roa-core/README.md",
    "content": "[![Stable Test](https://github.com/Hexilee/roa/workflows/Stable%20Test/badge.svg)](https://github.com/Hexilee/roa/actions)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa)\n[![Rust Docs](https://docs.rs/roa-core/badge.svg)](https://docs.rs/roa-core)\n[![Crate version](https://img.shields.io/crates/v/roa-core.svg)](https://crates.io/crates/roa-core)\n[![Download](https://img.shields.io/crates/d/roa-core.svg)](https://crates.io/crates/roa-core)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\n### Introduction\n\nCore components of Roa framework.\n\nIf you are new to roa, please go to the documentation of [roa framework](https://docs.rs/roa).\n\n### Application\n\nA Roa application is a structure composing and executing middlewares and an endpoint in a stack-like manner.\n\nThe obligatory hello world application:\n\n```rust\nuse roa_core::App;\nlet app = App::new().end(\"Hello, World\");\n```\n\n#### Endpoint\n\nAn endpoint is a request handler.\n\nThere are some build-in endpoints in roa_core.\n\n- Functional endpoint\n\n    A normal functional endpoint is an async function with signature:\n    `async fn(&mut Context) -> Result`.\n    \n    ```rust\n    use roa_core::{App, Context, Result};\n    \n    async fn endpoint(ctx: &mut Context) -> Result {\n        Ok(())\n    }\n    \n    let app = App::new().end(endpoint);\n    ```\n  \n- Ok endpoint\n\n    `()` is an endpoint always return `Ok(())`\n    \n    ```rust\n    let app = roa_core::App::new().end(());\n    ```\n\n- Status endpoint\n\n    `Status` is an endpoint always return `Err(Status)`\n    \n    ```rust\n    use roa_core::{App, status};\n    use roa_core::http::StatusCode;\n    let app = App::new().end(status!(StatusCode::BAD_REQUEST));\n    ```\n\n- String endpoint\n\n    Write string to body.\n    \n    ```rust\n    use roa_core::App;\n    \n    let app = App::new().end(\"Hello, world\"); // static slice\n    let app = App::new().end(\"Hello, world\".to_owned()); // string\n    ```\n\n- Redirect endpoint\n\n    Redirect to an uri.\n    \n    ```rust\n    use roa_core::App;\n    use roa_core::http::Uri;\n    \n    let app = App::new().end(\"/target\".parse::<Uri>().unwrap());\n    ```\n\n\n#### Cascading\n\nThe following example responds with \"Hello World\", however, the request flows through\nthe `logging` middleware to mark when the request started, then continue\nto yield control through the endpoint. When a middleware invokes `next.await`\nthe function suspends and passes control to the next middleware or endpoint. After the endpoint is called,\nthe stack will unwind and each middleware is resumed to perform\nits upstream behaviour.\n\n```rust\nuse roa_core::{App, Context, Result, Status, MiddlewareExt, Next};\nuse std::time::Instant;\nuse tracing::info;\n\nlet app = App::new().gate(logging).end(\"Hello, World\");\n\nasync fn logging(ctx: &mut Context, next: Next<'_>) -> Result {\n    let inbound = Instant::now();\n    next.await?;\n    info!(\"time elapsed: {} ms\", inbound.elapsed().as_millis());\n    Ok(())\n}\n```\n\n### Status Handling\n\nYou can catch or straightly throw a status returned by next.\n\n```rust\nuse roa_core::{App, Context, Result, Status, MiddlewareExt, Next, throw};\nuse roa_core::http::StatusCode;\n        \nlet app = App::new().gate(catch).gate(gate).end(end);\n\nasync fn catch(ctx: &mut Context, next: Next<'_>) -> Result {\n    // catch\n    if let Err(status) = next.await {\n        // teapot is ok\n        if status.status_code != StatusCode::IM_A_TEAPOT {\n            return Err(status);\n        }\n    }\n    Ok(())\n}\nasync fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    next.await?; // just throw\n    unreachable!()\n}\n\nasync fn end(ctx: &mut Context) -> Result {\n    throw!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\")\n}\n```\n\n#### status_handler\nApp has an status_handler to handle `Status` thrown by the top middleware.\nThis is the status_handler:\n\n```rust\nuse roa_core::{Context, Status, Result, State};\npub fn status_handler<S: State>(ctx: &mut Context<S>, status: Status) {\n    ctx.resp.status = status.status_code;\n    if status.expose {\n        ctx.resp.write(status.message);\n    } else {\n        tracing::error!(\"{}\", status);\n    }\n}\n```\n\n### HTTP Server.\n\nUse `roa_core::accept` to construct a http server.\nPlease refer to `roa::tcp` for more information."
  },
  {
    "path": "roa-core/src/app/future.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\n\nuse futures::task::{Context, Poll};\n\n/// A wrapper to make future `Send`. It's used to wrap future returned by top middleware.\n/// So the future returned by each middleware or endpoint can be `?Send`.\n///\n/// But how to ensure thread safety? Because the middleware and the context must be `Sync + Send`,\n/// which means the only factor causing future `!Send` is the variables generated in `Future::poll`.\n/// And these variable mustn't be accessed from other threads.\n#[allow(clippy::non_send_fields_in_send_ty)]\npub struct SendFuture<F>(pub F);\n\nimpl<F> Future for SendFuture<F>\nwhere\n    F: 'static + Future + Unpin,\n{\n    type Output = F::Output;\n    #[inline]\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        Pin::new(&mut self.0).poll(cx)\n    }\n}\n\nunsafe impl<F> Send for SendFuture<F> {}\n"
  },
  {
    "path": "roa-core/src/app/runtime.rs",
    "content": "use crate::executor::{BlockingObj, FutureObj};\nuse crate::{App, Spawn};\n\nimpl<S> App<S, ()> {\n    /// Construct app with default runtime.\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"runtime\")))]\n    #[inline]\n    pub fn state(state: S) -> Self {\n        Self::with_exec(state, Exec)\n    }\n}\n\nimpl App<(), ()> {\n    /// Construct app with default runtime.\n    #[cfg_attr(feature = \"docs\", doc(cfg(feature = \"runtime\")))]\n    #[inline]\n    pub fn new() -> Self {\n        Self::state(())\n    }\n}\n\nimpl Default for App<(), ()> {\n    /// Construct app with default runtime.\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub struct Exec;\n\nimpl Spawn for Exec {\n    #[inline]\n    fn spawn(&self, fut: FutureObj) {\n        tokio::task::spawn(fut);\n    }\n\n    #[inline]\n    fn spawn_blocking(&self, task: BlockingObj) {\n        tokio::task::spawn_blocking(task);\n    }\n}\n"
  },
  {
    "path": "roa-core/src/app/stream.rs",
    "content": "use std::fmt::Debug;\nuse std::io;\nuse std::net::SocketAddr;\nuse std::pin::Pin;\nuse std::task::{self, Poll};\n\nuse futures::ready;\nuse tokio::io::{AsyncRead, AsyncWrite, ReadBuf};\nuse tracing::{instrument, trace};\n\n/// A transport returned yieled by `AddrIncoming`.\npub struct AddrStream<IO> {\n    /// The remote address of this stream.\n    pub remote_addr: SocketAddr,\n\n    /// The inner stream.\n    pub stream: IO,\n}\n\nimpl<IO> AddrStream<IO> {\n    /// Construct an AddrStream from an addr and a AsyncReadWriter.\n    #[inline]\n    pub fn new(remote_addr: SocketAddr, stream: IO) -> AddrStream<IO> {\n        AddrStream {\n            remote_addr,\n            stream,\n        }\n    }\n}\n\nimpl<IO> AsyncRead for AddrStream<IO>\nwhere\n    IO: Unpin + AsyncRead,\n{\n    #[inline]\n    #[instrument(skip(cx, buf))]\n    fn poll_read(\n        mut self: Pin<&mut Self>,\n        cx: &mut task::Context<'_>,\n        buf: &mut ReadBuf<'_>,\n    ) -> Poll<io::Result<()>> {\n        let poll = Pin::new(&mut self.stream).poll_read(cx, buf);\n        trace!(\"poll read: {:?}\", poll);\n        ready!(poll)?;\n        trace!(\"read {} bytes\", buf.filled().len());\n        Poll::Ready(Ok(()))\n    }\n}\n\nimpl<IO> AsyncWrite for AddrStream<IO>\nwhere\n    IO: Unpin + AsyncWrite,\n{\n    #[inline]\n    #[instrument(skip(cx, buf))]\n    fn poll_write(\n        mut self: Pin<&mut Self>,\n        cx: &mut task::Context<'_>,\n        buf: &[u8],\n    ) -> Poll<io::Result<usize>> {\n        let write_size = ready!(Pin::new(&mut self.stream).poll_write(cx, buf))?;\n        trace!(\"wrote {} bytes\", write_size);\n        Poll::Ready(Ok(write_size))\n    }\n\n    #[inline]\n    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.stream).poll_flush(cx)\n    }\n\n    #[inline]\n    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {\n        Pin::new(&mut self.stream).poll_shutdown(cx)\n    }\n}\n\nimpl<IO> Debug for AddrStream<IO> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AddrStream\")\n            .field(\"remote_addr\", &self.remote_addr)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "roa-core/src/app.rs",
    "content": "mod future;\n#[cfg(feature = \"runtime\")]\nmod runtime;\nmod stream;\n\nuse std::convert::Infallible;\nuse std::error::Error;\nuse std::future::Future;\nuse std::net::SocketAddr;\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::Poll;\n\nuse future::SendFuture;\nuse http::{Request as HttpRequest, Response as HttpResponse};\nuse hyper::service::Service;\nuse hyper::{Body as HyperBody, Server};\nuse tokio::io::{AsyncRead, AsyncWrite};\n\npub use self::stream::AddrStream;\nuse crate::{\n    Accept, Chain, Context, Endpoint, Executor, Middleware, MiddlewareExt, Request, Response,\n    Spawn, State,\n};\n\n/// The Application of roa.\n/// ### Example\n/// ```rust,no_run\n/// use roa_core::{App, Context, Next, Result, MiddlewareExt};\n/// use tracing::info;\n/// use tokio::fs::File;\n///\n/// let app = App::new().gate(gate).end(end);\n/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n///     info!(\"{} {}\", ctx.method(), ctx.uri());\n///     next.await\n/// }\n///\n/// async fn end(ctx: &mut Context) -> Result {\n///     ctx.resp.write_reader(File::open(\"assets/welcome.html\").await?);\n///     Ok(())\n/// }\n/// ```\n///\n/// ### State\n/// The `State` is designed to share data or handler between middlewares.\n/// The only one type implemented `State` by this crate is `()`, you can implement your custom state if neccassary.\n///\n/// ```rust\n/// use roa_core::{App, Context, Next, Result};\n/// use tracing::info;\n/// use futures::lock::Mutex;\n///\n/// use std::sync::Arc;\n/// use std::collections::HashMap;\n///\n/// #[derive(Clone)]\n/// struct State {\n///     id: u64,\n///     database: Arc<Mutex<HashMap<u64, String>>>,\n/// }\n///\n/// impl State {\n///     fn new() -> Self {\n///         Self {\n///             id: 0,\n///             database: Arc::new(Mutex::new(HashMap::new()))\n///         }\n///     }\n/// }\n///\n/// let app = App::state(State::new()).gate(gate).end(end);\n/// async fn gate(ctx: &mut Context<State>, next: Next<'_>) -> Result {\n///     ctx.id = 1;\n///     next.await\n/// }\n///\n/// async fn end(ctx: &mut Context<State>) -> Result {\n///     let id = ctx.id;\n///     ctx.database.lock().await.get(&id);\n///     Ok(())\n/// }\n/// ```\n///\npub struct App<S, T> {\n    service: T,\n    exec: Executor,\n    state: S,\n}\n\n/// An implementation of hyper HttpService.\npub struct HttpService<S, E> {\n    endpoint: Arc<E>,\n    remote_addr: SocketAddr,\n    exec: Executor,\n    pub(crate) state: S,\n}\n\nimpl<S, T> App<S, T> {\n    /// Map app::service\n    fn map_service<U>(self, mapper: impl FnOnce(T) -> U) -> App<S, U> {\n        let Self {\n            exec,\n            state,\n            service,\n        } = self;\n        App {\n            service: mapper(service),\n            exec,\n            state,\n        }\n    }\n}\n\nimpl<S> App<S, ()> {\n    /// Construct an application with custom runtime.\n    pub fn with_exec(state: S, exec: impl 'static + Send + Sync + Spawn) -> Self {\n        Self {\n            service: (),\n            exec: Executor(Arc::new(exec)),\n            state,\n        }\n    }\n}\n\nimpl<S, T> App<S, T>\nwhere\n    T: for<'a> Middleware<'a, S>,\n{\n    /// Use a middleware.\n    pub fn gate<M>(self, middleware: M) -> App<S, Chain<T, M>>\n    where\n        M: for<'a> Middleware<'a, S>,\n    {\n        self.map_service(move |service| service.chain(middleware))\n    }\n\n    /// Set endpoint, then app can only be used to serve http request.\n    pub fn end<E>(self, endpoint: E) -> App<S, Arc<Chain<T, E>>>\n    where\n        E: for<'a> Endpoint<'a, S>,\n    {\n        self.map_service(move |service| Arc::new(service.end(endpoint)))\n    }\n}\n\nimpl<S, E> App<S, Arc<E>>\nwhere\n    E: for<'a> Endpoint<'a, S>,\n{\n    /// Construct a hyper server by an incoming.\n    pub fn accept<I, IO>(self, incoming: I) -> Server<I, Self, Executor>\n    where\n        S: State,\n        IO: 'static + Send + Sync + Unpin + AsyncRead + AsyncWrite,\n        I: Accept<Conn = AddrStream<IO>>,\n        I::Error: Into<Box<dyn Error + Send + Sync>>,\n    {\n        Server::builder(incoming)\n            .executor(self.exec.clone())\n            .serve(self)\n    }\n\n    /// Make a fake http service for test.\n    #[cfg(test)]\n    pub fn http_service(&self) -> HttpService<S, E>\n    where\n        S: Clone,\n    {\n        let endpoint = self.service.clone();\n        let addr = ([127, 0, 0, 1], 0);\n        let state = self.state.clone();\n        let exec = self.exec.clone();\n        HttpService::new(endpoint, addr.into(), exec, state)\n    }\n}\n\nmacro_rules! impl_poll_ready {\n    () => {\n        #[inline]\n        fn poll_ready(\n            &mut self,\n            _cx: &mut std::task::Context<'_>,\n        ) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n    };\n}\n\ntype AppFuture<S, E> =\n    Pin<Box<dyn 'static + Future<Output = std::io::Result<HttpService<S, E>>> + Send>>;\n\nimpl<S, E, IO> Service<&AddrStream<IO>> for App<S, Arc<E>>\nwhere\n    S: State,\n    E: for<'a> Endpoint<'a, S>,\n    IO: 'static + Send + Sync + Unpin + AsyncRead + AsyncWrite,\n{\n    type Response = HttpService<S, E>;\n    type Error = std::io::Error;\n    type Future = AppFuture<S, E>;\n    impl_poll_ready!();\n\n    #[inline]\n    fn call(&mut self, stream: &AddrStream<IO>) -> Self::Future {\n        let endpoint = self.service.clone();\n        let addr = stream.remote_addr;\n        let state = self.state.clone();\n        let exec = self.exec.clone();\n        Box::pin(async move { Ok(HttpService::new(endpoint, addr, exec, state)) })\n    }\n}\n\ntype HttpFuture =\n    Pin<Box<dyn 'static + Future<Output = Result<HttpResponse<HyperBody>, Infallible>> + Send>>;\n\nimpl<S, E> Service<HttpRequest<HyperBody>> for HttpService<S, E>\nwhere\n    S: State,\n    E: for<'a> Endpoint<'a, S>,\n{\n    type Response = HttpResponse<HyperBody>;\n    type Error = Infallible;\n    type Future = HttpFuture;\n    impl_poll_ready!();\n\n    #[inline]\n    fn call(&mut self, req: HttpRequest<HyperBody>) -> Self::Future {\n        let service = self.clone();\n        Box::pin(async move {\n            let serve_future = SendFuture(Box::pin(service.serve(req.into())));\n            Ok(serve_future.await.into())\n        })\n    }\n}\n\nimpl<S, E> HttpService<S, E> {\n    pub fn new(endpoint: Arc<E>, remote_addr: SocketAddr, exec: Executor, state: S) -> Self {\n        Self {\n            endpoint,\n            remote_addr,\n            exec,\n            state,\n        }\n    }\n\n    /// Receive a request then return a response.\n    /// The entry point of http service.\n    pub async fn serve(self, req: Request) -> Response\n    where\n        S: 'static,\n        E: for<'a> Endpoint<'a, S>,\n    {\n        let Self {\n            endpoint,\n            remote_addr,\n            exec,\n            state,\n        } = self;\n        let mut ctx = Context::new(req, state, exec, remote_addr);\n        if let Err(status) = endpoint.call(&mut ctx).await {\n            ctx.resp.status = status.status_code;\n            if status.expose {\n                ctx.resp.write(status.message);\n            } else {\n                ctx.exec\n                    .spawn_blocking(move || tracing::error!(\"Uncaught status: {}\", status))\n                    .await;\n            }\n        }\n        ctx.resp\n    }\n}\n\nimpl<S: Clone, E> Clone for HttpService<S, E> {\n    fn clone(&self) -> Self {\n        Self {\n            endpoint: self.endpoint.clone(),\n            state: self.state.clone(),\n            exec: self.exec.clone(),\n            remote_addr: self.remote_addr,\n        }\n    }\n}\n\n#[cfg(all(test, feature = \"runtime\"))]\nmod tests {\n    use http::StatusCode;\n\n    use crate::{App, Request};\n\n    #[tokio::test]\n    async fn gate_simple() -> Result<(), Box<dyn std::error::Error>> {\n        let service = App::new().end(()).http_service();\n        let resp = service.serve(Request::default()).await;\n        assert_eq!(StatusCode::OK, resp.status);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/body.rs",
    "content": "use std::mem;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse bytes::{Bytes, BytesMut};\nuse futures::future::ok;\nuse futures::stream::{once, Stream, StreamExt};\nuse tokio::io::{self, AsyncRead, ReadBuf};\n\nconst DEFAULT_CHUNK_SIZE: usize = 4096;\n\n/// The body of response.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa_core::Body;\n/// use futures::StreamExt;\n/// use std::io;\n/// use bytes::Bytes;\n///\n/// async fn read_body(body: Body) -> io::Result<Bytes> {\n///     Ok(match body {\n///         Body::Empty => Bytes::new(),\n///         Body::Once(bytes) => bytes,\n///         Body::Stream(mut stream) => {\n///             let mut bytes = Vec::new();\n///             while let Some(item) = stream.next().await {\n///                 bytes.extend_from_slice(&*item?);\n///             }\n///             bytes.into()\n///         }\n///     })\n/// }\n/// ```\npub enum Body {\n    /// Empty kind\n    Empty,\n\n    /// Bytes kind.\n    Once(Bytes),\n\n    /// Stream kind.\n    Stream(Segment),\n}\n\n/// A boxed stream.\n#[derive(Default)]\npub struct Segment(Option<Pin<Box<dyn Stream<Item = io::Result<Bytes>> + Sync + Send + 'static>>>);\n\nimpl Body {\n    /// Construct an empty body.\n    #[inline]\n    pub fn empty() -> Self {\n        Body::Empty\n    }\n\n    /// Construct a once body.\n    #[inline]\n    pub fn once(bytes: impl Into<Bytes>) -> Self {\n        Body::Once(bytes.into())\n    }\n\n    /// Construct an empty body of stream kind.\n    #[inline]\n    pub fn stream<S>(stream: S) -> Self\n    where\n        S: Stream<Item = io::Result<Bytes>> + Sync + Send + 'static,\n    {\n        Body::Stream(Segment::new(stream))\n    }\n\n    /// Write stream.\n    #[inline]\n    pub fn write_stream(\n        &mut self,\n        stream: impl Stream<Item = io::Result<Bytes>> + Sync + Send + 'static,\n    ) -> &mut Self {\n        match self {\n            Body::Empty => {\n                *self = Self::stream(stream);\n            }\n            Body::Once(bytes) => {\n                let stream = once(ok(mem::take(bytes))).chain(stream);\n                *self = Self::stream(stream);\n            }\n            Body::Stream(segment) => {\n                *self = Self::stream(mem::take(segment).chain(stream));\n            }\n        }\n        self\n    }\n\n    /// Write reader with default chunk size.\n    #[inline]\n    pub fn write_reader(\n        &mut self,\n        reader: impl AsyncRead + Sync + Send + Unpin + 'static,\n    ) -> &mut Self {\n        self.write_chunk(reader, DEFAULT_CHUNK_SIZE)\n    }\n\n    /// Write reader with chunk size.\n    #[inline]\n    pub fn write_chunk(\n        &mut self,\n        reader: impl AsyncRead + Sync + Send + Unpin + 'static,\n        chunk_size: usize,\n    ) -> &mut Self {\n        self.write_stream(ReaderStream::new(reader, chunk_size))\n    }\n\n    /// Write `Bytes`.\n    #[inline]\n    pub fn write(&mut self, data: impl Into<Bytes>) -> &mut Self {\n        match self {\n            Body::Empty => {\n                *self = Self::once(data.into());\n                self\n            }\n            body => body.write_stream(once(ok(data.into()))),\n        }\n    }\n}\n\nimpl Segment {\n    #[inline]\n    fn new(stream: impl Stream<Item = io::Result<Bytes>> + Sync + Send + 'static) -> Self {\n        Self(Some(Box::pin(stream)))\n    }\n}\n\nimpl From<Body> for hyper::Body {\n    #[inline]\n    fn from(body: Body) -> Self {\n        match body {\n            Body::Empty => hyper::Body::empty(),\n            Body::Once(bytes) => hyper::Body::from(bytes),\n            Body::Stream(stream) => hyper::Body::wrap_stream(stream),\n        }\n    }\n}\n\nimpl Default for Body {\n    #[inline]\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\npub struct ReaderStream<R> {\n    chunk_size: usize,\n    reader: R,\n}\n\nimpl<R> ReaderStream<R> {\n    #[inline]\n    fn new(reader: R, chunk_size: usize) -> Self {\n        Self { reader, chunk_size }\n    }\n}\n\nimpl<R> Stream for ReaderStream<R>\nwhere\n    R: AsyncRead + Unpin,\n{\n    type Item = io::Result<Bytes>;\n    #[inline]\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        let chunk_size = self.chunk_size;\n        let mut chunk = BytesMut::with_capacity(chunk_size);\n        unsafe { chunk.set_len(chunk_size) };\n        let mut buf = ReadBuf::new(&mut *chunk);\n        futures::ready!(Pin::new(&mut self.reader).poll_read(cx, &mut buf))?;\n        let filled_len = buf.filled().len();\n        if filled_len == 0 {\n            Poll::Ready(None)\n        } else {\n            Poll::Ready(Some(Ok(chunk.freeze().slice(0..filled_len))))\n        }\n    }\n}\n\nimpl Stream for Body {\n    type Item = io::Result<Bytes>;\n    #[inline]\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        match &mut *self {\n            Body::Empty => Poll::Ready(None),\n            Body::Once(bytes) => {\n                let data = mem::take(bytes);\n                *self = Body::empty();\n                Poll::Ready(Some(Ok(data)))\n            }\n            Body::Stream(stream) => Pin::new(stream).poll_next(cx),\n        }\n    }\n}\n\nimpl Stream for Segment {\n    type Item = io::Result<Bytes>;\n    #[inline]\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        match self.0 {\n            None => Poll::Ready(None),\n            Some(ref mut stream) => stream.as_mut().poll_next(cx),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io;\n\n    use futures::{AsyncReadExt, TryStreamExt};\n    use tokio::fs::File;\n\n    use super::Body;\n\n    async fn read_body(body: Body) -> io::Result<String> {\n        let mut data = String::new();\n        body.into_async_read().read_to_string(&mut data).await?;\n        Ok(data)\n    }\n\n    #[tokio::test]\n    async fn body_empty() -> std::io::Result<()> {\n        let body = Body::default();\n        assert_eq!(\"\", read_body(body).await?);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn body_single() -> std::io::Result<()> {\n        let mut body = Body::default();\n        body.write(\"Hello, World\");\n        assert_eq!(\"Hello, World\", read_body(body).await?);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn body_multiple() -> std::io::Result<()> {\n        let mut body = Body::default();\n        body.write(\"He\").write(\"llo, \").write(\"World\");\n        assert_eq!(\"Hello, World\", read_body(body).await?);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn body_composed() -> std::io::Result<()> {\n        let mut body = Body::empty();\n        body.write(\"He\")\n            .write(\"llo, \")\n            .write_reader(File::open(\"../assets/author.txt\").await?)\n            .write_reader(File::open(\"../assets/author.txt\").await?)\n            .write(\".\");\n        assert_eq!(\"Hello, HexileeHexilee.\", read_body(body).await?);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/context/storage.rs",
    "content": "use std::any::{Any, TypeId};\nuse std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::fmt::Display;\nuse std::ops::Deref;\nuse std::str::FromStr;\nuse std::sync::Arc;\n\nuse http::StatusCode;\n\nuse crate::Status;\n\npub trait Value: Any + Send + Sync {}\n\nimpl<V> Value for V where V: Any + Send + Sync {}\n\n/// A context scoped storage.\n#[derive(Clone)]\npub struct Storage(HashMap<TypeId, HashMap<Cow<'static, str>, Arc<dyn Any + Send + Sync>>>);\n\n/// A wrapper of Arc.\n///\n/// ### Deref\n///\n/// ```rust\n/// use roa_core::Variable;\n///\n/// fn consume<V>(var: Variable<V>) {\n///     let value: &V = &var;\n/// }\n/// ```\n///\n/// ### Parse\n///\n/// ```rust\n/// use roa_core::{Variable, Result};\n///\n/// fn consume<V: AsRef<str>>(var: Variable<V>) -> Result {\n///     let value: i32 = var.parse()?;\n///     Ok(())\n/// }\n/// ```\n#[derive(Debug, Clone)]\npub struct Variable<'a, V> {\n    key: &'a str,\n    value: Arc<V>,\n}\n\nimpl<V> Deref for Variable<'_, V> {\n    type Target = V;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.value\n    }\n}\n\nimpl<'a, V> Variable<'a, V> {\n    /// Construct a variable from name and value.\n    #[inline]\n    fn new(key: &'a str, value: Arc<V>) -> Self {\n        Self { key, value }\n    }\n\n    /// Consume self and get inner Arc<T>.\n    #[inline]\n    pub fn value(self) -> Arc<V> {\n        self.value\n    }\n}\n\nimpl<V> Variable<'_, V>\nwhere\n    V: AsRef<str>,\n{\n    /// A wrapper of `str::parse`. Converts `T::FromStr::Err` to `roa_core::Error` automatically.\n    #[inline]\n    pub fn parse<T>(&self) -> Result<T, Status>\n    where\n        T: FromStr,\n        T::Err: Display,\n    {\n        self.as_ref().parse().map_err(|err| {\n            Status::new(\n                StatusCode::BAD_REQUEST,\n                format!(\n                    \"{}\\ntype of variable `{}` should be {}\",\n                    err,\n                    self.key,\n                    std::any::type_name::<T>()\n                ),\n                true,\n            )\n        })\n    }\n}\n\nimpl Storage {\n    /// Construct an empty Bucket.\n    #[inline]\n    pub fn new() -> Self {\n        Self(HashMap::new())\n    }\n\n    /// Inserts a key-value pair into the storage.\n    ///\n    /// If the storage did not have this key present, [`None`] is returned.\n    ///\n    /// If the storage did have this key present, the value is updated, and the old\n    /// value is returned.\n    pub fn insert<S, K, V>(&mut self, scope: S, key: K, value: V) -> Option<Arc<V>>\n    where\n        S: Any,\n        K: Into<Cow<'static, str>>,\n        V: Value,\n    {\n        let id = TypeId::of::<S>();\n        match self.0.get_mut(&id) {\n            Some(bucket) => bucket\n                .insert(key.into(), Arc::new(value))\n                .and_then(|value| value.downcast().ok()),\n            None => {\n                self.0.insert(id, HashMap::new());\n                self.insert(scope, key, value)\n            }\n        }\n    }\n\n    /// If the storage did not have this key present, [`None`] is returned.\n    ///\n    /// If the storage did have this key present, the key-value pair will be returned as a `Variable`\n    #[inline]\n    pub fn get<'a, S, V>(&self, key: &'a str) -> Option<Variable<'a, V>>\n    where\n        S: Any,\n        V: Value,\n    {\n        let value = self.0.get(&TypeId::of::<S>())?.get(key)?.clone();\n        Some(Variable::new(key, value.clone().downcast().ok()?))\n    }\n}\n\nimpl Default for Storage {\n    #[inline]\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use http::StatusCode;\n\n    use super::{Storage, Variable};\n\n    #[test]\n    fn storage() {\n        struct Scope;\n\n        let mut storage = Storage::default();\n        assert!(storage.get::<Scope, &'static str>(\"id\").is_none());\n        assert!(storage.insert(Scope, \"id\", \"1\").is_none());\n        let id: i32 = storage\n            .get::<Scope, &'static str>(\"id\")\n            .unwrap()\n            .parse()\n            .unwrap();\n        assert_eq!(1, id);\n        assert_eq!(\n            1,\n            storage\n                .insert(Scope, \"id\", \"2\")\n                .unwrap()\n                .parse::<i32>()\n                .unwrap()\n        );\n    }\n\n    #[test]\n    fn variable() {\n        assert_eq!(\n            1,\n            Variable::new(\"id\", Arc::new(\"1\")).parse::<i32>().unwrap()\n        );\n        let result = Variable::new(\"id\", Arc::new(\"x\")).parse::<usize>();\n        assert!(result.is_err());\n        let status = result.unwrap_err();\n        assert_eq!(StatusCode::BAD_REQUEST, status.status_code);\n        assert!(status\n            .message\n            .ends_with(\"type of variable `id` should be usize\"));\n    }\n}\n"
  },
  {
    "path": "roa-core/src/context.rs",
    "content": "mod storage;\n\nuse std::any::Any;\nuse std::borrow::Cow;\nuse std::net::SocketAddr;\nuse std::ops::{Deref, DerefMut};\nuse std::sync::Arc;\n\nuse http::header::AsHeaderName;\nuse http::{Method, StatusCode, Uri, Version};\npub use storage::Variable;\nuse storage::{Storage, Value};\n\nuse crate::{status, Executor, Request, Response};\n\n/// A structure to share request, response and other data between middlewares.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa_core::{App, Context, Next, Result};\n/// use tracing::info;\n/// use tokio::fs::File;\n///\n/// let app = App::new().gate(gate).end(end);\n/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n///     info!(\"{} {}\", ctx.method(), ctx.uri());\n///     next.await\n/// }\n///\n/// async fn end(ctx: &mut Context) -> Result {\n///     ctx.resp.write_reader(File::open(\"assets/welcome.html\").await?);\n///     Ok(())\n/// }\n/// ```\npub struct Context<S = ()> {\n    /// The request, to read http method, uri, version, headers and body.\n    pub req: Request,\n\n    /// The response, to set http status, version, headers and body.\n    pub resp: Response,\n\n    /// The executor, to spawn futures or blocking works.\n    pub exec: Executor,\n\n    /// Socket addr of last client or proxy.\n    pub remote_addr: SocketAddr,\n\n    storage: Storage,\n    state: S,\n}\n\nimpl<S> Context<S> {\n    /// Construct a context from a request, an app and a addr_stream.\n    #[inline]\n    pub(crate) fn new(request: Request, state: S, exec: Executor, remote_addr: SocketAddr) -> Self {\n        Self {\n            req: request,\n            resp: Response::default(),\n            state,\n            exec,\n            storage: Storage::default(),\n            remote_addr,\n        }\n    }\n\n    /// Clone URI.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(\"/\", ctx.uri().to_string());\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn uri(&self) -> &Uri {\n        &self.req.uri\n    }\n\n    /// Clone request::method.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    /// use roa_core::http::Method;\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(Method::GET, ctx.method());\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn method(&self) -> &Method {\n        &self.req.method\n    }\n\n    /// Search for a header value and try to get its string reference.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    /// use roa_core::http::header::CONTENT_TYPE;\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(\n    ///         Some(\"text/plain\"),\n    ///         ctx.get(CONTENT_TYPE),\n    ///     );\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn get(&self, name: impl AsHeaderName) -> Option<&str> {\n        self.req\n            .headers\n            .get(name)\n            .and_then(|value| value.to_str().ok())\n    }\n\n    /// Search for a header value and get its string reference.\n    ///\n    /// Otherwise return a 400 BAD REQUEST.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    /// use roa_core::http::header::CONTENT_TYPE;\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(\n    ///         \"text/plain\",\n    ///         ctx.must_get(CONTENT_TYPE)?,\n    ///     );\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn must_get(&self, name: impl AsHeaderName) -> crate::Result<&str> {\n        let value = self\n            .req\n            .headers\n            .get(name)\n            .ok_or_else(|| status!(StatusCode::BAD_REQUEST))?;\n        value\n            .to_str()\n            .map_err(|err| status!(StatusCode::BAD_REQUEST, err))\n    }\n    /// Clone response::status.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    /// use roa_core::http::StatusCode;\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(StatusCode::OK, ctx.status());\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn status(&self) -> StatusCode {\n        self.resp.status\n    }\n\n    /// Clone request::version.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result};\n    /// use roa_core::http::Version;\n    ///\n    /// let app = App::new().end(get);\n    ///\n    /// async fn get(ctx: &mut Context) -> Result {\n    ///     assert_eq!(Version::HTTP_11, ctx.version());\n    ///     Ok(())\n    /// }\n    /// ```\n    #[inline]\n    pub fn version(&self) -> Version {\n        self.req.version\n    }\n\n    /// Store key-value pair in specific scope.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result, Next};\n    ///\n    /// struct Scope;\n    /// struct AnotherScope;\n    ///\n    /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    ///     ctx.store_scoped(Scope, \"id\", \"1\".to_string());\n    ///     next.await\n    /// }\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     assert_eq!(1, ctx.load_scoped::<Scope, String>(\"id\").unwrap().parse::<i32>()?);\n    ///     assert!(ctx.load_scoped::<AnotherScope, String>(\"id\").is_none());\n    ///     Ok(())\n    /// }\n    ///\n    /// let app = App::new().gate(gate).end(end);\n    /// ```\n    #[inline]\n    pub fn store_scoped<SC, K, V>(&mut self, scope: SC, key: K, value: V) -> Option<Arc<V>>\n    where\n        SC: Any,\n        K: Into<Cow<'static, str>>,\n        V: Value,\n    {\n        self.storage.insert(scope, key, value)\n    }\n\n    /// Store key-value pair in public scope.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result, Next};\n    ///\n    /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    ///     ctx.store(\"id\", \"1\".to_string());\n    ///     next.await\n    /// }\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     assert_eq!(1, ctx.load::<String>(\"id\").unwrap().parse::<i32>()?);\n    ///     Ok(())\n    /// }\n    ///\n    /// let app = App::new().gate(gate).end(end);\n    /// ```\n    #[inline]\n    pub fn store<K, V>(&mut self, key: K, value: V) -> Option<Arc<V>>\n    where\n        K: Into<Cow<'static, str>>,\n        V: Value,\n    {\n        self.store_scoped(PublicScope, key, value)\n    }\n\n    /// Search for value by key in specific scope.\n    ///\n    /// ### Example\n    ///\n    /// ```rust\n    /// use roa_core::{App, Context, Result, Next};\n    ///\n    /// struct Scope;\n    ///\n    /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    ///     ctx.store_scoped(Scope, \"id\", \"1\".to_owned());\n    ///     next.await\n    /// }\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     assert_eq!(1, ctx.load_scoped::<Scope, String>(\"id\").unwrap().parse::<i32>()?);\n    ///     Ok(())\n    /// }\n    ///\n    /// let app = App::new().gate(gate).end(end);\n    /// ```\n    #[inline]\n    pub fn load_scoped<'a, SC, V>(&self, key: &'a str) -> Option<Variable<'a, V>>\n    where\n        SC: Any,\n        V: Value,\n    {\n        self.storage.get::<SC, V>(key)\n    }\n\n    /// Search for value by key in public scope.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result, Next};\n    ///\n    /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    ///     ctx.store(\"id\", \"1\".to_string());\n    ///     next.await\n    /// }\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     assert_eq!(1, ctx.load::<String>(\"id\").unwrap().parse::<i32>()?);\n    ///     Ok(())\n    /// }\n    ///\n    /// let app = App::new().gate(gate).end(end);\n    /// ```\n    #[inline]\n    pub fn load<'a, V>(&self, key: &'a str) -> Option<Variable<'a, V>>\n    where\n        V: Value,\n    {\n        self.load_scoped::<PublicScope, V>(key)\n    }\n}\n\n/// Public storage scope.\nstruct PublicScope;\n\nimpl<S> Deref for Context<S> {\n    type Target = S;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.state\n    }\n}\n\nimpl<S> DerefMut for Context<S> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.state\n    }\n}\n\nimpl<S: Clone> Clone for Context<S> {\n    #[inline]\n    fn clone(&self) -> Self {\n        Self {\n            req: Request::default(),\n            resp: Response::new(),\n            state: self.state.clone(),\n            exec: self.exec.clone(),\n            storage: self.storage.clone(),\n            remote_addr: self.remote_addr,\n        }\n    }\n}\n\n#[cfg(all(test, feature = \"runtime\"))]\nmod tests_with_runtime {\n    use std::error::Error;\n\n    use http::{HeaderValue, StatusCode, Version};\n\n    use crate::{App, Context, Next, Request, Status};\n\n    #[tokio::test]\n    async fn status_and_version() -> Result<(), Box<dyn Error>> {\n        async fn test(ctx: &mut Context) -> Result<(), Status> {\n            assert_eq!(Version::HTTP_11, ctx.version());\n            assert_eq!(StatusCode::OK, ctx.status());\n            Ok(())\n        }\n        let service = App::new().end(test).http_service();\n        service.serve(Request::default()).await;\n        Ok(())\n    }\n\n    #[derive(Clone)]\n    struct State {\n        data: usize,\n    }\n\n    #[tokio::test]\n    async fn state() -> Result<(), Box<dyn Error>> {\n        async fn gate(ctx: &mut Context<State>, next: Next<'_>) -> Result<(), Status> {\n            ctx.data = 1;\n            next.await\n        }\n\n        async fn test(ctx: &mut Context<State>) -> Result<(), Status> {\n            assert_eq!(1, ctx.data);\n            Ok(())\n        }\n        let service = App::state(State { data: 1 })\n            .gate(gate)\n            .end(test)\n            .http_service();\n        service.serve(Request::default()).await;\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn must_get() -> Result<(), Box<dyn Error>> {\n        use http::header::{CONTENT_TYPE, HOST};\n        async fn test(ctx: &mut Context) -> Result<(), Status> {\n            assert_eq!(Ok(\"github.com\"), ctx.must_get(HOST));\n            ctx.must_get(CONTENT_TYPE)?;\n            unreachable!()\n        }\n        let service = App::new().end(test).http_service();\n        let mut req = Request::default();\n        req.headers\n            .insert(HOST, HeaderValue::from_static(\"github.com\"));\n        let resp = service.serve(req).await;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/err.rs",
    "content": "use std::fmt::{Display, Formatter};\nuse std::result::Result as StdResult;\n\npub use http::StatusCode;\n\n/// Type alias for `StdResult`.\npub type Result<R = ()> = StdResult<R, Status>;\n\n/// Construct a `Status`.\n///\n/// - `status!(status_code)` will be expanded to `status!(status_code, \"\")`\n/// - `status!(status_code, message)` will be expanded to `status!(status_code, message, true)`\n/// - `status!(status_code, message, expose)` will be expanded to `Status::new(status_code, message, expose)`\n///\n/// ### Example\n/// ```rust\n/// use roa_core::{App, Context, Next, Result, status};\n/// use roa_core::http::StatusCode;\n///\n/// let app = App::new()\n///     .gate(gate)\n///     .end(status!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\"));\n/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n///     next.await?; // throw\n///     unreachable!();\n///     ctx.resp.status = StatusCode::OK;\n///     Ok(())\n/// }\n/// ```\n#[macro_export]\nmacro_rules! status {\n    ($status_code:expr) => {\n        $crate::status!($status_code, \"\")\n    };\n    ($status_code:expr, $message:expr) => {\n        $crate::status!($status_code, $message, true)\n    };\n    ($status_code:expr, $message:expr, $expose:expr) => {\n        $crate::Status::new($status_code, $message, $expose)\n    };\n}\n\n/// Throw an `Err(Status)`.\n///\n/// - `throw!(status_code)` will be expanded to `throw!(status_code, \"\")`\n/// - `throw!(status_code, message)` will be expanded to `throw!(status_code, message, true)`\n/// - `throw!(status_code, message, expose)` will be expanded to `return Err(Status::new(status_code, message, expose));`\n///\n/// ### Example\n/// ```rust\n/// use roa_core::{App, Context, Next, Result, throw};\n/// use roa_core::http::StatusCode;\n///\n/// let app = App::new().gate(gate).end(end);\n/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n///     next.await?; // throw\n///     unreachable!();\n///     ctx.resp.status = StatusCode::OK;\n///     Ok(())\n/// }\n///\n/// async fn end(ctx: &mut Context) -> Result {\n///     throw!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\"); // throw\n///     unreachable!()\n/// }\n/// ```\n#[macro_export]\nmacro_rules! throw {\n    ($status_code:expr) => {\n        return core::result::Result::Err($crate::status!($status_code))\n    };\n    ($status_code:expr, $message:expr) => {\n        return core::result::Result::Err($crate::status!($status_code, $message))\n    };\n    ($status_code:expr, $message:expr, $expose:expr) => {\n        return core::result::Result::Err($crate::status!($status_code, $message, $expose))\n    };\n}\n\n/// The `Status` of roa.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct Status {\n    /// StatusCode will be responded to client if Error is thrown by the top middleware.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Next, Result, MiddlewareExt, throw};\n    /// use roa_core::http::StatusCode;\n    ///\n    /// let app = App::new().gate(gate).end(end);\n    /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n    ///     ctx.resp.status = StatusCode::OK;\n    ///     next.await // not caught\n    /// }\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     throw!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\"); // throw\n    ///     unreachable!()\n    /// }\n    /// ```\n    pub status_code: StatusCode,\n\n    /// Data will be written to response body if self.expose is true.\n    /// StatusCode will be responded to client if Error is thrown by the top middleware.\n    ///\n    /// ### Example\n    /// ```rust\n    /// use roa_core::{App, Context, Result, Status};\n    /// use roa_core::http::StatusCode;\n    ///\n    /// let app = App::new().end(end);\n    ///\n    /// async fn end(ctx: &mut Context) -> Result {\n    ///     Err(Status::new(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\", false)) // message won't be exposed to user.\n    /// }\n    ///\n    /// ```\n    pub message: String,\n\n    /// if message exposed.\n    pub expose: bool,\n}\n\nimpl Status {\n    /// Construct an error.\n    #[inline]\n    pub fn new(status_code: StatusCode, message: impl ToString, expose: bool) -> Self {\n        Self {\n            status_code,\n            message: message.to_string(),\n            expose,\n        }\n    }\n}\n\nimpl<E> From<E> for Status\nwhere\n    E: std::error::Error,\n{\n    #[inline]\n    fn from(err: E) -> Self {\n        Self::new(StatusCode::INTERNAL_SERVER_ERROR, err, false)\n    }\n}\n\nimpl Display for Status {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), std::fmt::Error> {\n        f.write_str(&format!(\"{}: {}\", self.status_code, self.message))\n    }\n}\n"
  },
  {
    "path": "roa-core/src/executor.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::Arc;\n\nuse futures::channel::oneshot::{channel, Receiver};\nuse futures::task::{Context, Poll};\nuse hyper::rt;\n\n/// Future Object\npub type FutureObj = Pin<Box<dyn 'static + Send + Future<Output = ()>>>;\n\n/// Blocking task Object\npub type BlockingObj = Box<dyn 'static + Send + FnOnce()>;\n\n/// Executor constraint.\npub trait Spawn {\n    /// Spawn a future object\n    fn spawn(&self, fut: FutureObj);\n\n    /// Spawn a blocking task object\n    fn spawn_blocking(&self, task: BlockingObj);\n}\n\n/// A type implementing hyper::rt::Executor\n#[derive(Clone)]\npub struct Executor(pub(crate) Arc<dyn 'static + Send + Sync + Spawn>);\n\n/// A handle that awaits the result of a task.\npub struct JoinHandle<T>(Receiver<T>);\n\nimpl Executor {\n    /// Spawn a future by app runtime\n    #[inline]\n    pub fn spawn<Fut>(&self, fut: Fut) -> JoinHandle<Fut::Output>\n    where\n        Fut: 'static + Send + Future,\n        Fut::Output: 'static + Send,\n    {\n        let (sender, recv) = channel();\n        self.0.spawn(Box::pin(async move {\n            if sender.send(fut.await).is_err() {\n                // handler is dropped, do nothing.\n            };\n        }));\n        JoinHandle(recv)\n    }\n\n    /// Spawn a blocking task by app runtime\n    #[inline]\n    pub fn spawn_blocking<T, R>(&self, task: T) -> JoinHandle<R>\n    where\n        T: 'static + Send + FnOnce() -> R,\n        R: 'static + Send,\n    {\n        let (sender, recv) = channel();\n        self.0.spawn_blocking(Box::new(|| {\n            if sender.send(task()).is_err() {\n                // handler is dropped, do nothing.\n            };\n        }));\n        JoinHandle(recv)\n    }\n}\n\nimpl<T> Future for JoinHandle<T> {\n    type Output = T;\n    #[inline]\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let ready = futures::ready!(Pin::new(&mut self.0).poll(cx));\n        Poll::Ready(ready.expect(\"receiver in JoinHandle shouldn't be canceled\"))\n    }\n}\n\nimpl<F> rt::Executor<F> for Executor\nwhere\n    F: 'static + Send + Future,\n    F::Output: 'static + Send,\n{\n    #[inline]\n    fn execute(&self, fut: F) {\n        self.0.spawn(Box::pin(async move {\n            let _ = fut.await;\n        }));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use super::{BlockingObj, Executor, FutureObj, Spawn};\n\n    pub struct Exec;\n\n    impl Spawn for Exec {\n        fn spawn(&self, fut: FutureObj) {\n            tokio::task::spawn(fut);\n        }\n\n        fn spawn_blocking(&self, task: BlockingObj) {\n            tokio::task::spawn_blocking(task);\n        }\n    }\n\n    #[tokio::test]\n    async fn spawn() {\n        let exec = Executor(Arc::new(Exec));\n        assert_eq!(1, exec.spawn(async { 1 }).await);\n    }\n\n    #[tokio::test]\n    async fn spawn_blocking() {\n        let exec = Executor(Arc::new(Exec));\n        assert_eq!(1, exec.spawn_blocking(|| 1).await);\n    }\n}\n"
  },
  {
    "path": "roa-core/src/group.rs",
    "content": "use std::sync::Arc;\n\nuse crate::{async_trait, Context, Endpoint, Middleware, Next, Result};\n\n/// A set of method to chain middleware/endpoint to middleware\n/// or make middleware shared.\npub trait MiddlewareExt<S>: Sized + for<'a> Middleware<'a, S> {\n    /// Chain two middlewares.\n    fn chain<M>(self, next: M) -> Chain<Self, M>\n    where\n        M: for<'a> Middleware<'a, S>,\n    {\n        Chain(self, next)\n    }\n\n    /// Chain an endpoint to a middleware.\n    fn end<E>(self, next: E) -> Chain<Self, E>\n    where\n        E: for<'a> Endpoint<'a, S>,\n    {\n        Chain(self, next)\n    }\n\n    /// Make middleware shared.\n    fn shared(self) -> Shared<S>\n    where\n        S: 'static,\n    {\n        Shared(Arc::new(self))\n    }\n}\n\n/// Extra methods of endpoint.\npub trait EndpointExt<S>: Sized + for<'a> Endpoint<'a, S> {\n    /// Box an endpoint.\n    fn boxed(self) -> Boxed<S>\n    where\n        S: 'static,\n    {\n        Boxed(Box::new(self))\n    }\n}\n\nimpl<S, T> MiddlewareExt<S> for T where T: for<'a> Middleware<'a, S> {}\nimpl<S, T> EndpointExt<S> for T where T: for<'a> Endpoint<'a, S> {}\n\n/// A middleware composing and executing other middlewares in a stack-like manner.\npub struct Chain<T, U>(T, U);\n\n/// Shared middleware.\npub struct Shared<S>(Arc<dyn for<'a> Middleware<'a, S>>);\n\n/// Boxed endpoint.\npub struct Boxed<S>(Box<dyn for<'a> Endpoint<'a, S>>);\n\n#[async_trait(?Send)]\nimpl<'a, S, T, U> Middleware<'a, S> for Chain<T, U>\nwhere\n    U: Middleware<'a, S>,\n    T: for<'b> Middleware<'b, S>,\n{\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        let ptr = ctx as *mut Context<S>;\n        let mut next = self.1.handle(unsafe { &mut *ptr }, next);\n        self.0.handle(ctx, &mut next).await\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Middleware<'a, S> for Shared<S>\nwhere\n    S: 'static,\n{\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        self.0.handle(ctx, next).await\n    }\n}\n\nimpl<S> Clone for Shared<S> {\n    #[inline]\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for Boxed<S>\nwhere\n    S: 'static,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        self.0.call(ctx).await\n    }\n}\n\n#[async_trait(?Send)]\nimpl<'a, S, T, U> Endpoint<'a, S> for Chain<T, U>\nwhere\n    U: Endpoint<'a, S>,\n    T: for<'b> Middleware<'b, S>,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        let ptr = ctx as *mut Context<S>;\n        let mut next = self.1.call(unsafe { &mut *ptr });\n        self.0.handle(ctx, &mut next).await\n    }\n}\n\n#[cfg(all(test, feature = \"runtime\"))]\nmod tests {\n    use std::sync::Arc;\n\n    use futures::lock::Mutex;\n    use http::StatusCode;\n\n    use crate::{async_trait, App, Context, Middleware, Next, Request, Status};\n\n    struct Pusher {\n        data: usize,\n        vector: Arc<Mutex<Vec<usize>>>,\n    }\n\n    impl Pusher {\n        fn new(data: usize, vector: Arc<Mutex<Vec<usize>>>) -> Self {\n            Self { data, vector }\n        }\n    }\n\n    #[async_trait(?Send)]\n    impl<'a> Middleware<'a, ()> for Pusher {\n        async fn handle(&'a self, _ctx: &'a mut Context, next: Next<'a>) -> Result<(), Status> {\n            self.vector.lock().await.push(self.data);\n            next.await?;\n            self.vector.lock().await.push(self.data);\n            Ok(())\n        }\n    }\n\n    #[tokio::test]\n    async fn middleware_order() -> Result<(), Box<dyn std::error::Error>> {\n        let vector = Arc::new(Mutex::new(Vec::new()));\n        let service = App::new()\n            .gate(Pusher::new(0, vector.clone()))\n            .gate(Pusher::new(1, vector.clone()))\n            .gate(Pusher::new(2, vector.clone()))\n            .gate(Pusher::new(3, vector.clone()))\n            .gate(Pusher::new(4, vector.clone()))\n            .gate(Pusher::new(5, vector.clone()))\n            .gate(Pusher::new(6, vector.clone()))\n            .gate(Pusher::new(7, vector.clone()))\n            .gate(Pusher::new(8, vector.clone()))\n            .gate(Pusher::new(9, vector.clone()))\n            .end(())\n            .http_service();\n        let resp = service.serve(Request::default()).await;\n        assert_eq!(StatusCode::OK, resp.status);\n        for i in 0..10 {\n            assert_eq!(i, vector.lock().await[i]);\n            assert_eq!(i, vector.lock().await[19 - i]);\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/lib.rs",
    "content": "#![cfg_attr(feature = \"docs\", feature(doc_cfg))]\n#![cfg_attr(feature = \"docs\", doc = include_str!(\"../README.md\"))]\n#![cfg_attr(feature = \"docs\", warn(missing_docs))]\n\nmod app;\nmod body;\nmod context;\nmod err;\nmod executor;\nmod group;\nmod middleware;\nmod request;\nmod response;\nmod state;\n\n#[doc(inline)]\npub use app::{AddrStream, App};\npub use async_trait::async_trait;\n#[doc(inline)]\npub use body::Body;\n#[doc(inline)]\npub use context::{Context, Variable};\n#[doc(inline)]\npub use err::{Result, Status};\n#[doc(inline)]\npub use executor::{Executor, JoinHandle, Spawn};\n#[doc(inline)]\npub use group::{Boxed, Chain, EndpointExt, MiddlewareExt, Shared};\npub use http;\npub use hyper::server::accept::Accept;\npub use hyper::server::Server;\n#[doc(inline)]\npub use middleware::{Endpoint, Middleware, Next};\n#[doc(inline)]\npub use request::Request;\n#[doc(inline)]\npub use response::Response;\n#[doc(inline)]\npub use state::State;\n"
  },
  {
    "path": "roa-core/src/middleware.rs",
    "content": "use std::future::Future;\n\nuse http::header::LOCATION;\nuse http::{StatusCode, Uri};\n\nuse crate::{async_trait, throw, Context, Result, Status};\n\n/// ### Middleware\n///\n/// #### Build-in middlewares\n///\n/// - Functional middleware\n///\n/// A functional middleware is an async function with signature:\n/// `async fn(&mut Context, Next<'_>) -> Result`.\n///\n/// ```rust\n/// use roa_core::{App, Context, Next, Result};\n///\n/// async fn middleware(ctx: &mut Context, next: Next<'_>) -> Result {\n///     next.await\n/// }\n///\n/// let app = App::new().gate(middleware);\n/// ```\n///\n/// - Blank middleware\n///\n/// `()` is a blank middleware, it just calls the next middleware or endpoint.\n///\n/// ```rust\n/// let app = roa_core::App::new().gate(());\n/// ```\n///\n/// #### Custom middleware\n///\n/// You can implement custom `Middleware` for other types.\n///\n/// ```rust\n/// use roa_core::{App, Middleware, Context, Next, Result, async_trait};\n/// use std::sync::Arc;\n/// use std::time::Instant;\n///\n///\n/// struct Logger;\n///\n/// #[async_trait(?Send)]\n/// impl <'a> Middleware<'a> for Logger {\n///     async fn handle(&'a self, ctx: &'a mut Context, next: Next<'a>) -> Result {\n///         let start = Instant::now();\n///         let result = next.await;\n///         println!(\"time elapsed: {}ms\", start.elapsed().as_millis());\n///         result\n///     }\n/// }\n///\n/// let app = App::new().gate(Logger);\n/// ```\n#[async_trait(?Send)]\npub trait Middleware<'a, S = ()>: 'static + Sync + Send {\n    /// Handle context and next, return status.\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result;\n}\n\n#[async_trait(?Send)]\nimpl<'a, S, T, F> Middleware<'a, S> for T\nwhere\n    S: 'a,\n    T: 'static + Send + Sync + Fn(&'a mut Context<S>, Next<'a>) -> F,\n    F: 'a + Future<Output = Result>,\n{\n    #[inline]\n    async fn handle(&'a self, ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        (self)(ctx, next).await\n    }\n}\n\n/// ### Endpoint\n///\n/// An endpoint is a request handler.\n///\n/// #### Build-in endpoint\n///\n/// There are some build-in endpoints.\n///\n/// - Functional endpoint\n///\n/// A normal functional endpoint is an async function with signature:\n/// `async fn(&mut Context) -> Result`.\n///\n/// ```rust\n/// use roa_core::{App, Context, Result};\n///\n/// async fn endpoint(ctx: &mut Context) -> Result {\n///     Ok(())\n/// }\n///\n/// let app = App::new().end(endpoint);\n/// ```\n/// - Ok endpoint\n///\n/// `()` is an endpoint always return `Ok(())`\n///\n/// ```rust\n/// let app = roa_core::App::new().end(());\n/// ```\n///\n/// - Status endpoint\n///\n/// `Status` is an endpoint always return `Err(Status)`\n///\n/// ```rust\n/// use roa_core::{App, status};\n/// use roa_core::http::StatusCode;\n/// let app = App::new().end(status!(StatusCode::BAD_REQUEST));\n/// ```\n///\n/// - String endpoint\n///\n/// Write string to body.\n///\n/// ```rust\n/// use roa_core::App;\n///\n/// let app = App::new().end(\"Hello, world\"); // static slice\n/// let app = App::new().end(\"Hello, world\".to_owned()); // string\n/// ```\n///\n/// - Redirect endpoint\n///\n/// Redirect to an uri.\n///\n/// ```rust\n/// use roa_core::App;\n/// use roa_core::http::Uri;\n///\n/// let app = App::new().end(\"/target\".parse::<Uri>().unwrap());\n/// ```\n///\n/// #### Custom endpoint\n///\n/// You can implement custom `Endpoint` for your types.\n///\n/// ```rust\n/// use roa_core::{App, Endpoint, Context, Next, Result, async_trait};\n///\n/// fn is_endpoint(endpoint: impl for<'a> Endpoint<'a>) {\n/// }\n///\n/// struct Service;\n///\n/// #[async_trait(?Send)]\n/// impl <'a> Endpoint<'a> for Service {\n///     async fn call(&'a self, ctx: &'a mut Context) -> Result {\n///         Ok(())\n///     }\n/// }\n///\n/// let app = App::new().end(Service);\n/// ```\n#[async_trait(?Send)]\npub trait Endpoint<'a, S = ()>: 'static + Sync + Send {\n    /// Call this endpoint.\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result;\n}\n\n#[async_trait(?Send)]\nimpl<'a, S, T, F> Endpoint<'a, S> for T\nwhere\n    S: 'a,\n    T: 'static + Send + Sync + Fn(&'a mut Context<S>) -> F,\n    F: 'a + Future<Output = Result>,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        (self)(ctx).await\n    }\n}\n\n/// blank middleware.\n#[async_trait(?Send)]\nimpl<'a, S> Middleware<'a, S> for () {\n    #[allow(clippy::trivially_copy_pass_by_ref)]\n    #[inline]\n    async fn handle(&'a self, _ctx: &'a mut Context<S>, next: Next<'a>) -> Result {\n        next.await\n    }\n}\n\n/// ok endpoint, always return Ok(())\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for () {\n    #[allow(clippy::trivially_copy_pass_by_ref)]\n    #[inline]\n    async fn call(&'a self, _ctx: &'a mut Context<S>) -> Result {\n        Ok(())\n    }\n}\n\n/// status endpoint.\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for Status {\n    #[inline]\n    async fn call(&'a self, _ctx: &'a mut Context<S>) -> Result {\n        Err(self.clone())\n    }\n}\n\n/// String endpoint.\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for String {\n    #[inline]\n    #[allow(clippy::ptr_arg)]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        ctx.resp.write(self.clone());\n        Ok(())\n    }\n}\n\n/// Static slice endpoint.\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for &'static str {\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        ctx.resp.write(*self);\n        Ok(())\n    }\n}\n\n/// Redirect endpoint.\n#[async_trait(?Send)]\nimpl<'a, S> Endpoint<'a, S> for Uri {\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        ctx.resp.headers.insert(LOCATION, self.to_string().parse()?);\n        throw!(StatusCode::PERMANENT_REDIRECT)\n    }\n}\n\n/// Type of the second parameter in a middleware,\n/// an alias for `&mut (dyn Unpin + Future<Output = Result>)`\n///\n/// Developer of middleware can jump to next middleware by calling `next.await`.\n///\n/// ### Example\n///\n/// ```rust\n/// use roa_core::{App, Context, Result, Status, MiddlewareExt, Next};\n/// use roa_core::http::StatusCode;\n///\n/// let app = App::new()\n///     .gate(first)\n///     .gate(second)\n///     .gate(third)\n///     .end(end);\n/// async fn first(ctx: &mut Context, next: Next<'_>) -> Result {\n///     assert!(ctx.store(\"id\", \"1\").is_none());\n///     next.await?;\n///     assert_eq!(\"5\", *ctx.load::<&'static str>(\"id\").unwrap());\n///     Ok(())\n/// }\n/// async fn second(ctx: &mut Context, next: Next<'_>) -> Result {\n///     assert_eq!(\"1\", *ctx.load::<&'static str>(\"id\").unwrap());\n///     assert_eq!(\"1\", *ctx.store(\"id\", \"2\").unwrap());\n///     next.await?;\n///     assert_eq!(\"4\", *ctx.store(\"id\", \"5\").unwrap());\n///     Ok(())\n/// }\n/// async fn third(ctx: &mut Context, next: Next<'_>) -> Result {\n///     assert_eq!(\"2\", *ctx.store(\"id\", \"3\").unwrap());\n///     next.await?; // next is none; do nothing\n///     assert_eq!(\"3\", *ctx.store(\"id\", \"4\").unwrap());\n///     Ok(())\n/// }\n///\n/// async fn end(ctx: &mut Context) -> Result {\n///     assert_eq!(\"3\", *ctx.load::<&'static str>(\"id\").unwrap());\n///     Ok(())\n/// }\n/// ```\n///\n/// ### Error Handling\n///\n/// You can catch or straightly throw a Error returned by next.\n///\n/// ```rust\n/// use roa_core::{App, Context, Result, Status, MiddlewareExt, Next, status};\n/// use roa_core::http::StatusCode;\n///         \n/// let app = App::new()\n///     .gate(catch)\n///     .gate(gate)\n///     .end(status!(StatusCode::IM_A_TEAPOT, \"I'm a teapot!\"));\n///\n/// async fn catch(ctx: &mut Context, next: Next<'_>) -> Result {\n///     // catch\n///     if let Err(err) = next.await {\n///         // teapot is ok\n///         if err.status_code != StatusCode::IM_A_TEAPOT {\n///             return Err(err);\n///         }\n///     }\n///     Ok(())\n/// }\n/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {\n///     next.await?; // just throw\n///     unreachable!()\n/// }\n/// ```\n///\npub type Next<'a> = &'a mut (dyn Unpin + Future<Output = Result>);\n\n#[cfg(test)]\nmod tests {\n    use futures::{AsyncReadExt, TryStreamExt};\n    use http::header::LOCATION;\n    use http::{StatusCode, Uri};\n\n    use crate::{status, App, Request};\n\n    const HELLO: &str = \"Hello, world\";\n\n    #[tokio::test]\n    async fn status_endpoint() {\n        let app = App::new().end(status!(StatusCode::BAD_REQUEST));\n        let service = app.http_service();\n        let resp = service.serve(Request::default()).await;\n        assert_eq!(StatusCode::BAD_REQUEST, resp.status);\n    }\n\n    #[tokio::test]\n    async fn string_endpoint() {\n        let app = App::new().end(HELLO.to_owned());\n        let service = app.http_service();\n        let mut data = String::new();\n        service\n            .serve(Request::default())\n            .await\n            .body\n            .into_async_read()\n            .read_to_string(&mut data)\n            .await\n            .unwrap();\n        assert_eq!(HELLO, data);\n    }\n    #[tokio::test]\n    async fn static_slice_endpoint() {\n        let app = App::new().end(HELLO);\n        let service = app.http_service();\n        let mut data = String::new();\n        service\n            .serve(Request::default())\n            .await\n            .body\n            .into_async_read()\n            .read_to_string(&mut data)\n            .await\n            .unwrap();\n        assert_eq!(HELLO, data);\n    }\n    #[tokio::test]\n    async fn redirect_endpoint() {\n        let app = App::new().end(\"/target\".parse::<Uri>().unwrap());\n        let service = app.http_service();\n        let resp = service.serve(Request::default()).await;\n        assert_eq!(StatusCode::PERMANENT_REDIRECT, resp.status);\n        assert_eq!(\"/target\", resp.headers[LOCATION].to_str().unwrap())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/request.rs",
    "content": "use std::io;\n\nuse bytes::Bytes;\nuse futures::stream::{Stream, TryStreamExt};\nuse http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version};\nuse hyper::Body;\nuse tokio::io::AsyncRead;\nuse tokio_util::io::StreamReader;\n/// Http request type of roa.\npub struct Request {\n    /// The request's method\n    pub method: Method,\n\n    /// The request's URI\n    pub uri: Uri,\n\n    /// The request's version\n    pub version: Version,\n\n    /// The request's headers\n    pub headers: HeaderMap<HeaderValue>,\n\n    extensions: Extensions,\n\n    body: Body,\n}\n\nimpl Request {\n    /// Take raw hyper request.\n    /// This method will consume inner body and extensions.\n    #[inline]\n    pub fn take_raw(&mut self) -> http::Request<Body> {\n        let mut builder = http::Request::builder()\n            .method(self.method.clone())\n            .uri(self.uri.clone());\n        *builder.extensions_mut().expect(\"fail to get extensions\") =\n            std::mem::take(&mut self.extensions);\n        *builder.headers_mut().expect(\"fail to get headers\") = self.headers.clone();\n        builder\n            .body(self.raw_body())\n            .expect(\"fail to build raw body\")\n    }\n\n    /// Gake raw hyper body.\n    /// This method will consume inner body.\n    #[inline]\n    pub fn raw_body(&mut self) -> Body {\n        std::mem::take(&mut self.body)\n    }\n    /// Get body as Stream.\n    /// This method will consume inner body.\n    #[inline]\n    pub fn stream(\n        &mut self,\n    ) -> impl Stream<Item = io::Result<Bytes>> + Sync + Send + Unpin + 'static {\n        self.raw_body()\n            .map_err(|err| io::Error::new(io::ErrorKind::Other, err))\n    }\n\n    /// Get body as AsyncRead.\n    /// This method will consume inner body.\n    #[inline]\n    pub fn reader(&mut self) -> impl AsyncRead + Sync + Send + Unpin + 'static {\n        StreamReader::new(self.stream())\n    }\n}\n\nimpl From<http::Request<Body>> for Request {\n    #[inline]\n    fn from(req: http::Request<Body>) -> Self {\n        let (parts, body) = req.into_parts();\n        Self {\n            method: parts.method,\n            uri: parts.uri,\n            version: parts.version,\n            headers: parts.headers,\n            extensions: parts.extensions,\n            body,\n        }\n    }\n}\n\nimpl Default for Request {\n    #[inline]\n    fn default() -> Self {\n        http::Request::new(Body::empty()).into()\n    }\n}\n\n#[cfg(all(test, feature = \"runtime\"))]\nmod tests {\n    use http::StatusCode;\n    use hyper::Body;\n    use tokio::io::AsyncReadExt;\n\n    use crate::{App, Context, Request, Status};\n\n    async fn test(ctx: &mut Context) -> Result<(), Status> {\n        let mut data = String::new();\n        ctx.req.reader().read_to_string(&mut data).await?;\n        assert_eq!(\"Hello, World!\", data);\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn body_read() -> Result<(), Box<dyn std::error::Error>> {\n        let app = App::new().end(test);\n        let service = app.http_service();\n        let req = Request::from(http::Request::new(Body::from(\"Hello, World!\")));\n        let resp = service.serve(req).await;\n        assert_eq!(StatusCode::OK, resp.status);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "roa-core/src/response.rs",
    "content": "//! A module for Response and its body\nuse std::ops::{Deref, DerefMut};\n\nuse http::{HeaderMap, HeaderValue, StatusCode, Version};\n\npub use crate::Body;\n\n/// Http response type of roa.\npub struct Response {\n    /// Status code.\n    pub status: StatusCode,\n\n    /// Version of HTTP protocol.\n    pub version: Version,\n\n    /// Raw header map.\n    pub headers: HeaderMap<HeaderValue>,\n\n    /// Response body.\n    pub body: Body,\n}\n\nimpl Response {\n    #[inline]\n    pub(crate) fn new() -> Self {\n        Self {\n            status: StatusCode::default(),\n            version: Version::default(),\n            headers: HeaderMap::default(),\n            body: Body::default(),\n        }\n    }\n\n    #[inline]\n    fn into_resp(self) -> http::Response<hyper::Body> {\n        let (mut parts, _) = http::Response::new(()).into_parts();\n        let Response {\n            status,\n            version,\n            headers,\n            body,\n        } = self;\n        parts.status = status;\n        parts.version = version;\n        parts.headers = headers;\n        http::Response::from_parts(parts, body.into())\n    }\n}\n\nimpl Deref for Response {\n    type Target = Body;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.body\n    }\n}\n\nimpl DerefMut for Response {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.body\n    }\n}\n\nimpl From<Response> for http::Response<hyper::Body> {\n    #[inline]\n    fn from(value: Response) -> Self {\n        value.into_resp()\n    }\n}\n\nimpl Default for Response {\n    #[inline]\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "roa-core/src/state.rs",
    "content": "/// The `State` trait, should be replace with trait alias.\n/// The `App::state` will be cloned when a request inbounds.\n///\n/// `State` is designed to share data or handler between middlewares.\n///\n/// ### Example\n/// ```rust\n/// use roa_core::{App, Context, Next, Result};\n/// use roa_core::http::StatusCode;\n///\n/// #[derive(Clone)]\n/// struct State {\n///     id: u64,\n/// }\n///\n/// let app = App::state(State { id: 0 }).gate(gate).end(end);\n/// async fn gate(ctx: &mut Context<State>, next: Next<'_>) -> Result {\n///     ctx.id = 1;\n///     next.await\n/// }\n///\n/// async fn end(ctx: &mut Context<State>) -> Result {\n///     let id = ctx.id;\n///     assert_eq!(1, id);\n///     Ok(())\n/// }\n/// ```\npub trait State: 'static + Clone + Send + Sync + Sized {}\n\nimpl<T: 'static + Clone + Send + Sync + Sized> State for T {}\n"
  },
  {
    "path": "roa-diesel/Cargo.toml",
    "content": "[package]\nname = \"roa-diesel\"\nversion = \"0.6.0\"\nauthors = [\"Hexilee <i@hexilee.me>\"]\nedition = \"2018\"\nlicense = \"MIT\"\nreadme = \"./README.md\"\nrepository = \"https://github.com/Hexilee/roa\"\ndocumentation = \"https://docs.rs/roa-diesel\"\nhomepage = \"https://github.com/Hexilee/roa/wiki\"\ndescription = \"diesel integration with roa framework\"\nkeywords = [\"http\", \"web\", \"framework\", \"orm\"]\ncategories = [\"database\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"docs\"]\nrustdoc-args = [\"--cfg\", \"feature=\\\"docs\\\"\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nroa = { path = \"../roa\", version = \"0.6.0\", default-features = false }\ndiesel = { version = \"1.4\", features = [\"extras\"] }\nr2d2 = \"0.8\"\n\n[dev-dependencies]\ndiesel = { version = \"1.4\", features = [\"extras\", \"sqlite\"] }\n\n[features]\ndocs = [\"roa/docs\"]"
  },
  {
    "path": "roa-diesel/README.md",
    "content": "[![Stable Test](https://github.com/Hexilee/roa/workflows/Stable%20Test/badge.svg)](https://github.com/Hexilee/roa/actions)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa)\n[![Rust Docs](https://docs.rs/roa-diesel/badge.svg)](https://docs.rs/roa-diesel)\n[![Crate version](https://img.shields.io/crates/v/roa-diesel.svg)](https://crates.io/crates/roa-diesel)\n[![Download](https://img.shields.io/crates/d/roa-diesel.svg)](https://crates.io/crates/roa-diesel)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\nThis crate provides diesel integration with roa framework.\n\n### AsyncPool\nA context extension to access r2d2 pool asynchronously.\n\n```rust\nuse roa::{Context, Result};\nuse diesel::sqlite::SqliteConnection;\nuse roa_diesel::Pool;\nuse roa_diesel::preload::*;\nuse diesel::r2d2::ConnectionManager;\n\n#[derive(Clone)]\nstruct State(Pool<SqliteConnection>);\n\nimpl AsRef<Pool<SqliteConnection>> for State {\n    fn as_ref(&self) -> &Pool<SqliteConnection> {\n        &self.0\n    }\n}\n\nasync fn get(ctx: Context<State>) -> Result {\n    let conn = ctx.get_conn().await?;\n    // handle conn\n    Ok(())\n}\n```\n\n### SqlQuery\nA context extension to execute diesel query asynchronously.\n\nRefer to [integration example](https://github.com/Hexilee/roa/tree/master/integration/diesel-example)\nfor more use cases.\n"
  },
  {
    "path": "roa-diesel/src/async_ext.rs",
    "content": "use diesel::connection::Connection;\nuse diesel::helper_types::Limit;\nuse diesel::query_dsl::methods::{ExecuteDsl, LimitDsl, LoadQuery};\nuse diesel::query_dsl::RunQueryDsl;\nuse diesel::result::{Error as DieselError, OptionalExtension};\nuse roa::{async_trait, Context, Result, State};\n\nuse crate::pool::{AsyncPool, Pool};\n\n/// A context extension to execute diesel dsl asynchronously.\n#[async_trait]\npub trait SqlQuery<Conn: 'static + Connection> {\n    /// Executes the given command, returning the number of rows affected.\n    ///\n    /// `execute` is usually used in conjunction with [`insert_into`](../fn.insert_into.html),\n    /// [`update`](../fn.update.html) and [`delete`](../fn.delete.html) where the number of\n    /// affected rows is often enough information.\n    ///\n    /// When asking the database to return data from a query, [`load`](#method.load) should\n    /// probably be used instead.\n    async fn execute<E>(&self, exec: E) -> Result<usize>\n    where\n        E: 'static + Send + ExecuteDsl<Conn>;\n    /// Executes the given query, returning a `Vec` with the returned rows.\n    ///\n    /// When using the query builder,\n    /// the return type can be\n    /// a tuple of the values,\n    /// or a struct which implements [`Queryable`].\n    ///\n    /// When this method is called on [`sql_query`],\n    /// the return type can only be a struct which implements [`QueryableByName`]\n    ///\n    /// For insert, update, and delete operations where only a count of affected is needed,\n    /// [`execute`] should be used instead.\n    ///\n    /// [`Queryable`]: ../deserialize/trait.Queryable.html\n    /// [`QueryableByName`]: ../deserialize/trait.QueryableByName.html\n    /// [`execute`]: fn.execute.html\n    /// [`sql_query`]: ../fn.sql_query.html\n    ///\n    async fn load_data<U, Q>(&self, query: Q) -> Result<Vec<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>;\n\n    /// Runs the command, and returns the affected row.\n    ///\n    /// `Err(NotFound)` will be returned if the query affected 0 rows. You can\n    /// call `.optional()` on the result of this if the command was optional to\n    /// get back a `Result<Option<U>>`\n    ///\n    /// When this method is called on an insert, update, or delete statement,\n    /// it will implicitly add a `RETURNING *` to the query,\n    /// unless a returning clause was already specified.\n    async fn get_result<U, Q>(&self, query: Q) -> Result<Option<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>;\n\n    /// Runs the command, returning an `Vec` with the affected rows.\n    ///\n    /// This method is an alias for [`load`], but with a name that makes more\n    /// sense for insert, update, and delete statements.\n    ///\n    /// [`load`]: #method.load\n    async fn get_results<U, Q>(&self, query: Q) -> Result<Vec<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>;\n\n    /// Attempts to load a single record.\n    ///\n    /// This method is equivalent to `.limit(1).get_result()`\n    ///\n    /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are\n    /// returned. If the query truly is optional, you can call `.optional()` on\n    /// the result of this to get a `Result<Option<U>>`.\n    ///\n    async fn first<U, Q>(&self, query: Q) -> Result<Option<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LimitDsl,\n        Limit<Q>: LoadQuery<Conn, U>;\n}\n\n#[async_trait]\nimpl<S, Conn> SqlQuery<Conn> for Context<S>\nwhere\n    S: State + AsRef<Pool<Conn>>,\n    Conn: 'static + Connection,\n{\n    #[inline]\n    async fn execute<E>(&self, exec: E) -> Result<usize>\n    where\n        E: 'static + Send + ExecuteDsl<Conn>,\n    {\n        let conn = self.get_conn().await?;\n        Ok(self\n            .exec\n            .spawn_blocking(move || ExecuteDsl::<Conn>::execute(exec, &*conn))\n            .await?)\n    }\n\n    /// Executes the given query, returning a `Vec` with the returned rows.\n    ///\n    /// When using the query builder,\n    /// the return type can be\n    /// a tuple of the values,\n    /// or a struct which implements [`Queryable`].\n    ///\n    /// When this method is called on [`sql_query`],\n    /// the return type can only be a struct which implements [`QueryableByName`]\n    ///\n    /// For insert, update, and delete operations where only a count of affected is needed,\n    /// [`execute`] should be used instead.\n    ///\n    /// [`Queryable`]: ../deserialize/trait.Queryable.html\n    /// [`QueryableByName`]: ../deserialize/trait.QueryableByName.html\n    /// [`execute`]: fn.execute.html\n    /// [`sql_query`]: ../fn.sql_query.html\n    ///\n    #[inline]\n    async fn load_data<U, Q>(&self, query: Q) -> Result<Vec<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>,\n    {\n        let conn = self.get_conn().await?;\n        match self.exec.spawn_blocking(move || query.load(&*conn)).await {\n            Ok(data) => Ok(data),\n            Err(DieselError::NotFound) => Ok(Vec::new()),\n            Err(err) => Err(err.into()),\n        }\n    }\n\n    /// Runs the command, and returns the affected row.\n    ///\n    /// `Err(NotFound)` will be returned if the query affected 0 rows. You can\n    /// call `.optional()` on the result of this if the command was optional to\n    /// get back a `Result<Option<U>>`\n    ///\n    /// When this method is called on an insert, update, or delete statement,\n    /// it will implicitly add a `RETURNING *` to the query,\n    /// unless a returning clause was already specified.\n    #[inline]\n    async fn get_result<U, Q>(&self, query: Q) -> Result<Option<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>,\n    {\n        let conn = self.get_conn().await?;\n        Ok(self\n            .exec\n            .spawn_blocking(move || query.get_result(&*conn))\n            .await\n            .optional()?)\n    }\n\n    /// Runs the command, returning an `Vec` with the affected rows.\n    ///\n    /// This method is an alias for [`load`], but with a name that makes more\n    /// sense for insert, update, and delete statements.\n    ///\n    /// [`load`]: #method.load\n    #[inline]\n    async fn get_results<U, Q>(&self, query: Q) -> Result<Vec<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LoadQuery<Conn, U>,\n    {\n        self.load_data(query).await\n    }\n\n    /// Attempts to load a single record.\n    ///\n    /// This method is equivalent to `.limit(1).get_result()`\n    ///\n    /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are\n    /// returned. If the query truly is optional, you can call `.optional()` on\n    /// the result of this to get a `Result<Option<U>>`.\n    ///\n    #[inline]\n    async fn first<U, Q>(&self, query: Q) -> Result<Option<U>>\n    where\n        U: 'static + Send,\n        Q: 'static + Send + LimitDsl,\n        Limit<Q>: LoadQuery<Conn, U>,\n    {\n        let conn = self.get_conn().await?;\n        Ok(self\n            .exec\n            .spawn_blocking(move || query.limit(1).get_result(&*conn))\n            .await\n            .optional()?)\n    }\n}\n"
  },
  {
    "path": "roa-diesel/src/lib.rs",
    "content": "#![cfg_attr(feature = \"docs\", doc = include_str!(\"../README.md\"))]\n#![cfg_attr(feature = \"docs\", warn(missing_docs))]\n\nmod async_ext;\nmod pool;\n\n#[doc(inline)]\npub use diesel::r2d2::ConnectionManager;\n#[doc(inline)]\npub use pool::{builder, make_pool, Pool, WrapConnection};\n\n/// preload ext traits.\npub mod preload {\n    #[doc(inline)]\n    pub use crate::async_ext::SqlQuery;\n    #[doc(inline)]\n    pub use crate::pool::AsyncPool;\n}\n"
  },
  {
    "path": "roa-diesel/src/pool.rs",
    "content": "use std::time::Duration;\n\nuse diesel::r2d2::{ConnectionManager, PoolError};\nuse diesel::Connection;\nuse r2d2::{Builder, PooledConnection};\nuse roa::{async_trait, Context, State, Status};\n\n/// An alias for r2d2::Pool<diesel::r2d2::ConnectionManager<Conn>>.\npub type Pool<Conn> = r2d2::Pool<ConnectionManager<Conn>>;\n\n/// An alias for r2d2::PooledConnection<diesel::r2d2::ConnectionManager<Conn>>.\npub type WrapConnection<Conn> = PooledConnection<ConnectionManager<Conn>>;\n\n/// Create a connection pool.\n///\n/// ### Example\n///\n/// ```\n/// use roa_diesel::{make_pool, Pool};\n/// use diesel::sqlite::SqliteConnection;\n/// use std::error::Error;\n///\n/// # fn main() -> Result<(), Box<dyn Error>> {\n/// let pool: Pool<SqliteConnection> = make_pool(\":memory:\")?;\n/// Ok(())\n/// # }\n/// ```\npub fn make_pool<Conn>(url: impl Into<String>) -> Result<Pool<Conn>, PoolError>\nwhere\n    Conn: Connection + 'static,\n{\n    r2d2::Pool::new(ConnectionManager::<Conn>::new(url))\n}\n\n/// Create a pool builder.\npub fn builder<Conn>() -> Builder<ConnectionManager<Conn>>\nwhere\n    Conn: Connection + 'static,\n{\n    r2d2::Pool::builder()\n}\n\n/// A context extension to access r2d2 pool asynchronously.\n#[async_trait]\npub trait AsyncPool<Conn>\nwhere\n    Conn: Connection + 'static,\n{\n    /// Retrieves a connection from the pool.\n    ///\n    /// Waits for at most the configured connection timeout before returning an\n    /// error.\n    ///\n    /// ```\n    /// use roa::{Context, Result};\n    /// use diesel::sqlite::SqliteConnection;\n    /// use roa_diesel::preload::AsyncPool;\n    /// use roa_diesel::Pool;\n    /// use diesel::r2d2::ConnectionManager;\n    ///\n    /// #[derive(Clone)]\n    /// struct State(Pool<SqliteConnection>);\n    ///\n    /// impl AsRef<Pool<SqliteConnection>> for State {\n    ///     fn as_ref(&self) -> &Pool<SqliteConnection> {\n    ///         &self.0\n    ///     }\n    /// }\n    ///\n    /// async fn get(ctx: Context<State>) -> Result {\n    ///     let conn = ctx.get_conn().await?;\n    ///     // handle conn\n    ///     Ok(())\n    /// }\n    /// ```\n    async fn get_conn(&self) -> Result<WrapConnection<Conn>, Status>;\n\n    /// Retrieves a connection from the pool, waiting for at most `timeout`\n    ///\n    /// The given timeout will be used instead of the configured connection\n    /// timeout.\n    async fn get_timeout(&self, timeout: Duration) -> Result<WrapConnection<Conn>, Status>;\n\n    /// Returns information about the current state of the pool.\n    async fn pool_state(&self) -> r2d2::State;\n}\n\n#[async_trait]\nimpl<S, Conn> AsyncPool<Conn> for Context<S>\nwhere\n    S: State + AsRef<Pool<Conn>>,\n    Conn: Connection + 'static,\n{\n    #[inline]\n    async fn get_conn(&self) -> Result<WrapConnection<Conn>, Status> {\n        let pool = self.as_ref().clone();\n        Ok(self.exec.spawn_blocking(move || pool.get()).await?)\n    }\n\n    #[inline]\n    async fn get_timeout(&self, timeout: Duration) -> Result<WrapConnection<Conn>, Status> {\n        let pool = self.as_ref().clone();\n        Ok(self\n            .exec\n            .spawn_blocking(move || pool.get_timeout(timeout))\n            .await?)\n    }\n\n    #[inline]\n    async fn pool_state(&self) -> r2d2::State {\n        let pool = self.as_ref().clone();\n        self.exec.spawn_blocking(move || pool.state()).await\n    }\n}\n"
  },
  {
    "path": "roa-juniper/Cargo.toml",
    "content": "[package]\nname = \"roa-juniper\"\nversion = \"0.6.0\"\nauthors = [\"Hexilee <i@hexilee.me>\"]\nedition = \"2018\"\nreadme = \"./README.md\"\nrepository = \"https://github.com/Hexilee/roa\"\ndocumentation = \"https://docs.rs/roa-juniper\"\nhomepage = \"https://github.com/Hexilee/roa/wiki\"\ndescription = \"juniper integration for roa\"\nkeywords = [\"http\", \"web\", \"framework\", \"async\"]\ncategories = [\"network-programming\", \"asynchronous\",\n              \"web-programming::http-server\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nroa = { path = \"../roa\", version = \"0.6.0\", default-features = false, features = [\"json\"] }\nfutures = \"0.3\"\njuniper = { version = \"0.15\", default-features = false }\n"
  },
  {
    "path": "roa-juniper/README.md",
    "content": "[![Stable Test](https://github.com/Hexilee/roa/workflows/Stable%20Test/badge.svg)](https://github.com/Hexilee/roa/actions)\n[![codecov](https://codecov.io/gh/Hexilee/roa/branch/master/graph/badge.svg)](https://codecov.io/gh/Hexilee/roa)\n[![Rust Docs](https://docs.rs/roa-juniper/badge.svg)](https://docs.rs/roa-juniper)\n[![Crate version](https://img.shields.io/crates/v/roa-juniper.svg)](https://crates.io/crates/roa-juniper)\n[![Download](https://img.shields.io/crates/d/roa-juniper.svg)](https://crates.io/crates/roa-juniper)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hexilee/roa/blob/master/LICENSE)\n\n## Roa-juniper\n\nThis crate provides a juniper context and a graphql endpoint.\n\n### Example\n\nRefer to [integration-example](https://github.com/Hexilee/roa/tree/master/integration/juniper-example)."
  },
  {
    "path": "roa-juniper/src/lib.rs",
    "content": "//! This crate provides a juniper context and a graphql endpoint.\n//!\n//! ### Example\n//!\n//! Refer to [integration-example](https://github.com/Hexilee/roa/tree/master/integration/juniper-example)\n\n#![warn(missing_docs)]\n\nuse std::ops::{Deref, DerefMut};\n\nuse juniper::http::GraphQLRequest;\nuse juniper::{GraphQLType, GraphQLTypeAsync, RootNode, ScalarValue};\nuse roa::preload::*;\nuse roa::{async_trait, Context, Endpoint, Result, State};\n\n/// A wrapper for `roa_core::SyncContext`.\n/// As an implementation of `juniper::Context`.\npub struct JuniperContext<S>(Context<S>);\n\nimpl<S: State> juniper::Context for JuniperContext<S> {}\n\nimpl<S> Deref for JuniperContext<S> {\n    type Target = Context<S>;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\nimpl<S> DerefMut for JuniperContext<S> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// An endpoint.\npub struct GraphQL<QueryT, MutationT, SubscriptionT, Sca>(\n    pub RootNode<'static, QueryT, MutationT, SubscriptionT, Sca>,\n)\nwhere\n    QueryT: GraphQLType<Sca>,\n    MutationT: GraphQLType<Sca>,\n    SubscriptionT: GraphQLType<Sca>,\n    Sca: ScalarValue;\n\n#[async_trait(?Send)]\nimpl<'a, S, QueryT, MutationT, SubscriptionT, Sca> Endpoint<'a, S>\n    for GraphQL<QueryT, MutationT, SubscriptionT, Sca>\nwhere\n    S: State,\n    QueryT: GraphQLTypeAsync<Sca, Context = JuniperContext<S>> + Send + Sync + 'static,\n    QueryT::TypeInfo: Send + Sync,\n    MutationT: GraphQLTypeAsync<Sca, Context = QueryT::Context> + Send + Sync + 'static,\n    MutationT::TypeInfo: Send + Sync,\n    SubscriptionT: GraphQLType<Sca, Context = QueryT::Context> + Send + Sync + 'static,\n    SubscriptionT::TypeInfo: Send + Sync,\n    Sca: ScalarValue + Send + Sync + 'static,\n{\n    #[inline]\n    async fn call(&'a self, ctx: &'a mut Context<S>) -> Result {\n        let request: GraphQLRequest<Sca> = ctx.read_json().await?;\n        let juniper_ctx = JuniperContext(ctx.clone());\n        let resp = request.execute(&self.0, &juniper_ctx).await;\n        ctx.write_json(&resp)\n    }\n}\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "group_imports = \"StdExternalCrate\"\nimports_granularity = \"Module\"\nreorder_imports = true\nunstable_features = true\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#[cfg(doctest)]\ndoc_comment::doctest!(\"../README.md\");\n"
  },
  {
    "path": "templates/directory.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>{{title}}</title>\n    <link href=\"/assets/css/table.css\" rel=\"stylesheet\" type=\"text/css\">\n</head>\n<body>\n<h1>{{root}}</h1>\n<hr>\n<table>\n    <thead>\n    <tr>\n        <th>Name</th>\n        <th>Size</th>\n        <th>Modified</th>\n    </tr>\n    </thead>\n    <tbody>\n    {% for dir in dirs %}\n    <tr>\n        <td><a href={{dir.link}}>{{dir.name}}</a></td>\n        <td>-</td>\n        <td>{{dir.modified}}</td>\n    </tr>\n    {% endfor %}\n    {% for file in files %}\n    <tr>\n        <td><a href={{file.link}}>{{file.name}}</a></td>\n        <td>{{file.size}}</td>\n        <td>{{file.modified}}</td>\n    </tr>\n    {% endfor %}\n    </tbody>\n</table>\n</body>\n</html>"
  },
  {
    "path": "tests/logger.rs",
    "content": "use std::sync::RwLock;\n\nuse log::{Level, LevelFilter, Metadata, Record};\nuse once_cell::sync::Lazy;\nuse roa::http::StatusCode;\nuse roa::logger::logger;\nuse roa::preload::*;\nuse roa::{throw, App, Context};\nuse tokio::fs::File;\nuse tokio::task::spawn;\n\nstruct TestLogger {\n    records: RwLock<Vec<(String, String)>>,\n}\nimpl log::Log for TestLogger {\n    fn enabled(&self, metadata: &Metadata) -> bool {\n        metadata.level() <= Level::Info\n    }\n    fn log(&self, record: &Record) {\n        self.records\n            .write()\n            .unwrap()\n            .push((record.level().to_string(), record.args().to_string()))\n    }\n    fn flush(&self) {}\n}\n\nstatic LOGGER: Lazy<TestLogger> = Lazy::new(|| TestLogger {\n    records: RwLock::new(Vec::new()),\n});\n\nfn init() -> anyhow::Result<()> {\n    log::set_logger(&*LOGGER)\n        .map(|()| log::set_max_level(LevelFilter::Info))\n        .map_err(|err| anyhow::anyhow!(\"fail to init logger: {}\", err))\n}\n\n#[tokio::test]\nasync fn log() -> anyhow::Result<()> {\n    init()?;\n    async fn bytes_info(ctx: &mut Context) -> roa::Result {\n        ctx.resp.write(\"Hello, World.\");\n        Ok(())\n    }\n    // bytes info\n    let (addr, server) = App::new().gate(logger).end(bytes_info).run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    assert_eq!(\"Hello, World.\", resp.text().await?);\n    let records = LOGGER.records.read().unwrap().clone();\n    assert_eq!(2, records.len());\n    assert_eq!(\"INFO\", records[0].0);\n    assert_eq!(\"--> GET /\", records[0].1.trim_end());\n    assert_eq!(\"INFO\", records[1].0);\n    assert!(records[1].1.starts_with(\"<-- GET /\"));\n    assert!(records[1].1.contains(\"13 B\"));\n    assert!(records[1].1.trim_end().ends_with(\"200 OK\"));\n\n    // error\n    async fn err(_ctx: &mut Context) -> roa::Result {\n        throw!(StatusCode::BAD_REQUEST, \"Hello, World!\")\n    }\n    let (addr, server) = App::new().gate(logger).end(err).run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    assert_eq!(StatusCode::BAD_REQUEST, resp.status());\n    assert_eq!(\"Hello, World!\", resp.text().await?);\n    let records = LOGGER.records.read().unwrap().clone();\n    assert_eq!(4, records.len());\n    assert_eq!(\"INFO\", records[2].0);\n    assert_eq!(\"--> GET /\", records[2].1.trim_end());\n    assert_eq!(\"ERROR\", records[3].0);\n    assert!(records[3].1.starts_with(\"<-- GET /\"));\n    assert!(records[3].1.contains(&StatusCode::BAD_REQUEST.to_string()));\n    assert!(records[3].1.trim_end().ends_with(\"Hello, World!\"));\n\n    // stream info\n    async fn stream_info(ctx: &mut Context) -> roa::Result {\n        ctx.resp\n            .write_reader(File::open(\"assets/welcome.html\").await?);\n        Ok(())\n    }\n    // bytes info\n    let (addr, server) = App::new().gate(logger).end(stream_info).run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    assert_eq!(236, resp.text().await?.len());\n    let records = LOGGER.records.read().unwrap().clone();\n    assert_eq!(6, records.len());\n    assert_eq!(\"INFO\", records[4].0);\n    assert_eq!(\"--> GET /\", records[4].1.trim_end());\n    assert_eq!(\"INFO\", records[5].0);\n    assert!(records[5].1.starts_with(\"<-- GET /\"));\n    assert!(records[5].1.contains(\"236 B\"));\n    assert!(records[5].1.trim_end().ends_with(\"200 OK\"));\n    Ok(())\n}\n"
  },
  {
    "path": "tests/restful.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse http::StatusCode;\nuse multimap::MultiMap;\nuse roa::preload::*;\nuse roa::query::query_parser;\nuse roa::router::{get, post, Router};\nuse roa::{throw, App, Context};\nuse serde::{Deserialize, Serialize};\nuse serde_json::json;\nuse slab::Slab;\nuse tokio::sync::RwLock;\nuse tokio::task::spawn;\n\n#[derive(Debug, Clone, Deserialize, Serialize, Hash, Eq, PartialEq)]\nstruct User {\n    name: String,\n    age: u8,\n    favorite_fruit: String,\n}\n\nstruct DB {\n    main_table: Slab<User>,\n    name_index: MultiMap<String, usize>,\n}\n\nimpl DB {\n    fn new() -> Self {\n        Self {\n            main_table: Slab::new(),\n            name_index: MultiMap::new(),\n        }\n    }\n\n    fn add(&mut self, user: User) -> usize {\n        let name = user.name.clone();\n        let id = self.main_table.insert(user);\n        self.name_index.insert(name, id);\n        id\n    }\n\n    fn delete_index(&mut self, name: &str, id: usize) {\n        if let Some(id_set) = self.name_index.get_vec_mut(name) {\n            let uids = id_set.clone();\n            for (index, uid) in uids.into_iter().enumerate() {\n                if uid == id {\n                    id_set.remove(index);\n                }\n            }\n        }\n    }\n\n    fn delete(&mut self, id: usize) -> Option<User> {\n        if !self.main_table.contains(id) {\n            None\n        } else {\n            let user = self.main_table.remove(id);\n            self.delete_index(&user.name, id);\n            Some(user)\n        }\n    }\n\n    fn get(&self, id: usize) -> Option<&User> {\n        self.main_table.get(id)\n    }\n\n    fn get_by_name(&self, name: &str) -> Vec<(usize, &User)> {\n        match self.name_index.get_vec(name) {\n            None => Vec::new(),\n            Some(ids) => ids\n                .iter()\n                .filter_map(|id| self.get(*id).map(|user| (*id, user)))\n                .collect(),\n        }\n    }\n\n    fn update(&mut self, id: usize, new_user: &mut User) -> bool {\n        let new_name = new_user.name.clone();\n        let swapped = self\n            .main_table\n            .get_mut(id)\n            .map(|user| std::mem::swap(user, new_user))\n            .is_some();\n        if swapped {\n            self.delete_index(&new_user.name, id);\n            self.name_index.insert(new_name, id);\n        }\n        swapped\n    }\n}\n\n#[derive(Clone)]\nstruct State(Arc<RwLock<DB>>);\n\nimpl State {\n    fn new(db: DB) -> Self {\n        Self(Arc::new(RwLock::new(db)))\n    }\n\n    async fn add(&mut self, user: User) -> usize {\n        self.0.write().await.add(user)\n    }\n\n    async fn delete(&mut self, id: usize) -> Option<User> {\n        self.0.write().await.delete(id)\n    }\n\n    async fn get_user(&self, id: usize) -> Option<User> {\n        self.0.read().await.get(id).cloned()\n    }\n\n    async fn get_by_name(&self, name: &str) -> Vec<(usize, User)> {\n        self.0\n            .read()\n            .await\n            .get_by_name(name)\n            .into_iter()\n            .map(|(id, user)| (id, user.clone()))\n            .collect()\n    }\n\n    async fn get_all(&self) -> Vec<(usize, User)> {\n        self.0\n            .read()\n            .await\n            .main_table\n            .iter()\n            .map(|(id, user)| (id, user.clone()))\n            .collect()\n    }\n\n    async fn update(&mut self, id: usize, new_user: &mut User) -> bool {\n        self.0.write().await.update(id, new_user)\n    }\n}\n\nasync fn create_user(ctx: &mut Context<State>) -> roa::Result {\n    let user = ctx.read_json().await?;\n    let id = ctx.add(user).await;\n    ctx.resp.status = StatusCode::CREATED;\n    ctx.write_json(&json!({ \"id\": id }))\n}\n\nasync fn query_user(ctx: &mut Context<State>) -> roa::Result {\n    let id = ctx.must_param(\"id\")?.parse()?;\n    match ctx.get_user(id).await {\n        Some(user) => ctx.write_json(&user),\n        None => throw!(StatusCode::NOT_FOUND, format!(\"id({}) not found\", id)),\n    }\n}\n\nasync fn update_user(ctx: &mut Context<State>) -> roa::Result {\n    let id = ctx.must_param(\"id\")?.parse()?;\n    let mut user = ctx.read_json().await?;\n    if ctx.update(id, &mut user).await {\n        ctx.write_json(&user)\n    } else {\n        throw!(StatusCode::NOT_FOUND, format!(\"id({}) not found\", id))\n    }\n}\n\nasync fn delete_user(ctx: &mut Context<State>) -> roa::Result {\n    let id = ctx.must_param(\"id\")?.parse()?;\n    match ctx.delete(id).await {\n        Some(user) => ctx.write_json(&user),\n        None => throw!(StatusCode::NOT_FOUND, format!(\"id({}) not found\", id)),\n    }\n}\n\nfn crud_router() -> Router<State> {\n    Router::new()\n        .on(\"/\", post(create_user))\n        .on(\"/:id\", get(query_user).put(update_user).delete(delete_user))\n}\n\n#[tokio::test]\nasync fn restful_crud() -> Result<(), Box<dyn std::error::Error>> {\n    let app = App::state(State::new(DB::new())).end(crud_router().routes(\"/user\")?);\n    let (addr, server) = app.run()?;\n    spawn(server);\n    // first get, 404 Not Found\n    let resp = reqwest::get(&format!(\"http://{}/user/0\", addr)).await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n\n    let user = User {\n        name: \"Hexilee\".to_string(),\n        age: 20,\n        favorite_fruit: \"Apple\".to_string(),\n    };\n\n    // post\n    let client = reqwest::Client::new();\n    let resp = client\n        .post(&format!(\"http://{}/user\", addr))\n        .json(&user)\n        .send()\n        .await?;\n    assert_eq!(StatusCode::CREATED, resp.status());\n    let data: HashMap<String, usize> = serde_json::from_str(&resp.text().await?)?;\n    assert_eq!(0, data[\"id\"]);\n\n    // get\n    let resp = reqwest::get(&format!(\"http://{}/user/0\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    assert_eq!(&user, &resp.json().await?);\n\n    // put\n    let another = User {\n        name: \"Bob\".to_string(),\n        age: 120,\n        favorite_fruit: \"Lemon\".to_string(),\n    };\n\n    let resp = client\n        .put(&format!(\"http://{}/user/0\", addr))\n        .json(&another)\n        .send()\n        .await?;\n    assert_eq!(StatusCode::OK, resp.status());\n\n    // return first user\n    assert_eq!(&user, &resp.json().await?);\n\n    // updated, get new user\n    let resp = reqwest::get(&format!(\"http://{}/user/0\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    assert_eq!(&another, &resp.json().await?);\n\n    // delete, get deleted user\n    let resp = client\n        .delete(&format!(\"http://{}/user/0\", addr))\n        .send()\n        .await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    assert_eq!(&another, &resp.json().await?);\n\n    // delete again, 404 Not Found\n    let resp = client\n        .delete(&format!(\"http://{}/user/0\", addr))\n        .send()\n        .await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n\n    // put again, 404 Not Found\n    let resp = client\n        .put(&format!(\"http://{}/user/0\", addr))\n        .json(&another)\n        .send()\n        .await?;\n    assert_eq!(StatusCode::NOT_FOUND, resp.status());\n    Ok(())\n}\n\nasync fn create_batch(ctx: &mut Context<State>) -> roa::Result {\n    let users: Vec<User> = ctx.read_json().await?;\n    let mut ids = Vec::new();\n    for user in users {\n        ids.push(ctx.add(user).await)\n    }\n    ctx.resp.status = StatusCode::CREATED;\n    ctx.write_json(&ids)\n}\n\nasync fn query_batch(ctx: &mut Context<State>) -> roa::Result {\n    let users = match ctx.query(\"name\") {\n        Some(name) => ctx.get_by_name(&name).await,\n        None => ctx.get_all().await,\n    };\n    ctx.write_json(&users)\n}\n\nfn batch_router() -> Router<State> {\n    Router::new().on(\"/user\", get(query_batch).post(create_batch))\n}\n\n#[tokio::test]\nasync fn batch() -> Result<(), Box<dyn std::error::Error>> {\n    let app = App::state(State::new(DB::new()))\n        .gate(query_parser)\n        .end(batch_router().routes(\"/\")?);\n    let (addr, server) = app.run()?;\n    spawn(server);\n    // first get, list empty\n    let resp = reqwest::get(&format!(\"http://{}/user\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let data: Vec<(usize, User)> = resp.json().await?;\n    assert!(data.is_empty());\n\n    // post batch\n    let client = reqwest::Client::new();\n    let users = vec![\n        User {\n            name: \"Hexilee\".to_string(),\n            age: 20,\n            favorite_fruit: \"Apple\".to_string(),\n        },\n        User {\n            name: \"Bob\".to_string(),\n            age: 120,\n            favorite_fruit: \"Lemon\".to_string(),\n        },\n        User {\n            name: \"Hexilee\".to_string(),\n            age: 40,\n            favorite_fruit: \"Orange\".to_string(),\n        },\n    ];\n    let resp = client\n        .post(&format!(\"http://{}/user\", addr))\n        .json(&users)\n        .send()\n        .await?;\n    assert_eq!(StatusCode::CREATED, resp.status());\n    let ids: Vec<usize> = resp.json().await?;\n    assert_eq!(vec![0, 1, 2], ids);\n\n    // get all\n    let resp = reqwest::get(&format!(\"http://{}/user\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let data: Vec<(usize, User)> = resp.json().await?;\n    assert_eq!(3, data.len());\n    for (index, user) in data.iter() {\n        assert_eq!(&users[*index], user);\n    }\n\n    // get by name\n    let resp = reqwest::get(&format!(\"http://{}/user?name=Alice\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let data: Vec<(usize, User)> = resp.json().await?;\n    assert!(data.is_empty());\n\n    let resp = reqwest::get(&format!(\"http://{}/user?name=Hexilee\", addr)).await?;\n    assert_eq!(StatusCode::OK, resp.status());\n    let data: Vec<(usize, User)> = resp.json().await?;\n    assert_eq!(2, data.len());\n    assert_eq!(0, data[0].0);\n    assert_eq!(&users[0], &data[0].1);\n    assert_eq!(2, data[1].0);\n    assert_eq!(&users[2], &data[1].1);\n    Ok(())\n}\n"
  },
  {
    "path": "tests/serve-file.rs",
    "content": "use http::header::ACCEPT_ENCODING;\nuse roa::body::DispositionType;\nuse roa::compress::Compress;\nuse roa::preload::*;\nuse roa::router::{get, Router};\nuse roa::{App, Context};\nuse tokio::fs::read_to_string;\nuse tokio::task::spawn;\n\n#[tokio::test]\nasync fn serve_static_file() -> Result<(), Box<dyn std::error::Error>> {\n    async fn test(ctx: &mut Context) -> roa::Result {\n        ctx.write_file(\"assets/author.txt\", DispositionType::Inline)\n            .await\n    }\n    let app = App::new().end(get(test));\n    let (addr, server) = app.run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}\", addr)).await?;\n    assert_eq!(\"Hexilee\", resp.text().await?);\n    Ok(())\n}\n\n#[tokio::test]\nasync fn serve_router_variable() -> Result<(), Box<dyn std::error::Error>> {\n    async fn test(ctx: &mut Context) -> roa::Result {\n        let filename = ctx.must_param(\"filename\")?;\n        ctx.write_file(format!(\"assets/{}\", &*filename), DispositionType::Inline)\n            .await\n    }\n    let router = Router::new().on(\"/:filename\", get(test));\n    let app = App::new().end(router.routes(\"/\")?);\n    let (addr, server) = app.run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}/author.txt\", addr)).await?;\n    assert_eq!(\"Hexilee\", resp.text().await?);\n    Ok(())\n}\n\n#[tokio::test]\nasync fn serve_router_wildcard() -> Result<(), Box<dyn std::error::Error>> {\n    async fn test(ctx: &mut Context) -> roa::Result {\n        let path = ctx.must_param(\"path\")?;\n        ctx.write_file(format!(\"./{}\", &*path), DispositionType::Inline)\n            .await\n    }\n    let router = Router::new().on(\"/*{path}\", get(test));\n    let app = App::new().end(router.routes(\"/\")?);\n    let (addr, server) = app.run()?;\n    spawn(server);\n    let resp = reqwest::get(&format!(\"http://{}/assets/author.txt\", addr)).await?;\n    assert_eq!(\"Hexilee\", resp.text().await?);\n    Ok(())\n}\n\n#[tokio::test]\nasync fn serve_gzip() -> Result<(), Box<dyn std::error::Error>> {\n    async fn test(ctx: &mut Context) -> roa::Result {\n        ctx.write_file(\"assets/welcome.html\", DispositionType::Inline)\n            .await\n    }\n    let app = App::new().gate(Compress::default()).end(get(test));\n    let (addr, server) = app.run()?;\n    spawn(server);\n    let client = reqwest::Client::builder().gzip(true).build()?;\n    let resp = client\n        .get(&format!(\"http://{}\", addr))\n        .header(ACCEPT_ENCODING, \"gzip\")\n        .send()\n        .await?;\n\n    assert_eq!(\n        read_to_string(\"assets/welcome.html\").await?,\n        resp.text().await?\n    );\n    Ok(())\n}\n"
  }
]