diff --git a/.drone.star b/.drone.star index cae42f51..81d36457 100644 --- a/.drone.star +++ b/.drone.star @@ -14,8 +14,8 @@ def main(ctx): return generate( # Compilers [ - 'gcc >=5.0', - 'clang >=3.9', + 'gcc >=10.0', + 'clang >=10.0', 'msvc >=14.1', 'arm64-gcc latest', 's390x-gcc latest', @@ -27,7 +27,7 @@ def main(ctx): 'x86-msvc latest' ], # Standards - '>=11', + '>=20', packages=['zlib1g', 'zlib1g-dev', 'libbrotli-dev']) # from https://github.com/cppalliance/ci-automation diff --git a/.drone/drone.bat b/.drone/drone.bat index 0db571c2..b310d140 100644 --- a/.drone/drone.bat +++ b/.drone/drone.bat @@ -47,7 +47,7 @@ pushd !BOOST_ROOT!\libs git clone https://github.com/cppalliance/capy -b !BOOST_BRANCH! --depth 1 popd pushd !BOOST_ROOT!\libs -git clone https://github.com/cppalliance/http_proto -b !BOOST_BRANCH! --depth 1 +git clone https://github.com/cppalliance/http -b !BOOST_BRANCH! --depth 1 popd echo '==================================> COMPILE' diff --git a/.drone/drone.sh b/.drone/drone.sh index 23732c17..2a249e5b 100644 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -47,9 +47,9 @@ common_install () { popd fi - if [ ! -d "$BOOST_ROOT/libs/http_proto" ]; then + if [ ! -d "$BOOST_ROOT/libs/http" ]; then pushd $BOOST_ROOT/libs - git clone https://github.com/cppalliance/http_proto -b $BOOST_BRANCH --depth 1 + git clone https://github.com/cppalliance/http -b $BOOST_BRANCH --depth 1 popd fi } @@ -139,9 +139,9 @@ if [ ! -d "$BOOST_ROOT/libs/capy" ]; then popd fi -if [ ! -d "$BOOST_ROOT/libs/http_proto" ]; then +if [ ! -d "$BOOST_ROOT/libs/http" ]; then pushd $BOOST_ROOT/libs - git clone https://github.com/cppalliance/http_proto -b $BOOST_BRANCH --depth 1 + git clone https://github.com/cppalliance/http -b $BOOST_BRANCH --depth 1 popd fi diff --git a/.github/workflows/ci-failure-auto-fix.yml b/.github/workflows/ci-failure-auto-fix.yml deleted file mode 100644 index ff08a7a2..00000000 --- a/.github/workflows/ci-failure-auto-fix.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Auto Fix CI Failures - -on: - workflow_run: - workflows: ["CI"] - types: - - completed - -permissions: - contents: write - pull-requests: write - actions: read - issues: write - id-token: write # Required for OIDC token exchange - -jobs: - auto-fix: - if: | - github.event.workflow_run.conclusion == 'failure' && - github.event.workflow_run.pull_requests[0] && - !startsWith(github.event.workflow_run.head_branch, 'claude-auto-fix-ci-') - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v5 - with: - ref: ${{ github.event.workflow_run.head_branch }} - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup git identity - run: | - git config --global user.email "claude[bot]@users.noreply.github.com" - git config --global user.name "claude[bot]" - - - name: Create fix branch - id: branch - run: | - BRANCH_NAME="claude-auto-fix-ci-${{ github.event.workflow_run.head_branch }}-${{ github.run_id }}" - git checkout -b "$BRANCH_NAME" - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT - - - name: Get CI failure details - id: failure_details - uses: actions/github-script@v7 - with: - script: | - const run = await github.rest.actions.getWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }} - }); - - const jobs = await github.rest.actions.listJobsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }} - }); - - const failedJobs = jobs.data.jobs.filter(job => job.conclusion === 'failure'); - - let errorLogs = []; - for (const job of failedJobs) { - const logs = await github.rest.actions.downloadJobLogsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - job_id: job.id - }); - errorLogs.push({ - jobName: job.name, - logs: logs.data - }); - } - - return { - runUrl: run.data.html_url, - failedJobs: failedJobs.map(j => j.name), - errorLogs: errorLogs - }; - - - name: Fix CI failures with Claude - id: claude - uses: anthropics/claude-code-action@v1 - with: - prompt: | - /fix-ci - Failed CI Run: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }} - Failed Jobs: ${{ join(fromJSON(steps.failure_details.outputs.result).failedJobs, ', ') }} - PR Number: ${{ github.event.workflow_run.pull_requests[0].number }} - Branch Name: ${{ steps.branch.outputs.branch_name }} - Base Branch: ${{ github.event.workflow_run.head_branch }} - Repository: ${{ github.repository }} - - Error logs: - ${{ toJSON(fromJSON(steps.failure_details.outputs.result).errorLogs) }} - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - claude_args: "--allowedTools 'Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(git:*),Bash(bun:*),Bash(npm:*),Bash(npx:*),Bash(gh:*)'" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4f12931..1110c661 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,33 +62,33 @@ jobs: - compiler: "msvc" version: "14.42" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" runs-on: "windows-2022" b2-toolset: "msvc-14.4" generator: "Visual Studio 17 2022" is-latest: true - name: "MSVC 14.42: C++17-20" + name: "MSVC 14.42: C++20" shared: false build-type: "Release" build-cmake: true - compiler: "msvc" version: "14.34" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" runs-on: "windows-2022" b2-toolset: "msvc-14.3" generator: "Visual Studio 17 2022" is-latest: true - name: "MSVC 14.34: C++17-20" + name: "MSVC 14.34: C++20" shared: true build-type: "Release" build-cmake: true - compiler: "msvc" version: "14.34" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" runs-on: "windows-2022" b2-toolset: "msvc-14.3" @@ -101,13 +101,13 @@ jobs: - compiler: "msvc" version: "14.34" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" runs-on: "windows-2022" b2-toolset: "msvc-14.3" generator: "Visual Studio 17 2022" is-latest: true - name: "MSVC 14.34: C++17-20" + name: "MSVC 14.34: C++20" shared: true build-type: "Release" build-cmake: true @@ -220,7 +220,7 @@ jobs: - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -228,14 +228,14 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20" + name: "GCC 15: C++20" shared: false build-type: "Release" build-cmake: true - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -243,14 +243,14 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (no zlib)" + name: "GCC 15: C++20 (no zlib)" shared: true build-type: "Release" build-cmake: true - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -258,7 +258,7 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (x86)" + name: "GCC 15: C++20 (x86)" shared: false x86: true build-type: "Release" @@ -266,7 +266,7 @@ jobs: - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -274,14 +274,14 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20" + name: "GCC 15: C++20" shared: true build-type: "Release" build-cmake: true - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -289,7 +289,7 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (x86)" + name: "GCC 15: C++20 (x86)" shared: false x86: true build-type: "Release" @@ -298,7 +298,7 @@ jobs: - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -306,14 +306,14 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (asan)" + name: "GCC 15: C++20 (asan)" shared: true asan: true build-type: "RelWithDebInfo" - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -321,7 +321,7 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (asan, x86)" + name: "GCC 15: C++20 (asan, x86)" shared: false asan: true x86: true @@ -330,7 +330,7 @@ jobs: - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -338,14 +338,14 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (ubsan)" + name: "GCC 15: C++20 (ubsan)" shared: false ubsan: true build-type: "RelWithDebInfo" - compiler: "gcc" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-15" cc: "gcc-15" @@ -353,7 +353,7 @@ jobs: container: "ubuntu:25.04" b2-toolset: "gcc" is-latest: true - name: "GCC 15: C++17-20 (ubsan, x86)" + name: "GCC 15: C++20 (ubsan, x86)" shared: false ubsan: true x86: true @@ -362,38 +362,38 @@ jobs: - compiler: "gcc" version: "14" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-14" cc: "gcc-14" runs-on: "ubuntu-24.04" b2-toolset: "gcc" - name: "GCC 14: C++17-20" + name: "GCC 14: C++20" shared: true build-type: "Release" - compiler: "gcc" version: "13" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-13" cc: "gcc-13" runs-on: "ubuntu-24.04" b2-toolset: "gcc" - name: "GCC 13: C++17-20" + name: "GCC 13: C++20" shared: true build-type: "Release" - compiler: "gcc" version: "13" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-13" cc: "gcc-13" runs-on: "ubuntu-24.04" b2-toolset: "gcc" is-latest: true - name: "GCC 13: C++17-20 (coverage)" + name: "GCC 13: C++20 (coverage)" shared: false coverage: true build-type: "Debug" @@ -403,106 +403,27 @@ jobs: - compiler: "gcc" version: "12" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-12" cc: "gcc-12" runs-on: "ubuntu-latest" container: "ubuntu:22.04" b2-toolset: "gcc" - name: "GCC 12: C++17-20" + name: "GCC 12: C++20" shared: true build-type: "Release" - compiler: "gcc" version: "11" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "g++-11" cc: "gcc-11" runs-on: "ubuntu-latest" container: "ubuntu:22.04" b2-toolset: "gcc" - name: "GCC 11: C++17-20" - shared: false - build-type: "Release" - - - compiler: "gcc" - version: "10" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "g++-10" - cc: "gcc-10" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "gcc" - name: "GCC 10: C++14-17" - shared: true - build-type: "Release" - - - compiler: "gcc" - version: "9" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "g++-9" - cc: "gcc-9" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "gcc" - name: "GCC 9: C++14-17" - shared: false - build-type: "Release" - - - compiler: "gcc" - version: "8" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "g++-8" - cc: "gcc-8" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "gcc" - name: "GCC 8: C++14-17" - shared: true - build-type: "Release" - - - compiler: "gcc" - version: "7" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "g++-7" - cc: "gcc-7" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "gcc" - name: "GCC 7: C++14-17" - shared: false - build-type: "Release" - - - compiler: "gcc" - version: "6" - cxxstd: "11,14" - latest-cxxstd: "14" - cxx: "g++-6" - cc: "gcc-6" - runs-on: "ubuntu-latest" - container: "ubuntu:18.04" - b2-toolset: "gcc" - name: "GCC 6: C++11-14" - shared: true - build-type: "Release" - - - compiler: "gcc" - version: "5" - cxxstd: "11" - latest-cxxstd: "11" - cxx: "g++-5" - cc: "gcc-5" - runs-on: "ubuntu-latest" - container: "ubuntu:18.04" - b2-toolset: "gcc" - is-earliest: true - name: "GCC 5: C++11" + name: "GCC 11: C++20" shared: false build-type: "Release" @@ -557,7 +478,7 @@ jobs: - compiler: "clang" version: "20" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-20" cc: "clang-20" @@ -565,14 +486,14 @@ jobs: container: "ubuntu:24.04" b2-toolset: "clang" is-latest: true - name: "Clang 20: C++17-20 (asan)" + name: "Clang 20: C++20 (asan)" shared: false asan: true build-type: "RelWithDebInfo" - compiler: "clang" version: "20" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-20" cc: "clang-20" @@ -580,7 +501,7 @@ jobs: container: "ubuntu:24.04" b2-toolset: "clang" is-latest: true - name: "Clang 20: C++17-20 (asan, x86)" + name: "Clang 20: C++20 (asan, x86)" shared: true asan: true x86: true @@ -645,31 +566,31 @@ jobs: - compiler: "clang" version: "17" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-17" cc: "clang-17" runs-on: "ubuntu-24.04" b2-toolset: "clang" - name: "Clang 17: C++17-20" + name: "Clang 17: C++20" shared: false build-type: "Release" - compiler: "clang" version: "16" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-16" cc: "clang-16" runs-on: "ubuntu-24.04" b2-toolset: "clang" - name: "Clang 16: C++17-20" + name: "Clang 16: C++20" shared: true build-type: "Release" - compiler: "clang" version: "15" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-15" cc: "clang-15" @@ -682,7 +603,7 @@ jobs: - compiler: "clang" version: "14" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-14" cc: "clang-14" @@ -695,148 +616,56 @@ jobs: - compiler: "clang" version: "13" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-13" cc: "clang-13" runs-on: "ubuntu-latest" container: "ubuntu:22.04" b2-toolset: "clang" - name: "Clang 13: C++17-20" + name: "Clang 13: C++20" shared: false build-type: "Release" - compiler: "clang" version: "12" - cxxstd: "17,20" + cxxstd: "20" latest-cxxstd: "20" cxx: "clang++-12" cc: "clang-12" runs-on: "ubuntu-latest" container: "ubuntu:22.04" b2-toolset: "clang" - name: "Clang 12: C++17-20" + name: "Clang 12: C++20" shared: true build-type: "Release" - compiler: "clang" version: "11" - cxxstd: "14,17" - latest-cxxstd: "17" + cxxstd: "20" + latest-cxxstd: "20" cxx: "clang++-11" cc: "clang-11" runs-on: "ubuntu-latest" container: "ubuntu:22.04" b2-toolset: "clang" - name: "Clang 11: C++14-17" + name: "Clang 11: C++20" shared: false build-type: "Release" - compiler: "clang" version: "10" - cxxstd: "14,17" - latest-cxxstd: "17" + cxxstd: "20" + latest-cxxstd: "20" cxx: "clang++-10" cc: "clang-10" runs-on: "ubuntu-latest" container: "ubuntu:20.04" b2-toolset: "clang" - name: "Clang 10: C++14-17" - shared: true - build-type: "Release" - - - compiler: "clang" - version: "9" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "clang++-9" - cc: "clang-9" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "clang" - name: "Clang 9: C++14-17" - shared: false - build-type: "Release" - - - compiler: "clang" - version: "8" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "clang++-8" - cc: "clang-8" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "clang" - name: "Clang 8: C++14-17" - shared: true - build-type: "Release" - - - compiler: "clang" - version: "7" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "clang++-7" - cc: "clang-7" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "clang" - name: "Clang 7: C++14-17" - shared: false - build-type: "Release" - - - compiler: "clang" - version: "6" - cxxstd: "14,17" - latest-cxxstd: "17" - cxx: "clang++-6.0" - cc: "clang-6.0" - runs-on: "ubuntu-latest" - container: "ubuntu:20.04" - b2-toolset: "clang" - name: "Clang 6: C++14-17" - shared: true - build-type: "Release" - - - compiler: "clang" - version: "5" - cxxstd: "11,14" - latest-cxxstd: "14" - cxx: "clang++-5.0" - cc: "clang-5.0" - runs-on: "ubuntu-latest" - container: "ubuntu:18.04" - b2-toolset: "clang" - name: "Clang 5: C++11-14" - shared: false - build-type: "Release" - - - compiler: "clang" - version: "4" - cxxstd: "11,14" - latest-cxxstd: "14" - cxx: "clang++-4.0" - cc: "clang-4.0" - runs-on: "ubuntu-latest" - container: "ubuntu:18.04" - b2-toolset: "clang" - name: "Clang 4: C++11-14" + name: "Clang 10: C++20" shared: true build-type: "Release" - - compiler: "clang" - version: "3.9" - cxxstd: "11" - latest-cxxstd: "11" - cxx: "clang++-3.9" - cc: "clang-3.9" - runs-on: "ubuntu-latest" - container: "ubuntu:18.04" - b2-toolset: "clang" - is-earliest: true - name: "Clang 3.9: C++11" - shared: false - build-type: "Release" - name: ${{ matrix.name }} runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.runs-on] }} container: @@ -865,7 +694,7 @@ jobs: - name: Clone Boost.Http.Proto uses: actions/checkout@v3 with: - repository: cppalliance/http_proto + repository: cppalliance/http path: http-proto-root ref: develop @@ -916,7 +745,7 @@ jobs: capy-root scan-modules-ignore: | beast2 - http_proto + http buffers capy @@ -993,7 +822,7 @@ jobs: # Patch boost-root with workspace module cp -r "$workspace_root"/http-io-root "libs/$module" - cp -r "$workspace_root"/http-proto-root libs/http_proto + cp -r "$workspace_root"/http-proto-root libs/http cp -r "$workspace_root"/buffers-root libs/buffers cp -r "$workspace_root"/capy-root libs/capy @@ -1017,7 +846,7 @@ jobs: cxxflags: ${{ (matrix.asan && '-fsanitize=pointer-subtract') || '' }} user-config: ${{ ((startsWith(matrix.runs-on, 'windows') || startsWith(matrix.runs-on, 'macOS')) && format('{0}/user-config.jam', steps.patch.outputs.workspace_root)) || '' }} stop-on-error: true - extra-args: "libs/beast2/example" # https://github.com/ashtum/cpp-actions/issues/23 + extra-args: "libs/beast2/example" - name: Boost CMake Workflow uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.0 @@ -1101,28 +930,28 @@ jobs: ref-source-dir: boost-root/libs/beast2/test/cmake_test toolchain: ${{ (startsWith(matrix.runs-on, 'windows') && steps.patch-user-config.outputs.toolchain) || '' }} - - name: Root Project CMake Workflow - uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.0 - if: ${{ matrix.build-cmake || matrix.is-earliest }} - with: - source-dir: boost-root/libs/${{ steps.patch.outputs.module }} - build-dir: __build_root_test__ - run-tests: true - generator: ${{ matrix.generator }} - generator-toolset: ${{ matrix.generator-toolset }} - build-type: ${{ matrix.build-type }} - install: false - cxxstd: ${{ matrix.latest-cxxstd }} - cc: ${{ steps.setup-cpp.outputs.cc || matrix.cc }} - ccflags: ${{ matrix.ccflags }} - cxx: ${{ steps.setup-cpp.outputs.cxx || matrix.cxx }} - cxxflags: ${{ matrix.cxxflags }} - shared: ${{ matrix.shared }} - cmake-version: '>=3.20' - package: false - package-artifact: false - ref-source-dir: boost-root - toolchain: ${{ (startsWith(matrix.runs-on, 'windows') && steps.patch-user-config.outputs.toolchain) || '' }} + # - name: Root Project CMake Workflow + # uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.0 + # if: ${{ matrix.build-cmake || matrix.is-earliest }} + # with: + # source-dir: boost-root/libs/${{ steps.patch.outputs.module }} + # build-dir: __build_root_test__ + # run-tests: true + # generator: ${{ matrix.generator }} + # generator-toolset: ${{ matrix.generator-toolset }} + # build-type: ${{ matrix.build-type }} + # install: false + # cxxstd: ${{ matrix.latest-cxxstd }} + # cc: ${{ steps.setup-cpp.outputs.cc || matrix.cc }} + # ccflags: ${{ matrix.ccflags }} + # cxx: ${{ steps.setup-cpp.outputs.cxx || matrix.cxx }} + # cxxflags: ${{ matrix.cxxflags }} + # shared: ${{ matrix.shared }} + # cmake-version: '>=3.20' + # package: false + # package-artifact: false + # ref-source-dir: boost-root + # toolchain: ${{ (startsWith(matrix.runs-on, 'windows') && steps.patch-user-config.outputs.toolchain) || '' }} - name: FlameGraph uses: alandefreitas/cpp-actions/flamegraph@v1.9.0 @@ -1212,7 +1041,7 @@ jobs: - name: Clone Boost.Http.Proto uses: actions/checkout@v3 with: - repository: cppalliance/http_proto + repository: cppalliance/http path: http-proto-root ref: develop @@ -1244,7 +1073,7 @@ jobs: capy-root scan-modules-ignore: | beast2 - http_proto + http buffers capy @@ -1279,7 +1108,7 @@ jobs: # Patch boost-root with workspace module cp -r "$workspace_root"/http-io-root "libs/$module" - cp -r "$workspace_root"/http-proto-root libs/http_proto + cp -r "$workspace_root"/http-proto-root libs/http cp -r "$workspace_root"/buffers-root libs/buffers cp -r "$workspace_root"/capy-root libs/capy diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index c264a6a9..00000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - issues: write - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - - # Optional: Customize the trigger phrase (default: @claude) - # trigger_phrase: "/claude" - - # Optional: Trigger when specific user is assigned to an issue - # assignee_trigger: "claude-bot" - - # Optional: Configure Claude's behavior with CLI arguments - # claude_args: | - # --model claude-opus-4-1-20250805 - # --max-turns 10 - # --allowedTools "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" - # --system-prompt "Follow our coding standards. Ensure all new code has tests. Use TypeScript for new files." - - # Optional: Advanced settings configuration - # settings: | - # { - # "env": { - # "NODE_ENV": "test" - # } - # } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 42d344c2..06c484e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,5 @@ -/bin -/bin64 -/_build* -temp - -# Emacs -*# - -# Vim -*~ - -# Visual Studio -/.vs -/out - -# Visual Studio Code -/.vscode +/.vscode/ +/build/* +!/build/Jamfile +/out/ +CMakeUserPresets.json diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 85909ef4..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,59 +0,0 @@ -# CLAUDE.md - -## Code Style - -- C++11 unless otherwise specified -- Boost C++ Libraries naming conventions (snake_case) -- 4-space indentation, no tabs -- Braces on their own line for classes/functions - -## Javadoc Documentation - -Follow Boost C++ Libraries Javadoc style: - -- Brief descriptions on first line after `/**` -- Functions returning values: brief starts with "Return" -- Use `@param` for function parameters -- Use `@tparam` for template parameters, except: - - Variadic args (`Args...`) — omit - - Types deduced from function parameters — omit (self-evident from `@param`) -- Use `@return` for return value details -- Use `@pre` for preconditions -- Use `@post` for postconditions -- Use `@throws` for exceptions -- Use `@note` for important notes -- Use `@see` for cross-references -- Use `@code` / `@endcode` for examples - -## Examples - -```cpp -/** Return the size of the buffer sequence. - - @param buffers The buffer sequence to measure. - - @return The total byte count. -*/ -template -std::size_t -buffer_size(BufferSequence const& buffers); -``` - -No `@tparam` needed—`BufferSequence` is evident from `@param buffers`. - -```cpp -/** Return the default value. - - @tparam T The value type. -*/ -template -T default_value(); -``` - -`@tparam` needed—`T` has no corresponding function parameter. - -## Preferences - -- Concise, dry answers -- Full files, not diffs -- Accurate, compiling C++ code diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f581903..b1ae3901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,13 +55,21 @@ set(BOOST_SRC_DIR ${DEFAULT_BOOST_SRC_DIR} CACHE STRING "Boost source dir to use #------------------------------------------------- # The boost super-project requires one explicit dependency per-line. set(BOOST_BEAST2_DEPENDENCIES - Boost::asio + Boost::corosio Boost::assert + Boost::capy Boost::config - Boost::http_proto + Boost::core + Boost::describe + Boost::endian + Boost::http + Boost::json + Boost::mp11 + Boost::static_assert Boost::system Boost::throw_exception - ) + Boost::url + Boost::variant2) foreach (BOOST_BEAST2_DEPENDENCY ${BOOST_BEAST2_DEPENDENCIES}) if (BOOST_BEAST2_DEPENDENCY MATCHES "^[ ]*Boost::([A-Za-z0-9_]+)[ ]*$") @@ -70,10 +78,10 @@ foreach (BOOST_BEAST2_DEPENDENCY ${BOOST_BEAST2_DEPENDENCIES}) endforeach () # Conditional dependencies if (BOOST_BEAST2_BUILD_TESTS) - set(BOOST_BEAST2_UNIT_TEST_LIBRARIES beast url) + set(BOOST_BEAST2_UNIT_TEST_LIBRARIES beast) endif () if (BOOST_BEAST2_BUILD_EXAMPLES) - set(BOOST_BEAST2_EXAMPLE_LIBRARIES json program_options scope url multiprecision) + set(BOOST_BEAST2_EXAMPLE_LIBRARIES program_options scope multiprecision) endif () # Complete dependency list set(BOOST_INCLUDE_LIBRARIES ${BOOST_BEAST2_INCLUDE_LIBRARIES} ${BOOST_BEAST2_UNIT_TEST_LIBRARIES} ${BOOST_BEAST2_EXAMPLE_LIBRARIES}) @@ -115,7 +123,7 @@ if (BOOST_BEAST2_IS_ROOT) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${BOOST_SRC_DIR}/tools/cmake/include") else () # From Boost Package - find_package(Boost REQUIRED COMPONENTS buffers http_proto program_options scope url) + find_package(Boost REQUIRED COMPONENTS capy corosio http json program_options scope system url) foreach (BOOST_INCLUDE_LIBRARY ${BOOST_INCLUDE_LIBRARIES}) if (NOT TARGET Boost::${BOOST_INCLUDE_LIBRARY}) add_library(Boost::${BOOST_INCLUDE_LIBRARY} ALIAS Boost::headers) @@ -139,9 +147,8 @@ source_group("" FILES "include/boost/beast2.hpp" "build/Jamfile") source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/beast2 PREFIX "include" FILES ${BOOST_BEAST2_HEADERS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX "src" FILES ${BOOST_BEAST2_SOURCES}) - function(boost_beast2_setup_properties target) - target_compile_features(${target} PUBLIC cxx_constexpr) + target_compile_features(${target} PUBLIC cxx_std_20) target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include" PRIVATE "${PROJECT_SOURCE_DIR}") @@ -164,9 +171,10 @@ if (BOOST_BEAST2_MRDOCS_BUILD) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp" "#include \n") add_library(boost_beast2_mrdocs "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp") boost_beast2_setup_properties(boost_beast2_mrdocs) - boost_beast2_setup_properties(boost_beast2_mrdocs PUBLIC BOOST_BEAST2_MRDOCS) + target_compile_definitions(boost_beast2_mrdocs PUBLIC BOOST_BEAST2_MRDOCS) + set_target_properties(boost_beast2_mrdocs PROPERTIES EXPORT_COMPILE_COMMANDS ON) return() -endif() +endif () add_library(boost_beast2 include/boost/beast2.hpp build/Jamfile ${BOOST_BEAST2_HEADERS} ${BOOST_BEAST2_SOURCES}) add_library(Boost::beast2 ALIAS boost_beast2) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..ca4abfb1 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,16 @@ +{ + "version": 8, + "configurePresets": [ + { + "name": "Custom configure preset", + "displayName": "Custom configure preset", + "description": "Sets Ninja generator, build and install directory", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" + } + } + ] +} \ No newline at end of file diff --git a/build/Jamfile b/build/Jamfile index e2870fba..bf1ff920 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -11,11 +11,7 @@ import config : requires ; constant c11-requires : [ requires - cxx11_constexpr - cxx11_decltype - cxx11_hdr_tuple - cxx11_template_aliases - cxx11_variadic_templates + cxx20_hdr_coroutine ] ; @@ -32,15 +28,6 @@ project boost/beast2 shared:BOOST_BEAST2_DYN_LINK=1 static:BOOST_BEAST2_STATIC_LINK=1 clang-win:"-Wno-unused-private-field" - - #TODO: Remove the following defines once asio headers are no longer included in source files - windows:_WIN32_WINNT=0x0601 - windows,gcc:ws2_32 - windows,gcc:mswsock - windows,gcc-cygwin:__USE_W32_SOCKETS - msvc:_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING - msvc:_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING - msvc:_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING : source-location $(BEAST2_ROOT) ; @@ -51,11 +38,13 @@ explicit beast2_sources ; lib boost_beast2 : beast2_sources : requirements - /boost//http_proto + /boost//corosio + /boost//http ../ BOOST_BEAST2_SOURCE : usage-requirements - /boost//http_proto + /boost//corosio + /boost//http ; boost-install boost_beast2 ; diff --git a/doc/antora.yml b/doc/antora.yml index 6dcc0082..6f547189 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -13,8 +13,8 @@ ext: config: doc/mrdocs.yml cpp-tagfiles: files: - - file: ./doc/tagfiles/boost-http_proto-doxygen.tag.xml + - file: ./doc/tagfiles/boost-http-doxygen.tag.xml base_url: 'xref:reference:' using-namespaces: - boost::beast2 - - boost::http_proto + - boost::http diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 1c7dc9e7..8181191a 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -14,13 +14,12 @@ This is a portable C++ library which implements the HTTP/1 protocol using Boost.HTTP.Proto and Boost.Asio. The library is distinguished by these provided features: -* Require only C++11 * Works without exceptions * Fast compilation, few templates == Requirements -* Requires Boost and a compiler supporting at least C++11 +* Requires Boost and a compiler supporting at least C++20 * Link to a static or dynamically linked version of this library * Supports `-fno-exceptions`, detected automatically @@ -28,8 +27,8 @@ provided features: Boost.Buffers has been tested with the following compilers: -* gcc: 5 to 14 (except 8.0.1) -* clang: 3.9, 4 to 18 +* gcc: 10 to 15 +* clang: 11 to 20 * msvc: 14.1 to 14.42 == Quality Assurance diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index 8a10fc1a..3a2054ec 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -33,4 +33,4 @@ use-system-stdlib: true warn-unnamed-param: true warn-if-undoc-enum-val: false -cmake: '-DCMAKE_CXX_STANDARD=20 -DBOOST_BEAST2_MRDOCS_BUILD=ON -DBOOST_BEAST2_BUILD_TESTS=OFF -DBOOST_BEAST2_BUILD_EXAMPLES=OFF' +cmake: '-DCMAKE_CXX_STANDARD=20 -DBOOST_BEAST2_MRDOCS_BUILD=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF' diff --git a/doc/tagfiles/boost-http_proto-doxygen.tag.xml b/doc/tagfiles/boost-http-doxygen.tag.xml similarity index 55% rename from doc/tagfiles/boost-http_proto-doxygen.tag.xml rename to doc/tagfiles/boost-http-doxygen.tag.xml index 88ccc858..48273eab 100644 --- a/doc/tagfiles/boost-http_proto-doxygen.tag.xml +++ b/doc/tagfiles/boost-http-doxygen.tag.xml @@ -1,2545 +1,2705 @@ - boost::http_proto - boost/http_proto.adoc - boost::http_proto::grammar - boost::http_proto::is_sink - boost::http_proto::is_source - boost::http_proto::fields - boost::http_proto::fields_base - boost::http_proto::file - boost::http_proto::file_sink - boost::http_proto::file_source - boost::http_proto::header_limits - boost::http_proto::message_base - boost::http_proto::metadata - boost::http_proto::parameter - boost::http_proto::parser - boost::http_proto::quoted_token_view - boost::http_proto::request - boost::http_proto::request_base - boost::http_proto::request_parser - boost::http_proto::response - boost::http_proto::response_base - boost::http_proto::response_parser - boost::http_proto::serializer - boost::http_proto::sink - boost::http_proto::source - boost::http_proto::static_request - boost::http_proto::static_response - boost::http_proto::string_body - boost::http_proto::upgrade_protocol - boost::http_proto::condition - boost::http_proto::content_coding - boost::http_proto::error - boost::http_proto::field - boost::http_proto::file_mode - boost::http_proto::method - boost::http_proto::payload - boost::http_proto::status - boost::http_proto::status_class - boost::http_proto::version - boost::http_proto::swap - boost::http_proto::to_status_class - boost::http_proto::to_string - boost::http_proto::operator<< - boost::http_proto::parameter_rule - boost::http_proto::quoted_token_rule - boost::http_proto::tchars - boost::http_proto::token_rule - boost::http_proto::upgrade_protocol_rule - boost::http_proto::upgrade_rule + boost::http + boost/http.adoc + boost::http::grammar + boost::http::is_sink + boost::http::is_source + boost::http::route_result + boost::http::router + boost::http::acceptor_config + boost::http::any_router + boost::http::basic_router + boost::http::cors + boost::http::cors_options + boost::http::fields + boost::http::fields_base + boost::http::file_sink + boost::http::file_source + boost::http::header_limits + boost::http::message_base + boost::http::metadata + boost::http::parameter + boost::http::parser + boost::http::quoted_token_view + boost::http::request + boost::http::request_base + boost::http::request_parser + boost::http::response + boost::http::response_base + boost::http::response_parser + boost::http::resumer + boost::http::route_params + boost::http::route_params_base + boost::http::router_options + boost::http::serializer + boost::http::sink + boost::http::source + boost::http::static_request + boost::http::static_response + boost::http::string_body + boost::http::suspender + boost::http::upgrade_protocol + boost::http::condition + boost::http::content_coding + boost::http::error + boost::http::field + boost::http::method + boost::http::payload + boost::http::route + boost::http::status + boost::http::status_class + boost::http::version + boost::http::swap + boost::http::to_status_class + boost::http::to_string + boost::http::operator<< + boost::http::parameter_rule + boost::http::quoted_token_rule + boost::http::tchars + boost::http::token_rule + boost::http::upgrade_protocol_rule + boost::http::upgrade_rule + + auto + co_route + boost/http/co_route.adoc + + (std::function<capy::task<route_result>(route_params&)> f) + core::string_view combine_field_values - boost/http_proto/combine_field_values.adoc + boost/http/combine_field_values.adoc (const fields_base::subrange& vr, grammar::recycled_ptr<std::string>& temp) void install_parser_service - boost/http_proto/install_parser_service.adoc + boost/http/install_parser_service.adoc - (rts::context& ctx, const parser::config_base& cfg) + (capy::polystore& ctx, const parser::config_base& cfg) void install_serializer_service - boost/http_proto/install_serializer_service.adoc + boost/http/install_serializer_service.adoc - (rts::context& ctx, const serializer::config& cfg) + (capy::polystore& ctx, const serializer::config& cfg) status int_to_status - boost/http_proto/int_to_status.adoc + boost/http/int_to_status.adoc (unsigned int v) - implementation_defined::list_rule_t<Rule> - list_rule - boost/http_proto/list_rule.adoc + bool + is_route_result + boost/http/is_route_result.adoc - (const Rule& r, std::size_t n, std::size_t m) + (route_result rv) - system::error_code - make_error_code - boost/http_proto/make_error_code.adoc + implementation_defined::list_rule_t<Rule> + list_rule + boost/http/list_rule.adoc - (error ev) + (const Rule& r, std::size_t n, std::size_t m) system::error_condition make_error_condition - boost/http_proto/make_error_condition.adoc + boost/http/make_error_condition.adoc (condition c) - - core::string_view - obsolete_reason - boost/http_proto/obsolete_reason.adoc - - (status v) - boost::optional<field> string_to_field - boost/http_proto/string_to_field.adoc + boost/http/string_to_field.adoc (core::string_view s) method string_to_method - boost/http_proto/string_to_method.adoc + boost/http/string_to_method.adoc (core::string_view s) - boost::http_proto::grammar - boost/http_proto/grammar.adoc + boost::http::grammar + boost/http/grammar.adoc + + + boost::http::is_sink + boost/http/is_sink.adoc + + + boost::http::is_source + boost/http/is_source.adoc + + + boost::http::route_result + boost/http/route_result.adoc + + + boost::http::router + boost/http/router.adoc + + + boost::http::acceptor_config + boost/http/acceptor_config.adoc + + + boost::http::any_router + boost/http/any_router.adoc + + void + ~any_router + boost/http/any_router/2destructor.adoc + + () + + + std::size_t + count + boost/http/any_router/count.adoc + + () + + + route_result + do_dispatch + boost/http/any_router/do_dispatch.adoc + + (route_params_base&) + + + layer& + new_layer + boost/http/any_router/new_layer.adoc + + (core::string_view pattern) + + + route_result + resume_impl + boost/http/any_router/resume_impl.adoc + + (route_params_base&, route_result ec) + + + + boost::http::basic_router + boost/http/basic_router.adoc + + void + all + boost/http/basic_router/all.adoc + + (core::string_view pattern, H1&& h1, ...HN...&& hn) + + + route_result + resume + boost/http/basic_router/resume.adoc + + (Params& p, const route_result& rv) + + + fluent_route + route + boost/http/basic_router/route.adoc + + (core::string_view pattern) + - boost::http_proto::is_sink - boost/http_proto/is_sink.adoc + boost::http::cors + boost/http/cors.adoc + + void + cors + boost/http/cors/2constructor.adoc + + (cors_options options) + + + route_result + operator() + boost/http/cors/operator_call.adoc + + (route_params& p) + - boost::http_proto::is_source - boost/http_proto/is_source.adoc + boost::http::cors_options + boost/http/cors_options.adoc - boost::http_proto::fields - boost/http_proto/fields.adoc + boost::http::fields + boost/http/fields.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () void swap - boost/http_proto/fields/swap.adoc + boost/http/fields/swap.adoc (fields& other) std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::fields_base - boost/http_proto/fields_base.adoc + boost::http::fields_base + boost/http/fields_base.adoc void ~fields_base - boost/http_proto/fields_base/2destructor.adoc + boost/http/fields_base/2destructor.adoc () iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc - - () - - - - boost::http_proto::file - boost/http_proto/file.adoc - - void - ~file - boost/http_proto/file/2destructor.adoc - - () - - - file& - operator= - boost/http_proto/file/operator_assign.adoc - - (file&& other) - - - bool - is_open - boost/http_proto/file/is_open.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::file_sink - boost/http_proto/file_sink.adoc + boost::http::file_sink + boost/http/file_sink.adoc void ~file_sink - boost/http_proto/file_sink/2destructor.adoc + boost/http/file_sink/2destructor.adoc () results write - boost/http_proto/sink/write.adoc + boost/http/sink/write.adoc (const ConstBufferSequence& bs, bool more) - boost::http_proto::file_source - boost/http_proto/file_source.adoc + boost::http::file_source + boost/http/file_source.adoc void ~file_source - boost/http_proto/file_source/2destructor.adoc + boost/http/file_source/2destructor.adoc () results read - boost/http_proto/source/read.adoc + boost/http/source/read.adoc (const MutableBufferSequence& bs) - boost::http_proto::header_limits - boost/http_proto/header_limits.adoc + boost::http::header_limits + boost/http/header_limits.adoc std::size_t valid_space_needed - boost/http_proto/header_limits/valid_space_needed.adoc + boost/http/header_limits/valid_space_needed.adoc () - boost::http_proto::message_base - boost/http_proto/message_base.adoc + boost::http::message_base + boost/http/message_base.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::metadata - boost/http_proto/metadata.adoc + boost::http::metadata + boost/http/metadata.adoc void metadata - boost/http_proto/metadata/2constructor.adoc + boost/http/metadata/2constructor.adoc () - boost::http_proto::parameter - boost/http_proto/parameter.adoc + boost::http::parameter + boost/http/parameter.adoc - boost::http_proto::parser - boost/http_proto/parser.adoc + boost::http::parser + boost/http/parser.adoc core::string_view body - boost/http_proto/parser/body.adoc + boost/http/parser/body.adoc () void commit - boost/http_proto/parser/commit.adoc + boost/http/parser/commit.adoc (std::size_t n) void commit_eof - boost/http_proto/parser/commit_eof.adoc + boost/http/parser/commit_eof.adoc () void consume_body - boost/http_proto/parser/consume_body.adoc + boost/http/parser/consume_body.adoc (std::size_t n) bool got_header - boost/http_proto/parser/got_header.adoc + boost/http/parser/got_header.adoc + + () + + + bool + is_body_set + boost/http/parser/is_body_set.adoc () bool is_complete - boost/http_proto/parser/is_complete.adoc + boost/http/parser/is_complete.adoc () void parse - boost/http_proto/parser/parse.adoc + boost/http/parser/parse.adoc (system::error_code& ec) mutable_buffers_type prepare - boost/http_proto/parser/prepare.adoc + boost/http/parser/prepare.adoc () const_buffers_type pull_body - boost/http_proto/parser/pull_body.adoc + boost/http/parser/pull_body.adoc () core::string_view release_buffered_data - boost/http_proto/parser/release_buffered_data.adoc + boost/http/parser/release_buffered_data.adoc () void reset - boost/http_proto/parser/reset.adoc + boost/http/parser/reset.adoc () void set_body_limit - boost/http_proto/parser/set_body_limit.adoc + boost/http/parser/set_body_limit.adoc (uint64_t n) void start - boost/http_proto/parser/start.adoc + boost/http/parser/start.adoc () void ~parser - boost/http_proto/parser/2destructor.adoc + boost/http/parser/2destructor.adoc () - bool - is_body_set - boost/http_proto/parser/is_body_set.adoc + void + assign + boost/http/parser/assign.adoc - () + (parser&& other) const static_request& safe_get_request - boost/http_proto/parser/safe_get_request.adoc + boost/http/parser/safe_get_request.adoc () const static_response& safe_get_response - boost/http_proto/parser/safe_get_response.adoc + boost/http/parser/safe_get_response.adoc () void start_impl - boost/http_proto/parser/start_impl.adoc + boost/http/parser/start_impl.adoc - (bool ) + (bool) detail::workspace& ws - boost/http_proto/parser/ws.adoc + boost/http/parser/ws.adoc () - boost::http_proto::quoted_token_view - boost/http_proto/quoted_token_view.adoc + boost::http::quoted_token_view + boost/http/quoted_token_view.adoc quoted_token_view& operator= - boost/http_proto/quoted_token_view/operator_assign-07.adoc + boost/http/quoted_token_view/operator_assign-0e.adoc (const quoted_token_view& other) const_reference at - boost/http_proto/quoted_token_view/at.adoc + boost/http/quoted_token_view/at.adoc (size_type pos) const_reference back - boost/http_proto/quoted_token_view/back.adoc + boost/http/quoted_token_view/back.adoc () const_iterator begin - boost/http_proto/quoted_token_view/begin.adoc + boost/http/quoted_token_view/begin.adoc () const_iterator cbegin - boost/http_proto/quoted_token_view/cbegin.adoc + boost/http/quoted_token_view/cbegin.adoc () const_iterator cend - boost/http_proto/quoted_token_view/cend.adoc + boost/http/quoted_token_view/cend.adoc () size_type copy - boost/http_proto/quoted_token_view/copy.adoc + boost/http/quoted_token_view/copy.adoc (char* s, size_type n, size_type pos) const_reverse_iterator crbegin - boost/http_proto/quoted_token_view/crbegin.adoc + boost/http/quoted_token_view/crbegin.adoc () const_reverse_iterator crend - boost/http_proto/quoted_token_view/crend.adoc + boost/http/quoted_token_view/crend.adoc () const_pointer data - boost/http_proto/quoted_token_view/data.adoc + boost/http/quoted_token_view/data.adoc () bool empty - boost/http_proto/quoted_token_view/empty.adoc + boost/http/quoted_token_view/empty.adoc () const_iterator end - boost/http_proto/quoted_token_view/end.adoc + boost/http/quoted_token_view/end.adoc () const_reference front - boost/http_proto/quoted_token_view/front.adoc + boost/http/quoted_token_view/front.adoc () bool has_escapes - boost/http_proto/quoted_token_view/has_escapes.adoc + boost/http/quoted_token_view/has_escapes.adoc () size_type length - boost/http_proto/quoted_token_view/length.adoc + boost/http/quoted_token_view/length.adoc () size_type max_size - boost/http_proto/quoted_token_view/max_size.adoc + boost/http/quoted_token_view/max_size.adoc () const_reference operator[] - boost/http_proto/quoted_token_view/operator_subs.adoc + boost/http/quoted_token_view/operator_subs.adoc (size_type pos) const_reverse_iterator rbegin - boost/http_proto/quoted_token_view/rbegin.adoc + boost/http/quoted_token_view/rbegin.adoc () const_reverse_iterator rend - boost/http_proto/quoted_token_view/rend.adoc + boost/http/quoted_token_view/rend.adoc () size_type size - boost/http_proto/quoted_token_view/size.adoc + boost/http/quoted_token_view/size.adoc () core::string_view substr - boost/http_proto/quoted_token_view/substr.adoc + boost/http/quoted_token_view/substr.adoc (size_type pos, size_type n) std::size_t unescaped_size - boost/http_proto/quoted_token_view/unescaped_size.adoc + boost/http/quoted_token_view/unescaped_size.adoc () std::string operator basic_string<char> - boost/http_proto/quoted_token_view/2conversion-042.adoc + boost/http/quoted_token_view/2conversion-08.adoc () std::string_view operator basic_string_view<char> - boost/http_proto/quoted_token_view/2conversion-04e.adoc + boost/http/quoted_token_view/2conversion-04.adoc () string_view_base& operator= - boost/http_proto/quoted_token_view/operator_assign-0e.adoc + boost/http/quoted_token_view/operator_assign-07.adoc (const string_view_base& other) void swap - boost/http_proto/quoted_token_view/swap.adoc + boost/http/quoted_token_view/swap.adoc (string_view_base& s) - boost::http_proto::request - boost/http_proto/request.adoc + boost::http::request + boost/http/request.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::method + http::method method - boost/http_proto/request_base/method.adoc + boost/http/request_base/method.adoc () core::string_view method_text - boost/http_proto/request_base/method_text.adoc + boost/http/request_base/method_text.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_expect_100_continue - boost/http_proto/request_base/set_expect_100_continue.adoc + boost/http/request_base/set_expect_100_continue.adoc (bool b) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) void set_target - boost/http_proto/request_base/set_target.adoc + boost/http/request_base/set_target.adoc (core::string_view s) void set_version - boost/http_proto/request_base/set_version.adoc + boost/http/request_base/set_version.adoc - (http_proto::version v) + (http::version v) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () void swap - boost/http_proto/request/swap.adoc + boost/http/request/swap.adoc (request& other) core::string_view target - boost/http_proto/request_base/target.adoc + boost/http/request_base/target.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::request_base - boost/http_proto/request_base.adoc + boost::http::request_base + boost/http/request_base.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::method + http::method method - boost/http_proto/request_base/method.adoc + boost/http/request_base/method.adoc () core::string_view method_text - boost/http_proto/request_base/method_text.adoc + boost/http/request_base/method_text.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_expect_100_continue - boost/http_proto/request_base/set_expect_100_continue.adoc + boost/http/request_base/set_expect_100_continue.adoc (bool b) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) void set_target - boost/http_proto/request_base/set_target.adoc + boost/http/request_base/set_target.adoc (core::string_view s) void set_version - boost/http_proto/request_base/set_version.adoc + boost/http/request_base/set_version.adoc - (http_proto::version v) + (http::version v) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () core::string_view target - boost/http_proto/request_base/target.adoc + boost/http/request_base/target.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () void set_start_line_impl - boost/http_proto/request_base/set_start_line_impl.adoc + boost/http/request_base/set_start_line_impl.adoc - (http_proto::method m, core::string_view ms, core::string_view t, http_proto::version v) + (http::method m, core::string_view ms, core::string_view t, http::version v) - boost::http_proto::request_parser - boost/http_proto/request_parser.adoc + boost::http::request_parser + boost/http/request_parser.adoc void ~request_parser - boost/http_proto/request_parser/2destructor.adoc + boost/http/request_parser/2destructor.adoc () + + request_parser& + operator= + boost/http/request_parser/operator_assign.adoc + + (request_parser&& other) + core::string_view body - boost/http_proto/parser/body.adoc + boost/http/parser/body.adoc () void commit - boost/http_proto/parser/commit.adoc + boost/http/parser/commit.adoc (std::size_t n) void commit_eof - boost/http_proto/parser/commit_eof.adoc + boost/http/parser/commit_eof.adoc () void consume_body - boost/http_proto/parser/consume_body.adoc + boost/http/parser/consume_body.adoc (std::size_t n) const static_request& get - boost/http_proto/request_parser/get.adoc + boost/http/request_parser/get.adoc () bool got_header - boost/http_proto/parser/got_header.adoc + boost/http/parser/got_header.adoc + + () + + + bool + is_body_set + boost/http/parser/is_body_set.adoc () bool is_complete - boost/http_proto/parser/is_complete.adoc + boost/http/parser/is_complete.adoc () void parse - boost/http_proto/parser/parse.adoc + boost/http/parser/parse.adoc (system::error_code& ec) mutable_buffers_type prepare - boost/http_proto/parser/prepare.adoc + boost/http/parser/prepare.adoc () const_buffers_type pull_body - boost/http_proto/parser/pull_body.adoc + boost/http/parser/pull_body.adoc () core::string_view release_buffered_data - boost/http_proto/parser/release_buffered_data.adoc + boost/http/parser/release_buffered_data.adoc () void reset - boost/http_proto/parser/reset.adoc + boost/http/parser/reset.adoc () void set_body_limit - boost/http_proto/parser/set_body_limit.adoc + boost/http/parser/set_body_limit.adoc (uint64_t n) void start - boost/http_proto/parser/start.adoc + boost/http/parser/start.adoc () - boost::http_proto::response - boost/http_proto/response.adoc + boost::http::response + boost/http/response.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () core::string_view reason - boost/http_proto/response_base/reason.adoc + boost/http/response_base/reason.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) + + void + set_status + boost/http/response_base/set_status.adoc + + (http::status sc) + + + void + set_version + boost/http/response_base/set_version.adoc + + (http::version v) + void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () - http_proto::status + http::status status - boost/http_proto/response_base/status.adoc + boost/http/response_base/status.adoc () unsigned short status_int - boost/http_proto/response_base/status_int.adoc + boost/http/response_base/status_int.adoc () void swap - boost/http_proto/response/swap.adoc + boost/http/response/swap.adoc (response& other) - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::response_base - boost/http_proto/response_base.adoc + boost::http::response_base + boost/http/response_base.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () core::string_view reason - boost/http_proto/response_base/reason.adoc + boost/http/response_base/reason.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) + + void + set_status + boost/http/response_base/set_status.adoc + + (http::status sc) + + + void + set_version + boost/http/response_base/set_version.adoc + + (http::version v) + void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () - http_proto::status + http::status status - boost/http_proto/response_base/status.adoc + boost/http/response_base/status.adoc () unsigned short status_int - boost/http_proto/response_base/status_int.adoc + boost/http/response_base/status_int.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () void set_start_line_impl - boost/http_proto/response_base/set_start_line_impl.adoc + boost/http/response_base/set_start_line_impl.adoc - (http_proto::status sc, unsigned short si, core::string_view reason, http_proto::version v) + (http::status sc, unsigned short si, core::string_view reason, http::version v) - boost::http_proto::response_parser - boost/http_proto/response_parser.adoc - - void - ~response_parser - boost/http_proto/response_parser/2destructor.adoc - - () - - - core::string_view - body - boost/http_proto/parser/body.adoc - - () - + boost::http::response_parser + boost/http/response_parser.adoc + + + boost::http::resumer + boost/http/resumer.adoc - void - commit - boost/http_proto/parser/commit.adoc + resumer& + operator= + boost/http/resumer/operator_assign.adoc - (std::size_t n) + (const resumer& other) + + + boost::http::route_params + boost/http/route_params.adoc void - commit_eof - boost/http_proto/parser/commit_eof.adoc + ~route_params + boost/http/route_params/2destructor.adoc () - void - consume_body - boost/http_proto/parser/consume_body.adoc - - (std::size_t n) - - - const static_response& - get - boost/http_proto/response_parser/get.adoc + route_result + read_body + boost/http/route_params/read_body.adoc - () + (ValueSink&& sink, Callback&& callback) - bool - got_header - boost/http_proto/parser/got_header.adoc + void + reset + boost/http/route_params/reset.adoc () - bool - is_complete - boost/http_proto/parser/is_complete.adoc + route_params& + set_body + boost/http/route_params/set_body.adoc - () + (std::string s) - void - parse - boost/http_proto/parser/parse.adoc + route_result + spawn + boost/http/route_params/spawn.adoc - (system::error_code& ec) + (capy::task<route_result> coro) - mutable_buffers_type - prepare - boost/http_proto/parser/prepare.adoc - - () - - - const_buffers_type - pull_body - boost/http_proto/parser/pull_body.adoc + route_params& + status + boost/http/route_params/status.adoc - () + (http::status code) + + + boost::http::route_params_base + boost/http/route_params_base.adoc - core::string_view - release_buffered_data - boost/http_proto/parser/release_buffered_data.adoc + route_params_base& + operator= + boost/http/route_params_base/operator_assign.adoc - () + (const route_params_base&) + + + boost::http::router_options + boost/http/router_options.adoc void - reset - boost/http_proto/parser/reset.adoc + router_options + boost/http/router_options/2constructor.adoc () - void - set_body_limit - boost/http_proto/parser/set_body_limit.adoc + router_options& + case_sensitive + boost/http/router_options/case_sensitive.adoc - (uint64_t n) + (bool value) - void - start - boost/http_proto/parser/start.adoc + router_options& + merge_params + boost/http/router_options/merge_params.adoc - () + (bool value) - void - start_head_response - boost/http_proto/response_parser/start_head_response.adoc + router_options& + strict + boost/http/router_options/strict.adoc - () + (bool value) - boost::http_proto::serializer - boost/http_proto/serializer.adoc + boost::http::serializer + boost/http/serializer.adoc void ~serializer - boost/http_proto/serializer/2destructor.adoc + boost/http/serializer/2destructor.adoc () + + serializer& + operator= + boost/http/serializer/operator_assign.adoc + + (serializer&& other) + void consume - boost/http_proto/serializer/consume.adoc + boost/http/serializer/consume.adoc (std::size_t n) bool is_done - boost/http_proto/serializer/is_done.adoc + boost/http/serializer/is_done.adoc () system::result<const_buffers_type> prepare - boost/http_proto/serializer/prepare.adoc + boost/http/serializer/prepare.adoc () void reset - boost/http_proto/serializer/reset.adoc + boost/http/serializer/reset.adoc () stream start_stream - boost/http_proto/serializer/start_stream.adoc + boost/http/serializer/start_stream.adoc (const message_base& m) void start_buffers - boost/http_proto/serializer/start_buffers.adoc + boost/http/serializer/start_buffers.adoc - (const message_base& , cbs_gen& ) + (const message_base&, cbs_gen&) void start_init - boost/http_proto/serializer/start_init.adoc + boost/http/serializer/start_init.adoc - (const message_base& ) + (const message_base&) void start_source - boost/http_proto/serializer/start_source.adoc + boost/http/serializer/start_source.adoc - (const message_base& , source& ) + (const message_base&, source&) detail::workspace& ws - boost/http_proto/serializer/ws.adoc + boost/http/serializer/ws.adoc () - boost::http_proto::sink - boost/http_proto/sink.adoc + boost::http::sink + boost/http/sink.adoc results write - boost/http_proto/sink/write.adoc + boost/http/sink/write.adoc (const ConstBufferSequence& bs, bool more) - boost::http_proto::source - boost/http_proto/source.adoc + boost::http::source + boost/http/source.adoc results read - boost/http_proto/source/read.adoc + boost/http/source/read.adoc (const MutableBufferSequence& bs) - boost::http_proto::static_request - boost/http_proto/static_request.adoc + boost::http::static_request + boost/http/static_request.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::method + http::method method - boost/http_proto/request_base/method.adoc + boost/http/request_base/method.adoc () core::string_view method_text - boost/http_proto/request_base/method_text.adoc + boost/http/request_base/method_text.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_expect_100_continue - boost/http_proto/request_base/set_expect_100_continue.adoc + boost/http/request_base/set_expect_100_continue.adoc (bool b) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) void set_target - boost/http_proto/request_base/set_target.adoc + boost/http/request_base/set_target.adoc (core::string_view s) void set_version - boost/http_proto/request_base/set_version.adoc + boost/http/request_base/set_version.adoc - (http_proto::version v) + (http::version v) void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () core::string_view target - boost/http_proto/request_base/target.adoc + boost/http/request_base/target.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::static_response - boost/http_proto/static_response.adoc + boost::http::static_response + boost/http/static_response.adoc iterator begin - boost/http_proto/fields_base/begin.adoc + boost/http/fields_base/begin.adoc () core::string_view buffer - boost/http_proto/fields_base/buffer.adoc + boost/http/fields_base/buffer.adoc () std::size_t capacity_in_bytes - boost/http_proto/fields_base/capacity_in_bytes.adoc + boost/http/fields_base/capacity_in_bytes.adoc () bool chunked - boost/http_proto/message_base/chunked.adoc + boost/http/message_base/chunked.adoc () void clear - boost/http_proto/fields_base/clear.adoc + boost/http/fields_base/clear.adoc () iterator end - boost/http_proto/fields_base/end.adoc + boost/http/fields_base/end.adoc () bool keep_alive - boost/http_proto/message_base/keep_alive.adoc + boost/http/message_base/keep_alive.adoc () std::size_t max_capacity_in_bytes - boost/http_proto/fields_base/max_capacity_in_bytes.adoc + boost/http/fields_base/max_capacity_in_bytes.adoc () - const http_proto::metadata& + const http::metadata& metadata - boost/http_proto/message_base/metadata.adoc + boost/http/message_base/metadata.adoc () - http_proto::payload + http::payload payload - boost/http_proto/message_base/payload.adoc + boost/http/message_base/payload.adoc () uint64_t payload_size - boost/http_proto/message_base/payload_size.adoc + boost/http/message_base/payload_size.adoc () reverse_iterator rbegin - boost/http_proto/fields_base/rbegin.adoc + boost/http/fields_base/rbegin.adoc () core::string_view reason - boost/http_proto/response_base/reason.adoc + boost/http/response_base/reason.adoc () reverse_iterator rend - boost/http_proto/fields_base/rend.adoc + boost/http/fields_base/rend.adoc () void reserve_bytes - boost/http_proto/fields_base/reserve_bytes.adoc + boost/http/fields_base/reserve_bytes.adoc (std::size_t n) void set_chunked - boost/http_proto/message_base/set_chunked.adoc + boost/http/message_base/set_chunked.adoc (bool value) void set_content_length - boost/http_proto/message_base/set_content_length.adoc + boost/http/message_base/set_content_length.adoc (uint64_t n) void set_keep_alive - boost/http_proto/message_base/set_keep_alive.adoc + boost/http/message_base/set_keep_alive.adoc (bool value) void set_max_capacity_in_bytes - boost/http_proto/fields_base/set_max_capacity_in_bytes.adoc + boost/http/fields_base/set_max_capacity_in_bytes.adoc (std::size_t n) void set_payload_size - boost/http_proto/message_base/set_payload_size.adoc + boost/http/message_base/set_payload_size.adoc (uint64_t n) + + void + set_status + boost/http/response_base/set_status.adoc + + (http::status sc) + + + void + set_version + boost/http/response_base/set_version.adoc + + (http::version v) + void shrink_to_fit - boost/http_proto/fields_base/shrink_to_fit.adoc + boost/http/fields_base/shrink_to_fit.adoc () std::size_t size - boost/http_proto/fields_base/size.adoc + boost/http/fields_base/size.adoc () - http_proto::status + http::status status - boost/http_proto/response_base/status.adoc + boost/http/response_base/status.adoc () unsigned short status_int - boost/http_proto/response_base/status_int.adoc + boost/http/response_base/status_int.adoc () - http_proto::version + http::version version - boost/http_proto/message_base/version.adoc + boost/http/message_base/version.adoc () std::size_t max_size - boost/http_proto/fields_base/max_size.adoc + boost/http/fields_base/max_size.adoc () - boost::http_proto::string_body - boost/http_proto/string_body.adoc + boost::http::string_body + boost/http/string_body.adoc const_iterator begin - boost/http_proto/string_body/begin.adoc + boost/http/string_body/begin.adoc () const_iterator end - boost/http_proto/string_body/end.adoc + boost/http/string_body/end.adoc () - boost::http_proto::upgrade_protocol - boost/http_proto/upgrade_protocol.adoc + boost::http::suspender + boost/http/suspender.adoc + + suspender& + operator= + boost/http/suspender/operator_assign.adoc + + (const suspender& other) + + + route_result + operator() + boost/http/suspender/operator_call.adoc + + (F&& f) + + + + boost::http::upgrade_protocol + boost/http/upgrade_protocol.adoc - boost::http_proto::condition - boost/http_proto/condition.adoc + boost::http::condition + boost/http/condition.adoc - boost::http_proto::content_coding - boost/http_proto/content_coding.adoc + boost::http::content_coding + boost/http/content_coding.adoc - boost::http_proto::error - boost/http_proto/error.adoc + boost::http::error + boost/http/error.adoc - boost::http_proto::field - boost/http_proto/field.adoc + boost::http::field + boost/http/field.adoc - boost::http_proto::file_mode - boost/http_proto/file_mode.adoc + boost::http::method + boost/http/method.adoc - boost::http_proto::method - boost/http_proto/method.adoc + boost::http::payload + boost/http/payload.adoc - boost::http_proto::payload - boost/http_proto/payload.adoc + boost::http::route + boost/http/route.adoc - boost::http_proto::status - boost/http_proto/status.adoc + boost::http::status + boost/http/status.adoc - boost::http_proto::status_class - boost/http_proto/status_class.adoc + boost::http::status_class + boost/http/status_class.adoc - boost::http_proto::version - boost/http_proto/version.adoc + boost::http::version + boost/http/version.adoc - boost::http_proto::swap - boost/http_proto/swap-0e.adoc + boost::http::swap + boost/http/swap-0b.adoc - boost::http_proto::to_status_class - boost/http_proto/to_status_class-08.adoc + boost::http::to_status_class + boost/http/to_status_class-03.adoc - boost::http_proto::to_string - boost/http_proto/to_string-0078.adoc + boost::http::to_string + boost/http/to_string-082.adoc - boost::http_proto::operator<< - boost/http_proto/operator_lshift-0f.adoc + boost::http::operator<< + boost/http/operator_lshift-0d.adoc - boost::http_proto::parameter_rule - boost/http_proto/parameter_rule.adoc + boost::http::parameter_rule + boost/http/parameter_rule.adoc - boost::http_proto::quoted_token_rule - boost/http_proto/quoted_token_rule.adoc + boost::http::quoted_token_rule + boost/http/quoted_token_rule.adoc - boost::http_proto::tchars - boost/http_proto/tchars.adoc + boost::http::tchars + boost/http/tchars.adoc - boost::http_proto::token_rule - boost/http_proto/token_rule.adoc + boost::http::token_rule + boost/http/token_rule.adoc - boost::http_proto::upgrade_protocol_rule - boost/http_proto/upgrade_protocol_rule.adoc + boost::http::upgrade_protocol_rule + boost/http/upgrade_protocol_rule.adoc - boost::http_proto::upgrade_rule - boost/http_proto/upgrade_rule.adoc + boost::http::upgrade_rule + boost/http/upgrade_rule.adoc diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index f9abbaca..d4b2ddbc 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -8,5 +8,5 @@ # Official repository: https://github.com/cppalliance/beast2 # -add_subdirectory(client) +# add_subdirectory(client) # disabled for now add_subdirectory(server) diff --git a/example/client/CMakeLists.txt b/example/client/CMakeLists.txt index 7bf9965f..6ec818ed 100644 --- a/example/client/CMakeLists.txt +++ b/example/client/CMakeLists.txt @@ -12,7 +12,7 @@ find_package(OpenSSL) add_subdirectory(visit) if (OPENSSL_FOUND) - add_subdirectory(burl) + # add_subdirectory(burl) add_subdirectory(get) add_subdirectory(jsonrpc) endif () diff --git a/example/client/burl/CMakeLists.txt b/example/client/burl/CMakeLists.txt index 5cd5e701..921ecea6 100644 --- a/example/client/burl/CMakeLists.txt +++ b/example/client/burl/CMakeLists.txt @@ -7,48 +7,45 @@ # Official repository: https://github.com/cppalliance/beast2 # -if (CMAKE_CXX_STANDARD EQUAL 20) - file(GLOB_RECURSE PFILES CONFIGURE_DEPENDS *.cpp *.hpp - CMakeLists.txt - Jamfile) +file(GLOB_RECURSE PFILES CONFIGURE_DEPENDS *.cpp *.hpp + CMakeLists.txt + Jamfile) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${PFILES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${PFILES}) - add_executable(beast2_example_client_burl ${PFILES}) +add_executable(beast2_example_client_burl ${PFILES}) - target_compile_definitions(beast2_example_client_burl - PRIVATE BOOST_ASIO_NO_DEPRECATED) +target_compile_definitions(beast2_example_client_burl + PRIVATE BOOST_ASIO_NO_DEPRECATED) - set_property(TARGET beast2_example_client_burl - PROPERTY FOLDER "examples") +set_property(TARGET beast2_example_client_burl + PROPERTY FOLDER "examples") - find_package(OpenSSL REQUIRED) +find_package(OpenSSL REQUIRED) - target_link_libraries(beast2_example_client_burl - Boost::beast2 - Boost::url - Boost::program_options - Boost::scope - OpenSSL::SSL - OpenSSL::Crypto) +target_link_libraries(beast2_example_client_burl + Boost::beast2 + Boost::url + Boost::program_options + Boost::scope + OpenSSL::SSL + OpenSSL::Crypto) - if (WIN32) - target_link_libraries(beast2_example_client_burl crypt32) - endif() - - if (TARGET Boost::capy_zlib) - target_link_libraries(beast2_example_client_burl Boost::capy_zlib) - endif() - - if (TARGET Boost::capy_brotli) - target_link_libraries(beast2_example_client_burl Boost::capy_brotli) - endif() +if (WIN32) + target_link_libraries(beast2_example_client_burl crypt32) +endif() - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - find_package(Libpsl) - if (Libpsl_FOUND) - target_link_libraries(beast2_example_client_burl Libpsl::Libpsl) - target_compile_definitions(beast2_example_client_burl PRIVATE BURL_HAS_LIBPSL) - endif () +if (TARGET Boost::capy_zlib) + target_link_libraries(beast2_example_client_burl Boost::capy_zlib) +endif() +if (TARGET Boost::capy_brotli) + target_link_libraries(beast2_example_client_burl Boost::capy_brotli) endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(Libpsl) +if (Libpsl_FOUND) + target_link_libraries(beast2_example_client_burl Libpsl::Libpsl) + target_compile_definitions(beast2_example_client_burl PRIVATE BURL_HAS_LIBPSL) +endif () diff --git a/example/client/burl/Jamfile b/example/client/burl/Jamfile index b342c0ee..666382f1 100644 --- a/example/client/burl/Jamfile +++ b/example/client/burl/Jamfile @@ -20,7 +20,6 @@ lib user32 ; project : requirements [ requires - cxx20_hdr_concepts cxx20_hdr_format ] /boost/beast2//boost_beast2 diff --git a/example/client/burl/any_stream.hpp b/example/client/burl/any_stream.hpp index 62d348fe..ed13e94d 100644 --- a/example/client/burl/any_stream.hpp +++ b/example/client/burl/any_stream.hpp @@ -16,21 +16,21 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include namespace asio = boost::asio; -namespace buffers = boost::buffers; -namespace http_proto = boost::http_proto; +namespace capy = boost::capy; +namespace http = boost::http; using error_code = boost::system::error_code; class any_stream { public: - using const_buffers_type = http_proto::serializer::const_buffers_type; - using mutable_buffers_type = http_proto::parser::mutable_buffers_type; + using const_buffers_type = http::serializer::const_buffers_type; + using mutable_buffers_type = http::parser::mutable_buffers_type; using executor_type = asio::any_io_executor; template @@ -56,7 +56,7 @@ class any_stream virtual void async_write_some( - const buffers::slice_of>& buffers, + const capy::slice_of>& buffers, asio::any_completion_handler handler) override { @@ -65,7 +65,7 @@ class any_stream virtual void async_read_some( - const buffers::slice_of>& buffers, + const capy::slice_of>& buffers, asio::any_completion_handler handler) override { @@ -153,7 +153,7 @@ class any_stream } BOOST_ASIO_CORO_YIELD stream_->async_write_some( - buffers::prefix(buffers, wr_remain_), + capy::prefix(buffers, wr_remain_), std::move(self)); wr_remain_ -= n; self.complete(ec, n); @@ -191,7 +191,7 @@ class any_stream } BOOST_ASIO_CORO_YIELD stream_->async_read_some( - buffers::prefix(buffers, rd_remain_), + capy::prefix(buffers, rd_remain_), std::move(self)); rd_remain_ -= n; self.complete(ec, n); @@ -226,12 +226,12 @@ class any_stream virtual void async_write_some( - const buffers::slice_of>&, + const capy::slice_of>&, asio::any_completion_handler) = 0; virtual void async_read_some( - const buffers::slice_of>&, + const capy::slice_of>&, asio::any_completion_handler) = 0; virtual void async_shutdown( diff --git a/example/client/burl/connect.cpp b/example/client/burl/connect.cpp index 7ed265ba..b9855d17 100644 --- a/example/client/burl/connect.cpp +++ b/example/client/burl/connect.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include namespace core = boost::core; @@ -166,8 +166,8 @@ connect_http_proxy( // Connect to the proxy server co_await asio::async_connect(stream, rresults); - using field = http_proto::field; - auto request = http_proto::request{}; + using field = http::field; + auto request = http::request{}; auto host_port = [&]() { auto rs = url.encoded_host().decode(); @@ -176,7 +176,7 @@ connect_http_proxy( return rs; }(); - request.set_method(http_proto::method::connect); + request.set_method(http::method::connect); request.set_target(host_port); request.set(field::host, host_port); request.set(field::proxy_connection, "keep-alive"); @@ -190,8 +190,8 @@ connect_http_proxy( request.set(field::proxy_authorization, basic_auth); } - auto serializer = http_proto::serializer{ capy_ctx }; - auto parser = http_proto::response_parser{ capy_ctx }; + auto serializer = http::serializer{ capy_ctx }; + auto parser = http::response_parser{ capy_ctx }; serializer.start(request); co_await beast2::async_write(stream, serializer); @@ -200,7 +200,7 @@ connect_http_proxy( parser.start(); co_await beast2::async_read_header(stream, parser); - if(parser.get().status() != http_proto::status::ok) + if(parser.get().status() != http::status::ok) throw std::runtime_error{ "Proxy server rejected the connection" }; } diff --git a/example/client/burl/main.cpp b/example/client/burl/main.cpp index 54139594..1f318507 100644 --- a/example/client/burl/main.cpp +++ b/example/client/burl/main.cpp @@ -28,9 +28,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -41,6 +41,7 @@ #include namespace beast2 = boost::beast2; +namespace capy = boost::capy; namespace scope = boost::scope; using system_error = boost::system::system_error; @@ -59,7 +60,7 @@ constexpr bool capy_has_brotli = false; void set_target( const operation_config& oc, - http_proto::request& request, + http::request& request, const urls::url_view& url) { if(oc.request_target) @@ -78,21 +79,21 @@ struct is_redirect_result }; is_redirect_result -is_redirect(const operation_config& oc, http_proto::status status) noexcept +is_redirect(const operation_config& oc, http::status status) noexcept { // The specifications do not intend for 301 and 302 // redirects to change the HTTP method, but most // user agents do change the method in practice. switch(status) { - case http_proto::status::moved_permanently: + case http::status::moved_permanently: return { true, !oc.post301 }; - case http_proto::status::found: + case http::status::found: return { true, !oc.post302 }; - case http_proto::status::see_other: + case http::status::see_other: return { true, !oc.post303 }; - case http_proto::status::temporary_redirect: - case http_proto::status::permanent_redirect: + case http::status::temporary_redirect: + case http::status::permanent_redirect: return { true, false }; default: return { false, false }; @@ -100,16 +101,16 @@ is_redirect(const operation_config& oc, http_proto::status status) noexcept } bool -is_transient_error(http_proto::status status) noexcept +is_transient_error(http::status status) noexcept { switch(status) { - case http_proto::status::request_timeout: - case http_proto::status::too_many_requests: - case http_proto::status::internal_server_error: - case http_proto::status::bad_gateway: - case http_proto::status::service_unavailable: - case http_proto::status::gateway_timeout: + case http::status::request_timeout: + case http::status::too_many_requests: + case http::status::internal_server_error: + case http::status::bad_gateway: + case http::status::service_unavailable: + case http::status::gateway_timeout: return true; default: return false; @@ -118,14 +119,14 @@ is_transient_error(http_proto::status status) noexcept bool can_reuse_connection( - http_proto::response_base const& response, + http::response_base const& response, const urls::url_view& a, const urls::url_view& b) noexcept { if(a.encoded_origin() != b.encoded_origin()) return false; - if(response.version() != http_proto::version::http_1_1) + if(response.version() != http::version::http_1_1) return false; if(response.metadata().connection.close) @@ -137,28 +138,28 @@ can_reuse_connection( bool should_ignore_body( const operation_config& oc, - http_proto::response_base const& response) noexcept + http::response_base const& response) noexcept { - if(oc.resume_from && !response.count(http_proto::field::content_range)) + if(oc.resume_from && !response.count(http::field::content_range)) return true; return false; } boost::optional -body_size(http_proto::response_base const& response) +body_size(http::response_base const& response) { - if(response.payload() == http_proto::payload::size) + if(response.payload() == http::payload::size) return response.payload_size(); return boost::none; } urls::url redirect_url( - http_proto::response_base const& response, + http::response_base const& response, const urls::url_view& referer) { - auto it = response.find(http_proto::field::location); + auto it = response.find(http::field::location); if(it != response.end()) { auto rs = urls::parse_uri_reference(it->value); @@ -225,21 +226,21 @@ report_progress(progress_meter& pm) } } -http_proto::request +http::request create_request( const operation_config& oc, const message& msg, const urls::url_view& url) { - using field = http_proto::field; - using method = http_proto::method; - using version = http_proto::version; + using field = http::field; + using method = http::method; + using version = http::version; if(oc.disallow_username_in_url && url.has_userinfo()) throw std::runtime_error( "Credentials was passed in the URL when prohibited"); - auto request = http_proto::request{}; + auto request = http::request{}; request.set_method(oc.no_body ? method::head : method::get); @@ -308,7 +309,7 @@ create_request( return request; } -class sink : public http_proto::sink +class sink : public http::sink { progress_meter* pm_; any_ostream* os_; @@ -323,7 +324,7 @@ class sink : public http_proto::sink } results - on_write(buffers::const_buffer cb, bool) override + on_write(capy::const_buffer cb, bool) override { auto chunk = core::string_view(static_cast(cb.data()), cb.size()); @@ -337,7 +338,7 @@ class sink : public http_proto::sink } }; -asio::awaitable +asio::awaitable perform_request( operation_config oc, boost::optional& header_output, @@ -348,11 +349,11 @@ perform_request( message msg, request_opt request_opt) { - using field = http_proto::field; + using field = http::field; auto executor = co_await asio::this_coro::executor; auto stream = any_stream{ asio::ip::tcp::socket{ executor } }; - auto parser = http_proto::response_parser{ capy_ctx }; - auto serializer = http_proto::serializer{ capy_ctx }; + auto parser = http::response_parser{ capy_ctx }; + auto serializer = http::serializer{ capy_ctx }; urls::url url = [&]() { @@ -440,7 +441,7 @@ perform_request( }(); if(oc.skip_existing && fs::exists(output_path)) - co_return http_proto::status::ok; + co_return http::status::ok; auto output = any_ostream{ output_path, !!oc.resume_from }; auto request = create_request(oc, msg, url); @@ -474,7 +475,7 @@ perform_request( stream.write_limit(oc.sendpersecond.value()); }; - auto stream_headers = [&](http_proto::response_base const& response) + auto stream_headers = [&](http::response_base const& response) { if(oc.show_headers) output << response.buffer(); @@ -518,7 +519,7 @@ perform_request( set_cookies(url, trusted); msg.start_serializer(serializer, request); - if(request.method() == http_proto::method::head) + if(request.method() == http::method::head) parser.start_head_response(); else parser.start(); @@ -562,9 +563,9 @@ perform_request( } // Change the method according to RFC 9110, Section 15.4.4. - if(need_method_change && request.method() != http_proto::method::head) + if(need_method_change && request.method() != http::method::head) { - request.set_method(http_proto::method::get); + request.set_method(http::method::get); request.erase(field::content_length); request.erase(field::content_encoding); request.erase(field::content_type); @@ -618,7 +619,7 @@ perform_request( } if(oc.resume_from && - parser.get().status() != http_proto::status::range_not_satisfiable && + parser.get().status() != http::status::range_not_satisfiable && parser.get().count(field::content_range) == 0) { throw std::runtime_error( @@ -663,7 +664,7 @@ perform_request( asio::awaitable retry( const operation_config& oc, - std::function()> request_task) + std::function()> request_task) { auto executor = co_await asio::this_coro::executor; auto timer = asio::steady_timer{ executor }; @@ -771,7 +772,7 @@ co_main(int argc, char* argv[]) // parser service { - http_proto::response_parser::config cfg; + http::response_parser::config cfg; cfg.body_limit = oc.max_filesize; cfg.min_buffer = 1024 * 1024; if constexpr(capy_has_brotli) @@ -785,14 +786,14 @@ co_main(int argc, char* argv[]) cfg.apply_gzip_decoder = true; capy::zlib::install_inflate_service(capy_ctx); } - http_proto::install_parser_service(capy_ctx, cfg); + http::install_parser_service(capy_ctx, cfg); } // serializer service { - http_proto::serializer::config cfg; + http::serializer::config cfg; cfg.payload_buffer = 1024 * 1024; - http_proto::install_serializer_service(capy_ctx, cfg); + http::install_serializer_service(capy_ctx, cfg); } if(!oc.headerfile.empty()) diff --git a/example/client/burl/message.cpp b/example/client/burl/message.cpp index 551ec9cc..d9601dc6 100644 --- a/example/client/burl/message.cpp +++ b/example/client/burl/message.cpp @@ -10,12 +10,14 @@ #include "message.hpp" #include "mime_type.hpp" -#include +#include +#include #include #include #include +namespace capy = boost::capy; namespace fs = std::filesystem; using system_error = boost::system::system_error; @@ -25,10 +27,10 @@ string_body::string_body(std::string body, std::string content_type) { } -http_proto::method +http::method string_body::method() const noexcept { - return http_proto::method::post; + return http::method::post; } core::string_view @@ -43,7 +45,7 @@ string_body::content_length() const noexcept return body_.size(); } -buffers::const_buffer +capy::const_buffer string_body::body() const noexcept { return { body_.data(), body_.size() }; @@ -56,10 +58,10 @@ file_body::file_body(std::string path) { } -http_proto::method +http::method file_body::method() const noexcept { - return http_proto::method::put; + return http::method::put; } core::string_view @@ -74,22 +76,22 @@ file_body::content_length() const return fs::file_size(path_); } -http_proto::file_source +http::file_source file_body::body() const { - http_proto::file file; + boost::capy::file file; error_code ec; - file.open(path_.c_str(), http_proto::file_mode::read, ec); + file.open(path_.c_str(), boost::capy::file_mode::read, ec); if(ec) throw system_error{ ec }; - return http_proto::file_source{ std::move(file), content_length() }; + return http::file_source{ std::move(file), content_length() }; } // ----------------------------------------------------------------------------- -boost::http_proto::source::results -stdin_body::source::on_read(buffers::mutable_buffer mb) +boost::http::source::results +stdin_body::source::on_read(capy::mutable_buffer mb) { std::cin.read(static_cast(mb.data()), mb.size()); @@ -98,10 +100,10 @@ stdin_body::source::on_read(buffers::mutable_buffer mb) .finished = std::cin.eof() }; } -http_proto::method +http::method stdin_body::method() const noexcept { - return http_proto::method::put; + return http::method::put; } core::string_view @@ -125,12 +127,12 @@ stdin_body::body() const // ----------------------------------------------------------------------------- void -message::set_headers(http_proto::request& request) const +message::set_headers(http::request& request) const { std::visit( [&](auto& f) { - using field = http_proto::field; + using field = http::field; if constexpr(!std::is_same_v) { request.set_method(f.method()); @@ -142,7 +144,7 @@ message::set_headers(http_proto::request& request) const { request.set_content_length(content_length.value()); if(content_length.value() >= 1024 * 1024 && - request.version() == http_proto::version::http_1_1) + request.version() == http::version::http_1_1) request.set(field::expect, "100-continue"); } else @@ -157,8 +159,8 @@ message::set_headers(http_proto::request& request) const void message::start_serializer( - http_proto::serializer& serializer, - http_proto::request& request) const + http::serializer& serializer, + http::request& request) const { std::visit( [&](auto& f) diff --git a/example/client/burl/message.hpp b/example/client/burl/message.hpp index 8b6725ff..3d3caef4 100644 --- a/example/client/burl/message.hpp +++ b/example/client/burl/message.hpp @@ -12,9 +12,9 @@ #include "multipart_form.hpp" -#include -#include -#include +#include +#include +#include #include @@ -28,7 +28,7 @@ class string_body public: string_body(std::string body, std::string content_type); - http_proto::method + http::method method() const noexcept; core::string_view @@ -37,7 +37,7 @@ class string_body std::size_t content_length() const noexcept; - buffers::const_buffer + capy::const_buffer body() const noexcept; }; @@ -48,7 +48,7 @@ class file_body public: file_body(std::string path); - http_proto::method + http::method method() const noexcept; core::string_view @@ -57,21 +57,21 @@ class file_body std::uint64_t content_length() const; - http_proto::file_source + http::file_source body() const; }; class stdin_body { public: - class source : public http_proto::source + class source : public http::source { public: results - on_read(buffers::mutable_buffer mb) override; + on_read(capy::mutable_buffer mb) override; }; - http_proto::method + http::method method() const noexcept; core::string_view @@ -106,12 +106,12 @@ class message } void - set_headers(http_proto::request& request) const; + set_headers(http::request& request) const; void start_serializer( - http_proto::serializer& serializer, - http_proto::request& request) const; + http::serializer& serializer, + http::request& request) const; }; #endif diff --git a/example/client/burl/multipart_form.cpp b/example/client/burl/multipart_form.cpp index 02ad288c..16c0edab 100644 --- a/example/client/burl/multipart_form.cpp +++ b/example/client/burl/multipart_form.cpp @@ -9,14 +9,15 @@ #include "multipart_form.hpp" -#include -#include -#include +#include +#include +#include #include #include #include +namespace capy = boost::capy; namespace core = boost::core; namespace fs = std::filesystem; using system_error = boost::system::system_error; @@ -83,10 +84,10 @@ multipart_form::append( serialize_headers(headers) }); } -http_proto::method +http::method multipart_form::method() const noexcept { - return http_proto::method::post; + return http::method::post; } std::string @@ -147,18 +148,18 @@ multipart_form::source::source(const multipart_form* form) noexcept } multipart_form::source::results -multipart_form::source::on_read(buffers::mutable_buffer mb) +multipart_form::source::on_read(capy::mutable_buffer mb) { auto rs = results{}; auto copy = [&](core::string_view sv) { - buffers::const_buffer source(sv.data(), sv.size()); - buffers::remove_prefix(mb, static_cast(skip_)); + capy::const_buffer source(sv.data(), sv.size()); + capy::remove_prefix(mb, static_cast(skip_)); - auto copied = buffers::copy(mb, source); + auto copied = capy::copy(mb, source); - buffers::remove_prefix(mb, copied); + capy::remove_prefix(mb, copied); rs.bytes += copied; skip_ += copied; @@ -171,9 +172,9 @@ multipart_form::source::on_read(buffers::mutable_buffer mb) auto read = [&](const std::string& path, uint64_t size) { - http_proto::file file; + capy::file file; - file.open(path.c_str(), http_proto::file_mode::read, rs.ec); + file.open(path.c_str(), capy::file_mode::read, rs.ec); if(rs.ec) return false; @@ -188,7 +189,7 @@ multipart_form::source::on_read(buffers::mutable_buffer mb) if(rs.ec) return false; - buffers::remove_prefix(mb, read); + capy::remove_prefix(mb, read); rs.bytes += read; skip_ += read; diff --git a/example/client/burl/multipart_form.hpp b/example/client/burl/multipart_form.hpp index 0bce3932..09a0068f 100644 --- a/example/client/burl/multipart_form.hpp +++ b/example/client/burl/multipart_form.hpp @@ -10,18 +10,18 @@ #ifndef BURL_MULTIPART_FORM_HPP #define BURL_MULTIPART_FORM_HPP -#include +#include #include -#include -#include +#include +#include #include #include #include #include -namespace buffers = boost::buffers; -namespace http_proto = boost::http_proto; +namespace capy = boost::capy; +namespace http = boost::http; using error_code = boost::system::error_code; class multipart_form @@ -55,7 +55,7 @@ class multipart_form boost::optional content_type = {}, std::vector headers = {}); - http_proto::method + http::method method() const noexcept; std::string @@ -68,7 +68,7 @@ class multipart_form body() const; }; -class multipart_form::source : public http_proto::source +class multipart_form::source : public http::source { const multipart_form* form_; std::vector::const_iterator it_{ form_->pacapy_.begin() }; @@ -79,7 +79,7 @@ class multipart_form::source : public http_proto::source explicit source(const multipart_form* form) noexcept; results - on_read(buffers::mutable_buffer mb) override; + on_read(capy::mutable_buffer mb) override; }; #endif diff --git a/example/client/burl/options.hpp b/example/client/burl/options.hpp index 9aed639c..feb287a6 100644 --- a/example/client/burl/options.hpp +++ b/example/client/burl/options.hpp @@ -13,7 +13,7 @@ #include "message.hpp" #include -#include +#include #include #include @@ -26,7 +26,7 @@ namespace asio = boost::asio; namespace ch = std::chrono; namespace fs = std::filesystem; -namespace http_proto = boost::http_proto; +namespace http = boost::http; namespace urls = boost::urls; struct operation_config @@ -80,7 +80,7 @@ struct operation_config bool rm_partial = false; bool use_httpget = false; boost::optional request_target; - http_proto::fields headers; + http::fields headers; std::vector omitheaders; bool show_headers = false; bool cookiesession = false; diff --git a/example/client/burl/request.hpp b/example/client/burl/request.hpp index fb54aa6c..2665d651 100644 --- a/example/client/burl/request.hpp +++ b/example/client/burl/request.hpp @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -25,15 +25,15 @@ namespace asio = boost::asio; namespace ch = std::chrono; namespace beast2 = boost::beast2; -namespace http_proto = boost::http_proto; +namespace http = boost::http; using error_code = boost::system::error_code; template class async_request_op { AsyncReadStream& stream_; - http_proto::serializer& serializer_; - http_proto::response_parser& parser_; + http::serializer& serializer_; + http::response_parser& parser_; ch::steady_clock::duration exp100_timeout_; asio::coroutine c; @@ -52,8 +52,8 @@ class async_request_op public: async_request_op( AsyncReadStream& stream, - http_proto::serializer& serializer, - http_proto::response_parser& parser, + http::serializer& serializer, + http::response_parser& parser, ch::steady_clock::duration exp100_timeout) : stream_{ stream } , serializer_{ serializer } @@ -83,7 +83,7 @@ class async_request_op return self.complete(ec); } - if(ec != http_proto::error::expect_100_continue) + if(ec != http::error::expect_100_continue) return self.complete(ec); // TODO: use associated allocator @@ -115,7 +115,7 @@ class async_request_op exp100->timer.cancel(); if(ec || parser.get().status() != - http_proto::status::continue_) + http::status::continue_) { exp100->state = exp100::cancelled; } @@ -156,8 +156,8 @@ template< auto async_request( AsyncReadStream& stream, - http_proto::serializer& serializer, - http_proto::response_parser& parser, + http::serializer& serializer, + http::response_parser& parser, ch::steady_clock::duration expect100_timeout, CompletionToken&& token = CompletionToken{}) { diff --git a/example/client/get/main.cpp b/example/client/get/main.cpp index 4c2815e1..a9bca59b 100644 --- a/example/client/get/main.cpp +++ b/example/client/get/main.cpp @@ -14,9 +14,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -37,9 +37,9 @@ class session asio::ssl::context& ssl_ctx_; asio::ssl::stream stream_; asio::ip::tcp::resolver resolver_; - http_proto::serializer sr_; - http_proto::response_parser pr_; - http_proto::request req_; + http::serializer sr_; + http::response_parser pr_; + http::request req_; urls::url url_; std::string host_; std::string port_; @@ -62,7 +62,7 @@ class session void run(urls::url_view url) { - using field = http_proto::field; + using field = http::field; // Set up an HTTP GET request if(!url.encoded_target().empty()) @@ -251,9 +251,9 @@ class session void on_redirect_response( - http_proto::response_base const& response) + http::response_base const& response) { - using field = http_proto::field; + using field = http::field; if(max_redirects_ == 0) return fail("Maximum redirects followed"); @@ -367,9 +367,9 @@ class session static bool - is_redirect(http_proto::status s) noexcept + is_redirect(http::status s) noexcept { - using status = http_proto::status; + using status = http::status; switch(s) { case status::moved_permanently: @@ -390,14 +390,14 @@ class session static bool can_reuse_connection( - http_proto::response_base const& response, + http::response_base const& response, urls::url_view a, urls::url_view b) noexcept { if(a.encoded_origin() != b.encoded_origin()) return false; - if(response.version() != http_proto::version::http_1_1) + if(response.version() != http::version::http_1_1) return false; if(response.metadata().connection.close) @@ -407,10 +407,10 @@ class session } // Writes body to standard out - struct stdout_sink : http_proto::sink + struct stdout_sink : http::sink { results - on_write(buffers::const_buffer cb, bool) override + on_write(capy::const_buffer cb, bool) override { std::cout.write( static_cast(cb.data()), cb.size()); @@ -446,7 +446,7 @@ main(int argc, char* argv[]) // Install parser service { - http_proto::response_parser::config cfg; + http::response_parser::config cfg; cfg.body_limit = std::uint64_t(-1); cfg.min_buffer = 64 * 1024; #ifdef BOOST_CAPY_HAS_BROTLI @@ -458,11 +458,11 @@ main(int argc, char* argv[]) cfg.apply_gzip_decoder = true; capy::zlib::install_inflate_service(capy_ctx); #endif - http_proto::install_parser_service(capy_ctx, cfg); + http::install_parser_service(capy_ctx, cfg); } // Install serializer service with default configuration - http_proto::install_serializer_service(capy_ctx, {}); + http::install_serializer_service(capy_ctx, {}); // Root certificates used for verification ssl_ctx.set_default_verify_paths(); diff --git a/example/client/jsonrpc/CMakeLists.txt b/example/client/jsonrpc/CMakeLists.txt index d3c4b40f..998f4895 100644 --- a/example/client/jsonrpc/CMakeLists.txt +++ b/example/client/jsonrpc/CMakeLists.txt @@ -37,32 +37,15 @@ if (WIN32) target_link_libraries(beast2_example_client_jsonrpc_lib PUBLIC crypt32) endif() -# CPP11 Example -add_executable(beast2_example_client_jsonrpc_cpp11 cpp11.cpp eth_methods.hpp Jamfile) -set_property(TARGET beast2_example_client_jsonrpc_cpp11 +add_executable(beast2_example_client_jsonrpc main.cpp eth_methods.hpp Jamfile) +set_property(TARGET beast2_example_client_jsonrpc PROPERTY FOLDER "examples") -target_link_libraries(beast2_example_client_jsonrpc_cpp11 +target_link_libraries(beast2_example_client_jsonrpc PRIVATE beast2_example_client_jsonrpc_lib) if (TARGET Boost::capy_zlib) - target_link_libraries(beast2_example_client_jsonrpc_cpp11 PRIVATE Boost::capy_zlib) + target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::capy_zlib) endif() if (TARGET Boost::capy_brotli) - target_link_libraries(beast2_example_client_jsonrpc_cpp11 PRIVATE Boost::capy_brotli) + target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::capy_brotli) endif() - -# CPP20 Example -if (CMAKE_CXX_STANDARD EQUAL 20) - add_executable(beast2_example_client_jsonrpc_cpp20 cpp20.cpp eth_methods.hpp Jamfile) - set_property(TARGET beast2_example_client_jsonrpc_cpp20 - PROPERTY FOLDER "examples") - target_link_libraries(beast2_example_client_jsonrpc_cpp20 - PRIVATE - beast2_example_client_jsonrpc_lib) - if (TARGET Boost::capy_zlib) - target_link_libraries(beast2_example_client_jsonrpc_cpp20 PRIVATE Boost::capy_zlib) - endif() - if (TARGET Boost::capy_brotli) - target_link_libraries(beast2_example_client_jsonrpc_cpp20 PRIVATE Boost::capy_brotli) - endif() -endif () diff --git a/example/client/jsonrpc/Jamfile b/example/client/jsonrpc/Jamfile index 8cf688b9..e828aa44 100644 --- a/example/client/jsonrpc/Jamfile +++ b/example/client/jsonrpc/Jamfile @@ -33,16 +33,7 @@ project . ; -exe cpp11 : - cpp11.cpp +exe jsonrpc_example : + main.cpp [ glob jsonrpc/*.cpp ] ; - -exe cpp20 : - cpp20.cpp - [ glob jsonrpc/*.cpp ] - : requirements - [ requires - cxx20_hdr_coroutine - ] - ; diff --git a/example/client/jsonrpc/cpp11.cpp b/example/client/jsonrpc/cpp11.cpp deleted file mode 100644 index 8bfa32bb..00000000 --- a/example/client/jsonrpc/cpp11.cpp +++ /dev/null @@ -1,277 +0,0 @@ -// -// Copyright (c) 2025 Mohammad Nejati -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include "jsonrpc/client.hpp" - -#include "eth_methods.hpp" - -#include -#include -#include -#include - -#include -#include - -using namespace boost; -using namespace std::placeholders; - -// Bring Ethereum methods into scope -using namespace eth_methods; - -class session -{ - jsonrpc::client client_; - -public: - session( - asio::io_context& ioc, - asio::ssl::context& ssl_ctx, - capy::polystore& ctx) - : client_( - urls::url("https://ethereum.publicnode.com"), - ctx, - ioc.get_executor(), - ssl_ctx) - { - } - - void - run() - { - // Set the user agent - client_.http_fields().set( - http_proto::field::user_agent, "Boost.Http.Io"); - - // Connect to the endpoint - client_.async_connect( - std::bind(&session::on_connect, this, _1)); - } - -private: - void - on_connect(system::error_code ec) - { - if(ec) - return fail(ec, "connect"); - - // Get Ethereum node client software and version - client_.async_call( - web3_clientVersion, - std::bind(&session::on_clientVersion, this, _1, _2)); - } - - void - on_clientVersion( - jsonrpc::error error, json::string version) - { - if(error.code()) - return fail(error); - - std::cout - << "web3 client: " - << version << '\n'; - - // Get the latest block number - client_.async_call( - eth_blockNumber, - std::bind(&session::on_blockNumber, this, _1, _2)); - } - - void - on_blockNumber( - jsonrpc::error error, json::string block_num) - { - if(error.code()) - return fail(error); - - std::cout - << "Block height: " - << block_num << '\n'; - - // Get block details - client_.async_call( - eth_getBlockByNumber, - { block_num, false }, - std::bind(&session::on_getBlockByNumber, this, _1, _2)); - } - - void - on_getBlockByNumber( - jsonrpc::error error, json::object block) - { - if(error.code()) - return fail(error); - - std::cout<< "Block hash: " << block["hash"] << '\n'; - std::cout<< "Block size: " << block["size"] << " Bytes\n"; - std::cout<< "Timestamp: " << block["timestamp"] << '\n'; - std::cout<< "Transactions: " << block["transactions"].as_array().size() << '\n'; - - // Get account balance - client_.async_call( - eth_getBalance, - { - "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", - block["number"] - }, - std::bind(&session::on_getBalance, this, _1, _2)); - } - - void - on_getBalance( - jsonrpc::error error, json::string balance) - { - if(error.code()) - return fail(error); - - std::cout - << "Balance: " - << balance << '\n'; - - // Estimate gas for a transfer - client_.async_call( - eth_estimateGas, - { - json::object - { - { "from", "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae" }, - { "to", "0x281055afc982d96fab65b3a49cac8b878184cb16" }, - { "value", "0x2386F26FC10000" } // 0.01 ETH in wei - } - }, - std::bind(&session::on_estimateGas, this, _1, _2)); - } - - void - on_estimateGas( - jsonrpc::error error, json::string gas_estimate) - { - if(error.code()) - return fail(error); - - std::cout - << "Gas estimate: " - << gas_estimate << '\n'; - - // Get the current gas price - client_.async_call( - eth_gasPrice, - std::bind(&session::on_gasPrice, this, _1, _2)); - } - - void - on_gasPrice( - jsonrpc::error error, json::string gas_price) - { - if(error.code()) - return fail(error); - - std::cout - << "Gas price: " - << gas_price << '\n'; - - // Gracefully close the stream - client_.async_shutdown( - std::bind(&session::on_shutdown, this, _1)); - } - - void - on_shutdown(system::error_code ec) - { - if(ec && ec != asio::ssl::error::stream_truncated) - return fail(ec, "shutdown"); - } - - static - void - fail(system::error_code ec, const char* operation) - { - std::cerr - << operation << ": " - << ec.message() << "\n"; - } - - static - void - fail(const jsonrpc::error& error) - { - if(!error.info().empty()) - std::cerr << error.info() << " "; - std::cerr << error.code().what() << "\n"; - } -}; - -/* -Sample output: - -web3 client: "erigon/3.0.4/linux-amd64/go1.23.9" -Block height: "0x161a027" -Block hash: "0xe35f0fb6199e295ff7ba864b034fbbcf3f722b089cf5fe18181f5a83352b2645" -Block size: "0x1604d" Bytes -Timestamp: "0x68a47207" -Transactions: 201 -Balance: "0x2647cc23d6974bdb8179" -Gas estimate: "0x5208" -Gas price: "0x1c9ad53b" -*/ - -int -main(int, char*[]) -{ - try - { - // The io_context is required for all I/O - asio::io_context ioc; - - // The SSL context is required, and holds certificates - asio::ssl::context ssl_ctx(asio::ssl::context::tls_client); - - // holds optional deflate and - // required configuration services - capy::polystore capy_ctx; - - // Install parser service - { - http_proto::response_parser::config cfg; - cfg.min_buffer = 64 * 1024; - #ifdef BOOST_CAPY_HAS_BROTLI - cfg.apply_brotli_decoder = true; - capy::brotli::install_decode_service(capy_ctx); - #endif - #ifdef BOOST_CAPY_HAS_ZLIB - cfg.apply_deflate_decoder = true; - cfg.apply_gzip_decoder = true; - capy::zlib::install_inflate_service(capy_ctx); - #endif - http_proto::install_parser_service(capy_ctx, cfg); - } - - // Install serializer service with default configuration - http_proto::install_serializer_service(capy_ctx, {}); - - // Root certificates used for verification - ssl_ctx.set_default_verify_paths(); - - // Verify the remote server's certificate - ssl_ctx.set_verify_mode(asio::ssl::verify_peer); - - session s(ioc, ssl_ctx, capy_ctx); - s.run(); - - ioc.run(); - - return EXIT_SUCCESS; - } - catch(const std::exception& e) - { - std::cerr << "Error: " << e.what() << std::endl; - return EXIT_FAILURE; - } -} diff --git a/example/client/jsonrpc/jsonrpc/any_stream.hpp b/example/client/jsonrpc/jsonrpc/any_stream.hpp index 4efecf61..8a8c7c59 100644 --- a/example/client/jsonrpc/jsonrpc/any_stream.hpp +++ b/example/client/jsonrpc/jsonrpc/any_stream.hpp @@ -11,7 +11,7 @@ #define JSONRPC_ANY_STREAM_HPP #include -#include +#include #include #include @@ -33,12 +33,12 @@ class any_stream virtual void async_write_some( - const boost::span&, + const boost::span&, boost::asio::any_completion_handler) = 0; virtual void async_read_some( - const boost::span&, + const boost::span&, boost::asio::any_completion_handler) = 0; virtual void diff --git a/example/client/jsonrpc/jsonrpc/client.cpp b/example/client/jsonrpc/jsonrpc/client.cpp index 86b658d7..5823c29a 100644 --- a/example/client/jsonrpc/jsonrpc/client.cpp +++ b/example/client/jsonrpc/jsonrpc/client.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -84,7 +84,7 @@ class stream_impl : public any_stream void async_write_some( - const boost::span& buffers, + const boost::span& buffers, asio::any_completion_handler handler) override { @@ -96,7 +96,7 @@ class stream_impl : public any_stream void async_read_some( - const boost::span& buffers, + const boost::span& buffers, asio::any_completion_handler handler) override { @@ -223,7 +223,7 @@ class stream_impl : public any_stream }; }; -class json_sink : public http_proto::sink +class json_sink : public http::sink { json::stream_parser& jpr_; @@ -236,7 +236,7 @@ class json_sink : public http_proto::sink private: results on_write( - buffers::const_buffer b, + capy::const_buffer b, bool more) override { results ret; @@ -250,7 +250,7 @@ class json_sink : public http_proto::sink } }; -class json_source : public http_proto::source +class json_source : public http::source { json::serializer& jsr_; json::value v_; @@ -266,7 +266,7 @@ class json_source : public http_proto::source private: results on_read( - buffers::mutable_buffer b) override + capy::mutable_buffer b) override { results ret; ret.bytes = jsr_.read( @@ -404,9 +404,9 @@ client( , endpoint_(std::move(endpoint)) , sr_(capy_ctx) , pr_(capy_ctx) - , req_(http_proto::method::post, "/") + , req_(http::method::post, "/") { - using field = http_proto::field; + using field = http::field; if(!endpoint_.encoded_target().empty()) req_.set_target(endpoint_.encoded_target()); diff --git a/example/client/jsonrpc/jsonrpc/client.hpp b/example/client/jsonrpc/jsonrpc/client.hpp index 49b09512..4265cfd4 100644 --- a/example/client/jsonrpc/jsonrpc/client.hpp +++ b/example/client/jsonrpc/jsonrpc/client.hpp @@ -19,9 +19,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -35,9 +35,9 @@ class client { std::unique_ptr stream_; boost::urls::url endpoint_; - boost::http_proto::serializer sr_; - boost::http_proto::response_parser pr_; - boost::http_proto::request req_; + boost::http::serializer sr_; + boost::http::response_parser pr_; + boost::http::request req_; boost::json::stream_parser jpr_; boost::json::serializer jsr_; std::uint64_t id_ = 0; @@ -104,7 +104,7 @@ class client This function can be used to customize HTTP headers, for example, to add the required credentials. */ - boost::http_proto::fields_base& + boost::http::fields_base& http_fields() { return req_; diff --git a/example/client/jsonrpc/cpp20.cpp b/example/client/jsonrpc/main.cpp similarity index 96% rename from example/client/jsonrpc/cpp20.cpp rename to example/client/jsonrpc/main.cpp index b4179b68..0b7c9f54 100644 --- a/example/client/jsonrpc/cpp20.cpp +++ b/example/client/jsonrpc/main.cpp @@ -151,7 +151,7 @@ main(int, char*[]) // Install parser service { - http_proto::response_parser::config cfg; + http::response_parser::config cfg; cfg.min_buffer = 64 * 1024; #ifdef BOOST_CAPY_HAS_BROTLI cfg.apply_brotli_decoder = true; @@ -162,11 +162,11 @@ main(int, char*[]) cfg.apply_gzip_decoder = true; capy::zlib::install_inflate_service(capy_ctx); #endif - http_proto::install_parser_service(capy_ctx, cfg); + http::install_parser_service(capy_ctx, cfg); } // Install serializer service with default configuration - http_proto::install_serializer_service(capy_ctx, {}); + http::install_serializer_service(capy_ctx, {}); // Root certificates used for verification ssl_ctx.set_default_verify_paths(); diff --git a/example/client/visit/main.cpp b/example/client/visit/main.cpp index 3796a63c..c5f3c15e 100644 --- a/example/client/visit/main.cpp +++ b/example/client/visit/main.cpp @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -196,7 +196,7 @@ struct worker boost::asio::ip::tcp, executor_type>; socket_type sock; - boost::http_proto::response_parser pr; + boost::http::response_parser pr; boost::urls::url_view url; explicit @@ -246,12 +246,12 @@ struct worker void do_request() { - boost::http_proto::request req; + boost::http::request req; auto path = url.encoded_path(); req.set_start_line( - boost::http_proto::method::get, + boost::http::method::get, path.empty() ? "/" : path, - boost::http_proto::version::http_1_1); + boost::http::version::http_1_1); do_shutdown(); } @@ -277,8 +277,8 @@ main(int argc, char* argv[]) (void)argv; boost::capy::polystore ctx; - boost::http_proto::parser::config_base cfg; - boost::http_proto::install_parser_service(ctx, cfg); + boost::http::parser::config_base cfg; + boost::http::install_parser_service(ctx, cfg); boost::asio::io_context ioc; diff --git a/example/post-rpc.html b/example/post-rpc.html new file mode 100644 index 00000000..136ff6e4 --- /dev/null +++ b/example/post-rpc.html @@ -0,0 +1,137 @@ + + + + + + JSON POST Client + + + +
+

JSON POST Client

+ + + + + + + + + +
+
+ + + + \ No newline at end of file diff --git a/example/post-test.html b/example/post-test.html new file mode 100644 index 00000000..13c86ee8 --- /dev/null +++ b/example/post-test.html @@ -0,0 +1,59 @@ + + + + POST Test + + + + + +

+
+    
+
+
\ No newline at end of file
diff --git a/example/server/CMakeLists.txt b/example/server/CMakeLists.txt
index 139c9213..f2f1508b 100644
--- a/example/server/CMakeLists.txt
+++ b/example/server/CMakeLists.txt
@@ -15,26 +15,17 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${PFILES})
 
 add_executable(beast2_server_example ${PFILES})
 
-target_compile_definitions(beast2_server_example
-    PRIVATE BOOST_ASIO_NO_DEPRECATED)
-
 set_property(TARGET beast2_server_example
     PROPERTY FOLDER "examples")
 
-find_package(OpenSSL REQUIRED)
 target_include_directories(beast2_server_example PRIVATE .)
 target_link_libraries(
     beast2_server_example
     Boost::beast2
+    Boost::json
     Boost::url
-    OpenSSL::SSL
-    OpenSSL::Crypto
     )
 
-if (WIN32)
-    target_link_libraries(beast2_server_example crypt32)
-endif()
-
 if (TARGET Boost::capy_zlib)
     target_link_libraries(beast2_server_example Boost::capy_zlib)
 endif()
@@ -42,4 +33,3 @@ endif()
 if (TARGET Boost::capy_brotli)
     target_link_libraries(beast2_server_example Boost::capy_brotli)
 endif()
-
diff --git a/example/server/Jamfile b/example/server/Jamfile
index 93255dd3..d9e15c1c 100644
--- a/example/server/Jamfile
+++ b/example/server/Jamfile
@@ -5,29 +5,18 @@
 # Distributed under the Boost Software License, Version 1.0. (See accompanying
 # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 #
-# Official repository: https://github.com/CPPAlliance/http_proto
+# Official repository: https://github.com/CPPAlliance/http
 #
 
-using openssl ;
 import ac ;
 
-lib advapi32 ;
-lib crypt32 ;
-lib gdi32 ;
-lib user32 ;
-
 project
     : requirements
       /boost/beast2//boost_beast2
       /boost/url//boost_url
+      /boost/json//boost_json
       [ ac.check-library /boost/capy//boost_capy_zlib : /boost/capy//boost_capy_zlib : ]
       [ ac.check-library /boost/capy//boost_capy_brotli : /boost/capy//boost_capy_brotli : ]
-      [ ac.check-library /openssl//ssl : /openssl//ssl/shared : no ]
-      [ ac.check-library /openssl//crypto : /openssl//crypto/shared : no ]
-      windows:advapi32
-      windows:crypt32
-      windows:gdi32
-      windows:user32
       .
     ;
 
diff --git a/example/server/certificate.cpp b/example/server/certificate.cpp
deleted file mode 100644
index 1e3e4dd4..00000000
--- a/example/server/certificate.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#include "certificate.hpp"
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-void
-load_server_certificate(
-    asio::ssl::context& ctx)
-{
-/*
-    Using Windows with OpenSSL version "1.1.1s  1 Nov 2022"
-
-    1. Generate a Root CA
-
-        openssl genrsa -out rootCA.key 4096
-        openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 10000 -out rootCA.pem -subj "//CN=Boost Test Root CA"
-
-    2. Create a Server Key
-
-        openssl genrsa -out server.key 2048
-*/
-    using keypair = std::pair;
-
-    // certificates for SSL listening ports
-    keypair const certs[] = {
-{
-/*  Create a Signed Server Certificate with the Test Root CA
-
-    openssl req -new -key server.key -out server.csr -subj "//CN=localhost"
-    openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 10000 -sha256 -extfile san.cnf
-*/
-    "-----BEGIN CERTIFICATE-----\n"
-    "MIID2TCCAcGgAwIBAgIURQ6waOrVlt/YykIgwb+46o0UtCUwDQYJKoZIhvcNAQEL\n"
-    "BQAwHTEbMBkGA1UEAwwSQm9vc3QgVGVzdCBSb290IENBMCAXDTI1MTAxMjAxMjIx\n"
-    "N1oYDzIwNTMwMjI3MDEyMjE3WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0G\n"
-    "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkHGuSplDQrW5f0JJwUK9UivHZMkE5\n"
-    "CDEThsUwjrqubofLpR49EzfAcBRWWQ1R0QmzK2sKnKQnku4IliFyitw/OAHsJrr4\n"
-    "c3OHXfpOOwtd4Kg3BP3P3oeAsO+IELrQIsJp/mrjOJKtBVTZ8kl5ZYrf94fEMivn\n"
-    "JnZ+neb2kPiSPTnAtFSBVSQc9aHU7Wg1gtQkUuEIkjUBvPzxGKi0m3nuZfUDJpev\n"
-    "2OB7fRftIPjiqZ/1n1k2CYLqLMBIXAeeYAjBgzM0x4UG3SW7jlPeoDI34XQ7dYxQ\n"
-    "K5jjs3OhoLs5x0za1sZ7MXkDRAqO5Cgeg3kNb5VlhjVzR8Njtapx4mXtAgMBAAGj\n"
-    "GDAWMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAgEAjEHn\n"
-    "AIfxiYXWBsVPtYsRbHzWYoNSIWGkzwauMaDPDGzeMV1ajKV1dBp8NHKBg/jlKdaQ\n"
-    "vGiKHLlPwSGRlDIyyglG1qsH7unQHV0w+cCh19Uoc0gtv9q4zTUuyDhk8eufIdEr\n"
-    "1SPNMIqJQ47A2KrYo31rd+HxnyoCit1fM8SUWwM0tLaoEM3iTF33LI5CBkT2VPbv\n"
-    "qJL/68qQFDeTIUGQJjPK9rs/cElqsweVfWF+O2Mn9wA9aJyc5+0jsOGrfADO/cI1\n"
-    "OA1HXgawSQjXKST74aprd0gBxACXrG+wA1G6NNsawp80xjADSTyCfNxtQVvY1W2n\n"
-    "9kJuionCnDUnhqEOnhnZdq1XikoOYMRJP0BxFHX8SNbxmJouJRWvusmh91lAgbQv\n"
-    "JVXlHcEQGYMIYIr5y6dLlZ55SamC9aK3vO+q8s9AEYZO4OTUi2WQ2bfrxcn3vI/0\n"
-    "UzLh8LzE1A2OT5Gin0jWKbeKpqXH2RAfgaEAf1bzDA92xMEjVGBrBoXagvUOGzMG\n"
-    "cDcj4WHzcO6BWENPkRZ2JNNIxIZRK/wr9Mw/q/Yu9iDNVw4XOEx+iGjh1cRHy1FH\n"
-    "VuS/N5CQCBnmhDKYrZ3Lz+D/l0CGc30A1jjfvlrDiE4k9NyrV8wVHqmO73vivAvW\n"
-    "hYG9wLo13LnWG9JrtT8Drl/H0YbuL4C46n/mua8=\n"
-    "-----END CERTIFICATE-----\n",
-
-    "-----BEGIN RSA PRIVATE KEY-----\n"
-    "MIIEpgIBAAKCAQEA5BxrkqZQ0K1uX9CScFCvVIrx2TJBOQgxE4bFMI66rm6Hy6Ue\n"
-    "PRM3wHAUVlkNUdEJsytrCpykJ5LuCJYhcorcPzgB7Ca6+HNzh136TjsLXeCoNwT9\n"
-    "z96HgLDviBC60CLCaf5q4ziSrQVU2fJJeWWK3/eHxDIr5yZ2fp3m9pD4kj05wLRU\n"
-    "gVUkHPWh1O1oNYLUJFLhCJI1Abz88RiotJt57mX1AyaXr9jge30X7SD44qmf9Z9Z\n"
-    "NgmC6izASFwHnmAIwYMzNMeFBt0lu45T3qAyN+F0O3WMUCuY47NzoaC7OcdM2tbG\n"
-    "ezF5A0QKjuQoHoN5DW+VZYY1c0fDY7WqceJl7QIDAQABAoIBAQCfXyvZPdHgugsP\n"
-    "bk2hov2cd6cZNH9VNV/0YIiMsGvFSvwdT7OcwDyHesb6vSUNMJsyTvduZppZ+9HK\n"
-    "tfmQaWwPzzWopDalNyRUQ1iKJ759TGS6bAZYoQTS6MuxqN6cZGyoWVScg/4WXE84\n"
-    "JosnAcbRS8PTU6pQyRKoy/F9+zNwF30B0GwQpNwEZMX7QvPQPoUIcurE8HmP7NhB\n"
-    "fuZn/af6q9N+D56fOaD7Rg4kdtBMa0cv4mKojaq3ymx5RK8ccPTDiXC0idg7uRVC\n"
-    "13sFf9H46Z7kIimyhPUrwTapMyrx1kjQNZa97YYBAHGfLwRoYsGu5XMWIXKDkZHz\n"
-    "Bt2U92iBAoGBAPJRdRqoHdZIe42rq1kEpIis4Eitn4idjuW6D8kgzPU8IT7UreT/\n"
-    "eS5c7ThnbwI3B966pHWzAPMiktc3sXQUIXlBy1vu1A6paqU+HpA9pbwB3tHfLsgm\n"
-    "akabqfv/nWpzKJt5kLlbNOkkCvJ8FvotyeCCrdCeip+q6d8NZc7s9YQZAoGBAPD9\n"
-    "m5nUHKR7tcE8SNc9pcBp1AoILuhp9XbUgP2YkePO8KfnHPUSU5LIT0c1sasm1k/w\n"
-    "ehpaqirjSAjYkjKQTfwj4SVP9LAlCb5kz0rhZJqdiQUtSK3oPIDzcXfutgZjRbyX\n"
-    "vR9r3a2DLoYJ/IxuajyvICImKSMHEySnCoxaEgr1AoGBALI2VGC5edAp2Kx1r/w1\n"
-    "HOjj88Of5a+s6PZtY8SxGevWQEEcW5QKi84cS97qu0quvFwDeoaRksY+DC66aAkN\n"
-    "8Rxj1jMTr+Pkl2lWCVZd8HEYEw7ZDGfpUMoDG/4YnWY3sYq+2kBoIr7AYki6GJAA\n"
-    "cvNqSHkg0KTjJ0ODb/fCcEKpAoGBALUX3rXaDywLSqnLA3G7gbL108E2JQnBlhOV\n"
-    "3Ni0rezitTV3FuuSufqzS9/XGYvjw2iO7TKgrv9Li/YZyML2baPr0mSXkOhM7OWG\n"
-    "G7/JYDBP8YdSYCtPOSgtyDa3y1FBiEYQQK48AHlC+tL+7ikZT/wKHbuLsZ4A0wHY\n"
-    "BLUzehuBAoGBAMC9mGnaYU4D9PjCqC96MyEnZjdpf5eGByx5GhV6O6kEx0wJbjPc\n"
-    "kq+QAn1cp1tVjPcNfOK62LxV5E5t4eCv/Su5dmQHWGVDZBrdug5osyLFaFZA8Lw/\n"
-    "GKCs22hqgjPqSrGnCXMV6bZkwvy3cmxNkWuEiiA67PYyc6aFpTcOj8fi\n"
-    "-----END RSA PRIVATE KEY-----\n"
-}
-    };
-
-/*  More secure Diffie-Helman parameters
-
-    openssl dhparam -out dh.pem 2048
-*/
-    std::string const dh =
-    "-----BEGIN DH PARAMETERS-----\n"
-    "MIIBCAKCAQEAu7R9qRNtiuayUH9FLFIIQJ9GmhKpdL/gcLG8+5/6x+RN+cgPwQgQ\n"
-    "FYqTtIHRgINxtxdZqUxnrcg6jbW13r7b8A7uWURsrW5T3Hy68v4SFY5F+c/a97m+\n"
-    "LyUHW12iwCqZPlwdl4Zvb/uAtrn3xjvl3Buea4nGPAlTlHVKR1OH8IuWPnxUvjXp\n"
-    "slcI5c20LQ3Z2znM3csLNGkgiGKIPLCb9Sq8Zx1+gCDQk9DjDC4K8ELDqvbwDz8m\n"
-    "760pgC5eQ0z1lgmxvRVgPZOx9twwO1/VhpISpGnb7vihEb+06jQtXZIC3LrANfhe\n"
-    "bnbac08nYv9yt7Caf2Zfy1UDvkeLtPYs2wIBAg==\n"
-    "-----END DH PARAMETERS-----\n";
-    
-    ctx.set_password_callback(
-        [](std::size_t,
-            asio::ssl::context_base::password_purpose)
-        {
-            return "test";
-        });
-
-    ctx.set_options(
-        asio::ssl::context::default_workarounds |
-        asio::ssl::context::no_sslv2 |
-        asio::ssl::context::single_dh_use);
-
-    for(auto const& t : certs)
-    {
-        ctx.use_certificate_chain(asio::buffer(t.first));
-
-        // use_private_key applies to the last inserted certificate,
-        // see: https://linux.die.net/man/3/ssl_ctx_use_privatekey
-        //
-        ctx.use_private_key(asio::buffer(t.second),
-            asio::ssl::context::file_format::pem);
-    }
-
-    ctx.use_tmp_dh(asio::buffer(dh));
-}
-
-} // beast2
-} // boost
diff --git a/example/server/certificate.hpp b/example/server/certificate.hpp
deleted file mode 100644
index 5bb4f7c9..00000000
--- a/example/server/certificate.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_EXAMPLE_SERVER_CERTIFICATE_HPP
-#define BOOST_BEAST2_EXAMPLE_SERVER_CERTIFICATE_HPP
-
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-/*  Load a signed certificate into the ssl context, and configure
-    the context for use with a server.
-
-    For this to work with the browser or operating system, it is
-    necessary to import the certificate in the file "beast-test-CA.crt",
-    which accompanies the library, into  the local certificate store,
-    browser, or operating system depending on your environment.
-*/
-void
-load_server_certificate(
-    asio::ssl::context& ctx);
-
-} // beast2
-} // boost
-
-#endif
diff --git a/example/server/main.cpp b/example/server/main.cpp
index af0b1543..6c1d0f7e 100644
--- a/example/server/main.cpp
+++ b/example/server/main.cpp
@@ -7,26 +7,34 @@
 // Official repository: https://github.com/cppalliance/beast2
 //
 
-#include "certificate.hpp"
-#include "serve_detached.hpp"
 #include "serve_log_admin.hpp"
-#include 
-#include 
+//#include 
 #include 
+#include 
 #include 
 #include 
 #include 
-#include 
-#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
+#include 
+
 namespace boost {
 namespace beast2 {
 
+capy::thread_pool g_tp;
+
 void install_services(capy::application& app)
 {
 #ifdef BOOST_CAPY_HAS_BROTLI
@@ -39,13 +47,107 @@ void install_services(capy::application& app)
     capy::zlib::install_inflate_service(app);
 #endif
 
-    // VFALCO These ugly incantations are needed for http_proto and will hopefully go away soon.
-    http_proto::install_parser_service(app,
-        http_proto::request_parser::config());
-    http_proto::install_serializer_service(app,
-        http_proto::serializer::config());
+    // VFALCO These ugly incantations are needed for http and will hopefully go away soon.
+    http::install_parser_service(app,
+        http::request_parser::config());
+    http::install_serializer_service(app,
+        http::serializer::config());
 }
 
+#if 0
+class json_sink : public http::sink
+{
+public:
+    json_sink(json_sink&&) = default;
+
+    json_sink(
+        json::storage_ptr sp = {})
+        : pr_(new json::parser(std::move(sp)))
+    {
+    }
+
+    auto release() -> json::value
+    {
+        return pr_->release();
+    }
+
+private:
+    results
+    on_write(
+        capy::const_buffer b,
+        bool more) override
+    {
+        results rv;
+        if(more)
+        {
+            rv.bytes = pr_->write_some(
+                static_cast(
+                    b.data()), b.size(), rv.ec);
+        }
+        else
+        {
+            rv.bytes = pr_->write(
+                static_cast(b.data()),
+                b.size(), rv.ec);
+        }
+        if(! rv.ec.failed())
+            return rv;
+        return rv;
+    }
+
+    std::unique_ptr pr_;
+};
+
+struct do_json_rpc
+{
+    auto operator()(
+        http::route_params& rp) const ->
+            http::route_result
+    {
+        if(! rp.is_method(http::method::post))
+            return http::route::next;
+        return rp.read_body(
+            json_sink(),
+            [this](
+                json::value jv) ->
+                    http::route_result
+            {
+                return dispatch(std::move(jv));
+            });
+    }
+
+    // process the JSON-RPC request
+    auto dispatch(
+        json::value jv) const ->
+            http::route_result
+    {
+        (void)jv;
+        return http::route::next;
+    }
+
+};
+#endif
+
+#if 0
+
+auto
+do_bcrypt(
+    http::route_params& rp) ->
+        capy::task
+{
+    std::string password = "boost";
+    //auto rv = co_await capy::bcrypt::async_hash(password);
+    co_await g_tp.get_executor().submit(
+        []()
+        {
+            std::this_thread::sleep_for(std::chrono::seconds(1));
+        });
+    co_return http::route::next;
+}
+
+
+#endif
+
 int server_main( int argc, char* argv[] )
 {
     try
@@ -58,22 +160,67 @@ int server_main( int argc, char* argv[] )
         }
 
         capy::application app;
+        corosio::io_context ioc;
 
         install_services(app);
 
+        auto addr = urls::parse_ipv4_address(argv[1]);
+        if(addr.has_error())
+        {
+            std::cerr << "Invalid address: " << argv[1] << "\n";
+            return EXIT_FAILURE;
+        }
+        auto port = static_cast(std::atoi(argv[2]));
+        corosio::endpoint ep(addr.value(), port);
+
+        http::router rr;
+
+        rr.use(
+            [](http::route_params& rp) -> capy::task
+            {
+                (void)rp;
+                co_return http::route::next;
+            });
+        http_server hsrv(ioc, std::atoi(argv[4]), std::move(rr));
+        auto ec = hsrv.bind(ep);
+        if(ec)
+        {
+            std::cerr << "Bind failed: " << ec.message() << "\n";
+            return EXIT_FAILURE;
+        }
+
+        hsrv.start();
+        ioc.run();
+
+#if 0
         auto& srv = install_plain_http_server(
             app,
             argv[1],
             (unsigned short)std::atoi(argv[2]),
             std::atoi(argv[4]));
 
-        //srv.wwwroot.use("/log", serve_log_admin(app));
-        //srv.wwwroot.use("/alt", serve_static( argv[3] ));
-        srv.wwwroot.use("/detach", serve_detached());
+        http::cors_options opts;
+        opts.allowedHeaders = "Content-Type";
+
+        srv.wwwroot.use(
+            []( http::route_params& rp ) ->
+                capy::task
+            {
+                co_return {};
+            });
+        srv.wwwroot.use(
+            "/rpc",
+            http::cors(opts),
+            do_json_rpc()
+        );
+
+        srv.wwwroot.use( "/bcrypt", do_bcrypt );
+
         srv.wwwroot.use("/", serve_static( argv[3] ));
 
         app.start();
-        srv.attach();
+        srv.run();
+#endif
     }
     catch( std::exception const& e )
     {
diff --git a/example/server/serve_detached.hpp b/example/server/serve_detached.hpp
deleted file mode 100644
index 70d74785..00000000
--- a/example/server/serve_detached.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_SERVER_SERVE_DETACHED_HPP
-#define BOOST_BEAST2_SERVER_SERVE_DETACHED_HPP
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-/** A route handler which delegates work to a foreign thread
-*/
-class serve_detached
-{
-public:
-    ~serve_detached()
-    {
-    }
-
-    serve_detached()
-        : tp_(new asio::thread_pool(1))
-    {
-    }
-
-    serve_detached(serve_detached&&) = default;
-
-    system::error_code
-    operator()(
-        Request&,
-        Response& res) const
-    {
-        return res.detach(
-            [&](resumer resume)
-            {
-                asio::post(*tp_,
-                    [&, resume]()
-                    {
-                        // Simulate some asynchronous work
-                        std::this_thread::sleep_for(std::chrono::seconds(1));
-                        res.status(http_proto::status::ok);
-                        res.set_body("Hello from serve_detached!\n");
-                        resume(route::send);
-                        // resume( res.send("Hello from serve_detached!\n") );
-                    });
-            });
-    }
-
-private:
-    std::unique_ptr tp_;
-};
-
-} // beast2
-} // boost
-
-#endif
diff --git a/example/server/serve_log_admin.cpp b/example/server/serve_log_admin.cpp
index 6c2a2525..743d27da 100644
--- a/example/server/serve_log_admin.cpp
+++ b/example/server/serve_log_admin.cpp
@@ -30,9 +30,10 @@ class serve_log_page
 
     system::error_code
     operator()(
-        Request&,
-        Response& res) const
+        http::route_params&) const
     {
+        return {};
+#if 0
         auto const v = ls_.get_sections();
         std::string s;
         format_to(s, "\n");
@@ -72,10 +73,11 @@ class serve_log_page
         format_to(s, "\n");
         format_to(s, "\n");
 
-        res.status(http_proto::status::ok);
-        res.message.set(http_proto::field::content_type, "text/html; charset=UTF-8");
-        res.set_body(std::move(s));
-        return error::success;
+        p.status(http::status::ok);
+        p.res.set(http::field::content_type, "text/html; charset=UTF-8");
+        p.set_body(std::move(s));
+        return http::route::send;
+#endif
     }
 
 private:
@@ -97,13 +99,15 @@ class handle_submit
 
     system::error_code
     operator()(
-        Request&,
-        Response& res) const
+        http::route_params&) const
     {
-        res.status(http_proto::status::ok);
-        res.message.set(http_proto::field::content_type, "plain/text; charset=UTF-8");
-        res.set_body("submit");
-        return error::success;
+        return {};
+#if 0
+        p.status(http::status::ok);
+        p.res.set(http::field::content_type, "plain/text; charset=UTF-8");
+        p.set_body("submit");
+        return http::route::send;
+#endif
     }
 
 private:
@@ -114,15 +118,17 @@ class handle_submit
 
 //------------------------------------------------
 
+#if 0
 router
 serve_log_admin(
     capy::polystore& ps)
 {
     router r;
-    r.add(http_proto::method::get, "/", serve_log_page(ps));
-    r.add(http_proto::method::get, "/submit", handle_submit(ps));
+    r.add(http::method::get, "/", serve_log_page(ps));
+    r.add(http::method::get, "/submit", handle_submit(ps));
     return r;
 }
+#endif
 
 } // beast2
 } // boost
diff --git a/example/server/serve_log_admin.hpp b/example/server/serve_log_admin.hpp
index 10e51d57..1851833c 100644
--- a/example/server/serve_log_admin.hpp
+++ b/example/server/serve_log_admin.hpp
@@ -12,7 +12,7 @@
 
 #include 
 #include 
-#include 
+#include 
 
 namespace boost {
 namespace beast2 {
diff --git a/include/boost/beast2.hpp b/include/boost/beast2.hpp
index c19ea89f..865e71a1 100644
--- a/include/boost/beast2.hpp
+++ b/include/boost/beast2.hpp
@@ -10,28 +10,23 @@
 #ifndef BOOST_BEAST2_HPP
 #define BOOST_BEAST2_HPP
 
+// Server components
 #include 
-#include 
-#include 
 #include 
 #include 
 #include 
-//#include 
-#include 
-#include 
-#include 
+#include 
 #include 
-#include 
-#include 
+#include 
 #include 
 #include 
-//#include 
 #include 
+
+// Test utilities
 #include 
 #include 
-#include 
-#include 
-#include 
+
+// Core utilities
 #include 
 #include 
 #include 
@@ -39,9 +34,5 @@
 #include 
 #include 
 #include 
-#include 
-#include 
-//#include 
-#include 
 
 #endif
diff --git a/src/server/https_server.cpp b/include/boost/beast2/application.hpp
similarity index 65%
rename from src/server/https_server.cpp
rename to include/boost/beast2/application.hpp
index a8949389..345b3b3d 100644
--- a/src/server/https_server.cpp
+++ b/include/boost/beast2/application.hpp
@@ -7,17 +7,22 @@
 // Official repository: https://github.com/cppalliance/beast2
 //
 
-#include 
+#ifndef BOOST_BEAST2_APPLICATION_HPP
+#define BOOST_BEAST2_APPLICATION_HPP
+
+#include 
+#include 
+#include 
 
 namespace boost {
 namespace beast2 {
 
-namespace {
-
-} // (anon)
-
-//------------------------------------------------
+class port
+{
+public:
+};
 
 } // beast2
 } // boost
 
+#endif
diff --git a/include/boost/beast2/asio_io_context.hpp b/include/boost/beast2/asio_io_context.hpp
deleted file mode 100644
index 2b63ef21..00000000
--- a/include/boost/beast2/asio_io_context.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_ASIO_IO_CONTEXT_HPP
-#define BOOST_BEAST2_ASIO_IO_CONTEXT_HPP
-
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-/** Asio's io_context as an application part
-*/
-class BOOST_SYMBOL_VISIBLE
-    asio_io_context
-{
-public:
-    using executor_type =
-        asio::io_context::executor_type;
-
-    /** Destructor
-    */
-    BOOST_BEAST2_DECL
-    ~asio_io_context();
-
-    virtual
-    executor_type
-    get_executor() noexcept = 0;
-
-    /** Return the concurrency level
-    */
-    virtual
-    std::size_t
-    concurrency() const noexcept = 0;
-
-    /** Run the context
-
-        This function attaches the current thread to I/O context
-        so that it may be used for executing submitted function
-        objects. Blocks the calling thread until the part is stopped
-        and has no outstanding work.
-    */
-    virtual void attach() = 0;
-
-    virtual void start() = 0;
-    virtual void stop() = 0;
-};
-
-BOOST_BEAST2_DECL
-auto
-install_single_threaded_asio_io_context(
-    capy::application& app) ->
-        asio_io_context&;
-
-BOOST_BEAST2_DECL
-auto
-install_multi_threaded_asio_io_context(
-    capy::application& app,
-    int num_threads) ->
-        asio_io_context&;
-
-} // beast2
-} // boost
-
-#endif
diff --git a/include/boost/beast2/body_read_stream.hpp b/include/boost/beast2/body_read_stream.hpp
deleted file mode 100644
index c9287f50..00000000
--- a/include/boost/beast2/body_read_stream.hpp
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-// Copyright (c) 2025 Mungo Gill
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_BODY_READ_STREAM_HPP
-#define BOOST_BEAST2_BODY_READ_STREAM_HPP
-
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-/** A body reader for HTTP/1 messages.
-
-    This type meets the requirements of asio's AsyncReadStream, and is
-    constructed with a reference to an underlying AsyncReadStream.
-
-    Any call to `async_read_some` initially triggers reads from the underlying
-    stream until all of the HTTP headers and at least one byte of the body have
-    been read and processed.  Thereafter, each subsequent call to
-    `async_read_some` processes at least one byte of the body, triggering, where
-    required, calls to the underlying stream's `async_read_some` method.  The
-    resulting body data is stored in the referenced MutableBufferSequence.
-
-    All processing depends on a beast2::parser object owned by the caller and
-    referenced in the construction of this object.
-
-    @see
-        @ref http_proto::parser.
-*/
-template
-class body_read_stream
-{
-
-public:
-    /** The type of the executor associated with the stream.
-
-        This will be the type of executor used to invoke completion handlers
-        which do not have an explicit associated executor.
-    */
-    typedef typename AsyncReadStream::executor_type executor_type;
-
-    /** Get the executor associated with the object.
-
-        This function may be used to obtain the executor object that the stream
-        uses to dispatch completion handlers without an assocaited executor.
-
-        @return A copy of the executor that stream will use to dispatch
-        handlers.
-    */
-    executor_type
-    get_executor()
-    {
-        return stream_.get_executor();
-    }
-
-    /** Constructor
-
-        This constructor creates the stream by forwarding all arguments to the
-        underlying socket.  The socket then needs to be open and connected or
-        accepted before data can be sent or received on it.
-
-        @param s  The underlying stream from which the HTTP message is read.
-                  This object's executor is initialized to that of the
-                  underlying stream.
-
-        @param pr A http_proto::parser object which will perform the parsing of
-                  the HTTP message and extraction of the body.  This must be
-                  initialized by the caller and ownership of the parser is
-                  retained by the caller, which must guarantee that it remains
-                  valid until the handler is called.
-    */
-    explicit body_read_stream(AsyncReadStream& s, http_proto::parser& pr);
-
-    /** Read some data asynchronously.
-
-        This function is used to asynchronously read data from the stream.
-
-        This call always returns immediately.  The asynchronous operation will
-        continue until one of the following conditions is true:
-
-        @li The HTTP headers are read in full from the underlying stream and one
-            or more bytes of the body are read from the stream and stored in the
-            buffer `mb`.
-
-        @li An error occurs.
-
-        The algorithm, known as a composed asynchronous operation, is
-        implemented in terms of calls to the underlying stream's
-        `async_read_some` function. The program must ensure that no other calls
-        implemented using the underlying stream's `async_read_some` are
-        performed until this operation completes.
-
-        @param mb The buffers into which the body data will be read.  If the
-        size of the buffers is zero bytes, the operation always completes
-        immediately with no error.  Although the buffers object may be copied as
-        necessary, ownership of the underlying memory blocks is retained by the
-        caller, which must guarantee that they remain valid until the handler is
-        called.  Where the mb buffer is not of sufficient size to hold the read
-        data, the remainder may be read by subsequent calls to this function.
-
-        @param handler The completion handler to invoke when the operation
-        completes.  The implementation takes ownership of the handler by
-        performing a decay-copy.  The equivalent function signature of the
-        handler must be:
-        @code
-        void handler(
-            error_code const& error,        // result of operation
-            std::size_t bytes_transferred   // the number of bytes consumed by the parser
-        );
-        @endcode
-        Regardless of whether the asynchronous operation
-        completes immediately or not, the completion handler will not be invoked
-        from within this function. On immediate completion, invocation of the
-        handler will be performed in a manner equivalent to using
-        `asio::async_immediate`.
-
-        @note The `async_read_some` operation may not receive all of the
-        requested number of bytes.  Consider using the function
-        `asio::async_read` if you need to ensure that the requested amount of
-        data is read before the asynchronous operation completes.
-
-        @par Per-Operation Cancellation
-
-        This asynchronous operation supports cancellation for the following
-        net::cancellation_type values:
-
-        @li @c net::cancellation_type::terminal
-        @li @c net::cancellation_type::partial
-        @li @c net::cancellation_type::total
-
-        if they are also supported by the underlying stream's @c async_read_some
-        operation.
-    */
-    template<
-        class MutableBufferSequence,
-        BOOST_ASIO_COMPLETION_TOKEN_FOR(void(system::error_code, std::size_t))
-            CompletionToken>
-    BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
-        CompletionToken,
-        void(system::error_code, std::size_t))
-    async_read_some(MutableBufferSequence mb, CompletionToken&& handler);
-
-private:
-    AsyncReadStream& stream_;
-    http_proto::parser& pr_;
-};
-
-} // beast2
-} // boost
-
-#include 
-
-#endif // BOOST_BEAST2_BODY_READ_STREAM_HPP
diff --git a/include/boost/beast2/buffer.hpp b/include/boost/beast2/buffer.hpp
index 17a1e5d6..f3db4777 100644
--- a/include/boost/beast2/buffer.hpp
+++ b/include/boost/beast2/buffer.hpp
@@ -11,13 +11,18 @@
 #define BOOST_BEAST2_BUFFER_HPP
 
 #include 
-#include 
+#include 
 #include 
 #include 
 
 namespace boost {
 namespace beast2 {
 
+// Re-export buffer types from capy
+using capy::mutable_buffer;
+using capy::const_buffer;
+using capy::buffer_size;
+
 } // beast2
 } // boost
 
diff --git a/include/boost/beast2/detail/config.hpp b/include/boost/beast2/detail/config.hpp
index 405f49cf..0a5f9a88 100644
--- a/include/boost/beast2/detail/config.hpp
+++ b/include/boost/beast2/detail/config.hpp
@@ -36,6 +36,8 @@ namespace beast2 {
 #  include 
 # endif
 
+//------------------------------------------------
+
 // Add source location to error codes
 #ifdef BOOST_BEAST2_NO_SOURCE_LOCATION
 # define BOOST_BEAST2_ERR(ev) (::boost::system::error_code(ev))
@@ -53,6 +55,12 @@ namespace beast2 {
 #endif
 
 } // beast2
+
+namespace http {}
+namespace beast2 {
+namespace http = http;
+}
+
 } // boost
 
 #endif
diff --git a/include/boost/beast2/format.hpp b/include/boost/beast2/format.hpp
index 4c5c9e7f..fb3994f5 100644
--- a/include/boost/beast2/format.hpp
+++ b/include/boost/beast2/format.hpp
@@ -11,7 +11,9 @@
 #define BOOST_BEAST2_FORMAT_HPP
 
 #include 
+#include 
 #include 
+
 #include 
 #include 
 #include 
@@ -46,21 +48,59 @@ struct format_impl
     next()
     {
         has_placeholder = false;
+        bool unmatched_open = false;
+        bool unmatched_close = false;
         while (p != end)
         {
-            if (*p++ != '{')
-                continue;
-            if (p == end)
-                break;
-            if (*p++ == '}')
+            if(unmatched_open)
+            {
+                if(*p == '{')
+                {
+                    p++;
+                    core::string_view seg(p0, (p - 1) - p0);
+                    p0 = p;
+                    return seg;
+                }
+                if(*p == '}')
+                {
+                    p++;
+                    core::string_view seg(p0, (p - 2) - p0);
+                    p0 = p;
+                    has_placeholder = true;
+                    return seg;
+                }
+                detail::throw_invalid_argument(
+                    "invalid format string, unmatched {");
+            }
+            if(unmatched_close)
+            {
+                if(*p == '}')
+                {
+                    p++;
+                    core::string_view seg(p0, (p - 1) - p0);
+                    p0 = p;
+                    return seg;
+                }
+                detail::throw_invalid_argument(
+                    "invalid format string, unmatched }");
+            }
+            if (*p == '{')
+            {
+                unmatched_open = true;
+            }
+            if(*p == '}')
             {
-                core::string_view seg(
-                    p0, (p - 2) - p0);
-                p0 = p;
-                has_placeholder = true;
-                return seg;
+                unmatched_close = true;
             }
+            p++;
         }
+        if (unmatched_open)
+            detail::throw_invalid_argument(
+                "invalid format string, unmatched {");
+        if(unmatched_close)
+            detail::throw_invalid_argument(
+                "invalid format string, unmatched }");
+
         core::string_view seg(
             p0, end - p0);
         p0 = end;
@@ -71,10 +111,14 @@ struct format_impl
     void do_arg(Arg const& arg)
     {
         core::string_view seg = next();
-        if (seg.size())
-            os.write(seg.data(), static_cast<
-                std::streamsize>(seg.size()));
-        if (has_placeholder)
+        while(seg.size())
+        {
+            os.write(seg.data(), static_cast(seg.size()));
+            if(has_placeholder)
+                break;
+            seg = next();
+        }
+        if(has_placeholder)
             os << arg;
     };
 
@@ -83,9 +127,18 @@ struct format_impl
     {
         using expander = int[];
         (void)expander{0, (do_arg(args), 0)...};
-        if (p0 < end)
-            os.write(p0, static_cast<
-                std::streamsize>(end - p0));
+
+        core::string_view seg;
+        do
+        {
+            seg = next();
+            if(has_placeholder)
+                detail::throw_invalid_argument(
+                    "too few format arguments provided");
+            if(seg.size())
+                os.write(seg.data(), static_cast(seg.size()));
+        }
+        while(seg.size());
     }
 };
 
diff --git a/include/boost/beast2/http_server.hpp b/include/boost/beast2/http_server.hpp
new file mode 100644
index 00000000..303f347f
--- /dev/null
+++ b/include/boost/beast2/http_server.hpp
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/cppalliance/beast2
+//
+
+#ifndef BOOST_BEAST2_HTTP_SERVER_HPP
+#define BOOST_BEAST2_HTTP_SERVER_HPP
+
+#include 
+#include 
+#include 
+#include 
+
+namespace boost {
+namespace http { class flat_router; }
+namespace beast2 {
+
+class BOOST_BEAST2_DECL
+    http_server : public corosio::tcp_server
+{
+    struct impl;
+    impl* impl_;
+
+public:
+    ~http_server();
+
+    http_server(
+        corosio::io_context& ctx,
+        std::size_t num_workers,
+        http::flat_router router);
+
+private:
+    struct worker;
+
+    capy::task
+    do_session(worker& w);
+};
+
+} // beast2
+} // boost
+
+#endif
diff --git a/include/boost/beast2/impl/body_read_stream.hpp b/include/boost/beast2/impl/body_read_stream.hpp
deleted file mode 100644
index 8b0ae287..00000000
--- a/include/boost/beast2/impl/body_read_stream.hpp
+++ /dev/null
@@ -1,165 +0,0 @@
-//
-// Copyright (c) 2025 Mungo Gill
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_IMPL_BODY_READ_STREAM_HPP
-#define BOOST_BEAST2_IMPL_BODY_READ_STREAM_HPP
-
-#include 
-#include 
-
-#include 
-#include 
-#include 
-#include 
-
-#include 
-
-namespace boost {
-namespace beast2 {
-
-namespace detail {
-
-template
-class body_read_stream_op : public asio::coroutine
-{
-
-    AsyncReadStream& stream_;
-    MutableBufferSequence mb_;
-    http_proto::parser& pr_;
-
-public:
-    body_read_stream_op(
-        AsyncReadStream& s,
-        MutableBufferSequence&& mb,
-        http_proto::parser& pr) noexcept
-        : stream_(s)
-        , mb_(std::move(mb))
-        , pr_(pr)
-    {
-    }
-
-    template
-    void
-    operator()(
-        Self& self,
-        system::error_code ec = {},
-        std::size_t bytes_transferred = 0)
-    {
-        boost::ignore_unused(bytes_transferred);
-
-        BOOST_ASIO_CORO_REENTER(*this)
-        {
-            self.reset_cancellation_state(asio::enable_total_cancellation());
-
-            if(buffers::size(mb_) == 0)
-            {
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION(
-                        (__FILE__, __LINE__, "immediate"));
-                    auto io_ex = self.get_io_executor();
-                    asio::async_immediate(
-                        io_ex,
-                        asio::append(
-                            std::move(self), system::error_code{}));
-                }
-                goto upcall;
-            }
-
-            if(!pr_.got_header())
-            {
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION(
-                        (__FILE__, __LINE__, "async_read_header"));
-                    beast2::async_read_header(
-                        stream_, pr_, std::move(self));
-                }
-                if(ec.failed())
-                    goto upcall;
-            }
-
-            if(!!self.cancelled())
-            {
-                ec = asio::error::operation_aborted;
-                goto upcall;
-            }
-
-            BOOST_ASIO_CORO_YIELD
-            {
-                BOOST_ASIO_HANDLER_LOCATION(
-                    (__FILE__, __LINE__, "async_read_some"));
-                beast2::async_read_some(stream_, pr_, std::move(self));
-            }
-
-        upcall:
-            std::size_t n = 0;
-
-            if(!ec.failed())
-            {
-                auto dbs = asio::buffer_size(mb_);
-
-                if(dbs > 0)
-                {
-                    auto source_buf = pr_.pull_body();
-
-                    n = asio::buffer_copy(mb_, source_buf);
-
-                    pr_.consume_body(n);
-
-                    ec = (n != 0) ? system::error_code{}
-                                  : asio::stream_errc::eof;
-                }
-            }
-
-            self.complete(ec, n);
-        }
-    }
-};
-
-} // detail
-
-//------------------------------------------------
-
-// TODO: copy in Beast's stream traits to check if AsyncReadStream
-// is an AsyncReadStream, and also static_assert that body_read_stream is too.
-
-template
-body_read_stream::body_read_stream(
-    AsyncReadStream& s,
-    http_proto::parser& pr)
-    : stream_(s)
-    , pr_(pr)
-{
-}
-
-template
-template<
-    class MutableBufferSequence,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(void(system::error_code, std::size_t))
-        CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
-    CompletionToken,
-    void(system::error_code, std::size_t))
-body_read_stream::async_read_some(
-    MutableBufferSequence mb,
-    CompletionToken&& token)
-{
-    return asio::
-        async_compose(
-            detail::body_read_stream_op{
-                stream_, std::move(mb), pr_ },
-            token,
-            stream_);
-}
-
-} // beast2
-} // boost
-
-#endif // BOOST_BEAST2_IMPL_BODY_READ_STREAM_HPP
diff --git a/include/boost/beast2/impl/read.hpp b/include/boost/beast2/impl/read.hpp
deleted file mode 100644
index 7ca0fa7e..00000000
--- a/include/boost/beast2/impl/read.hpp
+++ /dev/null
@@ -1,205 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-// Copyright (c) 2025 Mohammad Nejati
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_IMPL_READ_HPP
-#define BOOST_BEAST2_IMPL_READ_HPP
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-namespace detail {
-
-template
-class read_until_op
-    : public asio::coroutine
-{
-    AsyncStream& stream_;
-    http_proto::parser& pr_;
-    std::size_t total_bytes_ = 0;
-    bool (&condition_)(http_proto::parser&);
-
-public:
-    read_until_op(
-        AsyncStream& s,
-        http_proto::parser& pr,
-        bool (&condition)(http_proto::parser&)) noexcept
-        : stream_(s)
-        , pr_(pr)
-        , condition_(condition)
-    {
-    }
-
-    template
-    void
-    operator()(
-        Self& self,
-        system::error_code ec = {},
-        std::size_t bytes_transferred = 0)
-    {
-        BOOST_ASIO_CORO_REENTER(*this)
-        {
-            self.reset_cancellation_state(
-                asio::enable_total_cancellation());
-
-            for(;;)
-            {
-                for(;;)
-                {
-                    pr_.parse(ec);
-                    if(ec == http_proto::condition::need_more_input)
-                    {
-                        if(!!self.cancelled())
-                        {
-                            ec = asio::error::operation_aborted;
-                            goto upcall;
-                        }
-                        // specific to http_io::async_read_some
-                        if(total_bytes_ != 0 && condition_(pr_))
-                        {
-                            ec = {};
-                            goto upcall;
-                        }
-                        break;
-                    }
-                    if(ec.failed() || condition_(pr_))
-                    {
-                        if(total_bytes_ == 0)
-                        {
-                            BOOST_ASIO_CORO_YIELD
-                            {
-                                BOOST_ASIO_HANDLER_LOCATION((
-                                    __FILE__, __LINE__,
-                                    "immediate"));
-                                auto io_ex = self.get_io_executor();
-                                asio::async_immediate(
-                                    io_ex,
-                                    asio::append(std::move(self), ec));
-                            }
-                        }
-                        goto upcall;
-                    }
-                }
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION((
-                        __FILE__, __LINE__,
-                        "async_read_some"));
-                    stream_.async_read_some(
-                        pr_.prepare(),
-                        std::move(self));
-                }
-                pr_.commit(bytes_transferred);
-                total_bytes_ += bytes_transferred;
-                if(ec == asio::error::eof)
-                {
-                    BOOST_ASSERT(
-                        bytes_transferred == 0);
-                    pr_.commit_eof();
-                    ec = {};
-                }
-                else if(ec.failed())
-                {
-                    goto upcall;
-                }
-            }
-
-        upcall:
-            self.complete(ec, total_bytes_);
-        }
-    }
-};
-
-inline
-bool
-got_header_condition(http_proto::parser& pr)
-{
-    return pr.got_header();
-}
-
-inline
-bool
-is_complete_condition(http_proto::parser& pr)
-{
-    return pr.is_complete();
-}
-
-} // detail
-
-//------------------------------------------------
-
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read_some(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token)
-{
-    return asio::async_compose<
-        CompletionToken,
-        void(system::error_code, std::size_t)>(
-            detail::read_until_op
-                {s, pr, detail::got_header_condition},
-            token,
-            s);
-}
-
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read_header(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token)
-{
-    // TODO: async_read_header should not perform a read
-    // operation if `parser::got_header() == true`.
-    return async_read_some(s, pr, std::move(token));
-}
-
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token)
-{
-    return asio::async_compose<
-        CompletionToken,
-        void(system::error_code, std::size_t)>(
-            detail::read_until_op
-                {s, pr, detail::is_complete_condition},
-            token,
-            s);
-}
-
-} // beast2
-} // boost
-
-#endif
diff --git a/include/boost/beast2/impl/write.hpp b/include/boost/beast2/impl/write.hpp
deleted file mode 100644
index 7eb10f1e..00000000
--- a/include/boost/beast2/impl/write.hpp
+++ /dev/null
@@ -1,295 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_IMPL_WRITE_HPP
-#define BOOST_BEAST2_IMPL_WRITE_HPP
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-namespace detail {
-
-template
-class write_some_op
-    : public asio::coroutine
-{
-    using buffers_type =
-        http_proto::serializer::const_buffers_type;
-
-    WriteStream& dest_;
-    http_proto::serializer& sr_;
-
-public:
-    write_some_op(
-        WriteStream& dest,
-        http_proto::serializer& sr) noexcept
-        : dest_(dest)
-        , sr_(sr)
-    {
-    }
-
-    template
-    void
-    operator()(
-        Self& self,
-        system::error_code ec = {},
-        std::size_t bytes_transferred = {})
-    {
-        system::result rv;
-
-        BOOST_ASIO_CORO_REENTER(*this)
-        {
-            rv = sr_.prepare();
-            if(! rv)
-            {
-                ec = rv.error();
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION((
-                        __FILE__, __LINE__,
-                        "immediate"));
-                    auto io_ex = self.get_io_executor();
-                    asio::async_immediate(
-                        io_ex,
-                        asio::append(std::move(self), ec));
-                }
-                goto upcall;
-            }
-
-            BOOST_ASIO_CORO_YIELD
-            {
-                BOOST_ASIO_HANDLER_LOCATION((
-                    __FILE__, __LINE__,
-                    "beast2::write_some_op"));
-                dest_.async_write_some(
-                    *rv,
-                    std::move(self));
-            }
-            sr_.consume(bytes_transferred);
-
-        upcall:
-            self.complete(
-                ec, bytes_transferred );
-        }
-    }
-};
-
-//------------------------------------------------
-
-template
-class write_op
-    : public asio::coroutine
-{
-    WriteStream& dest_;
-    http_proto::serializer& sr_;
-    std::size_t n_ = 0;
-
-public:
-    write_op(
-        WriteStream& dest,
-        http_proto::serializer& sr) noexcept
-        : dest_(dest)
-        , sr_(sr)
-    {
-    }
-
-    template
-    void
-    operator()(
-        Self& self,
-        system::error_code ec = {},
-        std::size_t bytes_transferred = 0)
-    {
-        BOOST_ASIO_CORO_REENTER(*this)
-        {
-            do
-            {
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION((
-                        __FILE__, __LINE__,
-                        "beast2::write_op"));
-                    async_write_some(
-                        dest_, sr_, std::move(self));
-                }
-                n_ += bytes_transferred;
-                if(ec.failed())
-                    break;
-            }
-            while(! sr_.is_done());
-
-            // upcall
-            self.complete(ec, n_ );
-        }
-    }
-};
-
-//------------------------------------------------
-
-#if 0
-template<
-    class WriteStream,
-    class ReadStream,
-    class CompletionCondition>
-class relay_some_op
-    : public asio::coroutine
-{
-    WriteStream& dest_;
-    ReadStream& src_;
-    CompletionCondition cond_;
-    http_proto::serializer& sr_;
-    std::size_t bytes_read_ = 0;
-
-public:
-    relay_some_op(
-        WriteStream& dest,
-        ReadStream& src,
-        CompletionCondition const& cond,
-        http_proto::serializer& sr) noexcept
-        : dest_(dest)
-        , src_(src)
-        , cond_(cond)
-        , sr_(sr)
-    {
-    }
-
-    template
-    void
-    operator()(
-        Self& self,
-        system::error_code ec = {},
-        std::size_t bytes_transferred = 0)
-    {
-        urls::result<
-            http_proto::serializer::buffers> rv;
-
-        BOOST_ASIO_CORO_REENTER(*this)
-        {
-            // Nothing to do
-            BOOST_ASSERT(! sr_.is_complete());
-
-            rv = sr_.prepare();
-            if(! rv)
-            {
-                ec = rv.error();
-                BOOST_ASIO_CORO_YIELD
-                {
-                    BOOST_ASIO_HANDLER_LOCATION((
-                        __FILE__, __LINE__,
-                        "beast2::relay_some_op"));
-                    asio::post(std::move(self));
-                }
-                goto upcall;
-            }
-
-            BOOST_ASIO_CORO_YIELD
-            {
-                BOOST_ASIO_HANDLER_LOCATION((
-                    __FILE__, __LINE__,
-                    "beast2::relay_some_op"));
-                dest_.async_write_some(
-                    write_buffers(*rv),
-                    std::move(self));
-            }
-            sr_.consume(bytes_transferred);
-
-        upcall:
-            self.complete(
-                ec, bytes_transferred );
-        }
-    }
-};
-#endif
-
-} // detail
-
-//------------------------------------------------
-
-template<
-    class AsyncWriteStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_write_some(
-    AsyncWriteStream& dest,
-    http_proto::serializer& sr,
-    CompletionToken&& token)
-{
-    return asio::async_compose<
-        CompletionToken,
-        void(system::error_code, std::size_t)>(
-            detail::write_some_op<
-                AsyncWriteStream>{dest, sr},
-            token, dest);
-}
-
-template<
-    class AsyncWriteStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_write(
-    AsyncWriteStream& dest,
-    http_proto::serializer& sr,
-    CompletionToken&& token)
-{
-    return asio::async_compose<
-        CompletionToken,
-        void(system::error_code, std::size_t)>(
-            detail::write_op<
-                AsyncWriteStream>{dest, sr},
-            token,
-            dest);
-}
-
-#if 0
-template<
-    class AsyncWriteStream,
-    class AsyncReadStream,
-    class CompletionCondition,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_relay_some(
-    AsyncWriteStream& dest,
-    AsyncReadStream& src,
-    CompletionCondition const& cond,
-    http_proto::serializer& sr,
-    CompletionToken&& token)
-{
-    return asio::async_compose<
-        CompletionToken,
-        void(system::error_code, std::size_t)>(
-            detail::relay_some_op<
-                AsyncWriteStream,
-                AsyncReadStream,
-                CompletionCondition>{
-                    dest, src, cond, sr},
-            token,
-            dest,
-            src);
-}
-#endif
-
-} // beast2
-} // boost
-
-#endif
diff --git a/include/boost/beast2/log_service.hpp b/include/boost/beast2/log_service.hpp
index 4099f3d4..9a975330 100644
--- a/include/boost/beast2/log_service.hpp
+++ b/include/boost/beast2/log_service.hpp
@@ -12,7 +12,7 @@
 
 #include 
 #include 
-#include 
+#include 
 #include 
 #include 
 
diff --git a/include/boost/beast2/read.hpp b/include/boost/beast2/read.hpp
deleted file mode 100644
index aece67c1..00000000
--- a/include/boost/beast2/read.hpp
+++ /dev/null
@@ -1,238 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-// Copyright (c) 2025 Mohammad Nejati
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_READ_HPP
-#define BOOST_BEAST2_READ_HPP
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-/** Read a complete header from the stream.
-
-    This function is used to asynchronously read a complete
-    message header from a stream into an instance of parser.
-    The function call always returns immediately. The
-    asynchronous operation will continue until one of the
-    following conditions is true:
-
-    @li The parser reads the header section of the message.
-    @li An error occurs.
-
-    This operation is implemented in terms of zero or more
-    calls to the stream's `async_read_some` function, and is
-    known as a composed operation. The program must
-    ensure that the stream performs no other reads until
-    this operation completes.
-
-    @param stream The stream from which the data is to be
-    read. The type must meet the AsyncReadStream
-    requirements.
-
-    @param parser The parser to use. The object must remain
-    valid at least until the handler is called; ownership is
-    not transferred.
-
-    @param token The completion token that will be used to
-    produce a completion handler, which will be called when
-    the read completes. The function signature of the
-    completion handler must be:
-    @code
-    void handler(
-        error_code const& error,        // result of operation
-        std::size_t bytes_transferred   // the number of bytes consumed by the parser
-    );
-    @endcode
-    Regardless of whether the asynchronous operation
-    completes immediately or not, the completion handler
-    will not be invoked from within this function. On
-    immediate completion, invocation of the handler will be
-    performed in a manner equivalent to using
-    `asio::async_immediate`.
-
-    @par Per-Operation Cancellation
-
-    This asynchronous operation supports cancellation for
-    the following `asio::cancellation_type` values:
-
-    @li `asio::cancellation_type::terminal`
-    @li `asio::cancellation_type::partial`
-    @li `asio::cancellation_type::total`
-
-    if they are also supported by the AsyncReadStream type's
-    async_read_some operation.
-*/
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken
-            BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(
-                typename AsyncReadStream::executor_type)>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read_header(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token
-        BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(
-            typename AsyncReadStream::executor_type));
-
-/** Read part of the message body from the stream.
-
-    This function is used to asynchronously read part of a
-    message body from a stream into an instance of parser.
-    The function call always returns immediately. The
-    asynchronous operation will continue until one of the
-    following conditions is true:
-
-    @li The parser reads the header section of the message.
-    @li The parser reads the entire message.
-    @li Additional body data becomes available.
-    @li An error occurs.
-
-    This operation is implemented in terms of zero or more
-    calls to the stream's `async_read_some` function, and is
-    known as a composed operation. The program must
-    ensure that the stream performs no other reads until
-    this operation completes.
-
-    @param stream The stream from which the data is to be
-    read. The type must meet the AsyncReadStream
-    requirements.
-
-    @param parser The parser to use. The object must remain
-    valid at least until the handler is called; ownership is
-    not transferred.
-
-    @param token The completion token that will be used to
-    produce a completion handler, which will be called when
-    the read completes. The function signature of the
-    completion handler must be:
-    @code
-    void handler(
-        error_code const& error,        // result of operation
-        std::size_t bytes_transferred   // the number of bytes consumed by the parser
-    );
-    @endcode
-    Regardless of whether the asynchronous operation
-    completes immediately or not, the completion handler
-    will not be invoked from within this function. On
-    immediate completion, invocation of the handler will be
-    performed in a manner equivalent to using
-    `asio::async_immediate`.
-
-    @par Per-Operation Cancellation
-
-    This asynchronous operation supports cancellation for
-    the following `asio::cancellation_type` values:
-
-    @li `asio::cancellation_type::terminal`
-    @li `asio::cancellation_type::partial`
-    @li `asio::cancellation_type::total`
-
-    if they are also supported by the AsyncReadStream type's
-    async_read_some operation.
-*/
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken
-            BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(
-                typename AsyncReadStream::executor_type)>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read_some(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token
-        BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(
-            typename AsyncReadStream::executor_type));
-
-/** Read a complete message from the stream.
-
-    This function is used to asynchronously read a complete
-    message from a stream into an instance of parser.
-    The function call always returns immediately. The
-    asynchronous operation will continue until one of the
-    following conditions is true:
-
-    @li The parser reads the entire message.
-    @li An error occurs.
-
-    This operation is implemented in terms of zero or more
-    calls to the stream's `async_read_some` function, and is
-    known as a composed operation. The program must
-    ensure that the stream performs no other reads until
-    this operation completes.
-
-    @param stream The stream from which the data is to be
-    read. The type must meet the AsyncReadStream
-    requirements.
-
-    @param parser The parser to use. The object must remain
-    valid at least until the handler is called; ownership is
-    not transferred.
-
-    @param token The completion token that will be used to
-    produce a completion handler, which will be called when
-    the read completes. The function signature of the
-    completion handler must be:
-    @code
-    void handler(
-        error_code const& error,        // result of operation
-        std::size_t bytes_transferred   // the number of bytes consumed by the parser
-    );
-    @endcode
-    Regardless of whether the asynchronous operation
-    completes immediately or not, the completion handler
-    will not be invoked from within this function. On
-    immediate completion, invocation of the handler will be
-    performed in a manner equivalent to using
-    `asio::async_immediate`.
-
-    @par Per-Operation Cancellation
-
-    This asynchronous operation supports cancellation for
-    the following `asio::cancellation_type` values:
-
-    @li `asio::cancellation_type::terminal`
-    @li `asio::cancellation_type::partial`
-    @li `asio::cancellation_type::total`
-
-    if they are also supported by the AsyncReadStream type's
-    async_read_some operation.
-*/
-template<
-    class AsyncReadStream,
-    BOOST_ASIO_COMPLETION_TOKEN_FOR(
-        void(system::error_code, std::size_t)) CompletionToken
-            BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(
-                typename AsyncReadStream::executor_type)>
-BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
-    void (system::error_code, std::size_t))
-async_read(
-    AsyncReadStream& s,
-    http_proto::parser& pr,
-    CompletionToken&& token
-        BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(
-            typename AsyncReadStream::executor_type));
-
-} // beast2
-} // boost
-
-#include 
-
-#endif
diff --git a/include/boost/beast2/server/basic_router.hpp b/include/boost/beast2/server/basic_router.hpp
deleted file mode 100644
index c58b56a3..00000000
--- a/include/boost/beast2/server/basic_router.hpp
+++ /dev/null
@@ -1,1014 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
-#define BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-namespace boost {
-namespace beast2 {
-
-template
-class basic_router;
-
-/** Configuration options for routers.
-*/
-struct router_options
-{
-    /** Constructor
-
-        Routers constructed with default options inherit the values of
-        @ref case_sensitive and @ref strict from the parent router.
-        If there is no parent, both default to `false`.
-        The value of @ref merge_params always defaults to `false`
-        and is never inherited.
-    */
-    router_options() = default;
-
-    /** Set whether to merge parameters from parent routers.
-
-        This setting controls whether route parameters defined on parent
-        routers are made available in nested routers. It is not inherited
-        and always defaults to `false`.
-
-        @par Example
-        @code
-        router r(router_options()
-            .merge_params(true)
-            .case_sensitive(true)
-            .strict(false));
-        @endcode
-
-        @param value `true` to merge parameters from parent routers.
-        @return A reference to `*this` for chaining.
-    */
-    router_options&
-    merge_params(
-        bool value) noexcept
-    {
-        v_ = (v_ & ~1) | (value ? 1 : 0);
-        return *this;
-    }
-
-    /** Set whether pattern matching is case-sensitive.
-
-        When this option is not set explicitly, the value is inherited
-        from the parent router or defaults to `false` if there is no parent.
-
-        @par Example
-        @code
-        router r(router_options()
-            .case_sensitive(true)
-            .strict(true));
-        @endcode
-
-        @param value `true` to perform case-sensitive path matching.
-        @return A reference to `*this` for chaining.
-    */
-    router_options&
-    case_sensitive(
-        bool value) noexcept
-    {
-        if(value)
-            v_ = (v_ & ~6) | 2;
-        else
-            v_ = (v_ & ~6) | 4;
-        return *this;
-    }
-
-    /** Set whether pattern matching is strict.
-
-        When this option is not set explicitly, the value is inherited
-        from the parent router or defaults to `false` if there is no parent.
-        Strict matching treats a trailing slash as significant:
-        the pattern `"/api"` matches `"/api"` but not `"/api/"`.
-        When strict matching is disabled, these paths are treated
-        as equivalent.
-
-        @par Example
-        @code
-        router r(router_options()
-            .strict(true)
-            .case_sensitive(false));
-        @endcode
-
-        @param value `true` to enable strict path matching.
-        @return A reference to `*this` for chaining.
-    */
-    router_options&
-    strict(
-        bool value) noexcept
-    {
-        if(value)
-            v_ = (v_ & ~24) | 8;
-        else
-            v_ = (v_ & ~24) | 16;
-        return *this;
-    }
-
-private:
-    template friend class basic_router;
-    unsigned int v_ = 0;
-};
-
-//-----------------------------------------------
-
-namespace detail {
-
-class any_router;
-
-//-----------------------------------------------
-
-// implementation for all routers
-class any_router
-{
-private:
-    template
-    friend class beast2::basic_router;
-    using opt_flags = unsigned int;
-
-    struct BOOST_SYMBOL_VISIBLE any_handler
-    {
-        BOOST_BEAST2_DECL virtual ~any_handler();
-        virtual std::size_t count() const noexcept = 0;
-        virtual route_result invoke(
-            basic_request&, basic_response&) const = 0;
-    };
-
-    using handler_ptr = std::unique_ptr;
-
-    struct handler_list
-    {
-        std::size_t n;
-        handler_ptr* p;
-    };
-
-    using match_result = basic_request::match_result;
-    struct matcher;
-    struct layer;
-    struct impl;
-
-    BOOST_BEAST2_DECL ~any_router();
-    BOOST_BEAST2_DECL any_router(opt_flags);
-    BOOST_BEAST2_DECL any_router(any_router&&) noexcept;
-    BOOST_BEAST2_DECL any_router(any_router const&) noexcept;
-    BOOST_BEAST2_DECL any_router& operator=(any_router&&) noexcept;
-    BOOST_BEAST2_DECL any_router& operator=(any_router const&) noexcept;
-    BOOST_BEAST2_DECL std::size_t count() const noexcept;
-    BOOST_BEAST2_DECL layer& new_layer(core::string_view pattern);
-    BOOST_BEAST2_DECL void add_impl(core::string_view, handler_list const&);
-    BOOST_BEAST2_DECL void add_impl(layer&,
-        http_proto::method, handler_list const&);
-    BOOST_BEAST2_DECL void add_impl(layer&,
-        core::string_view, handler_list const&);
-    BOOST_BEAST2_DECL route_result resume_impl(
-        basic_request&, basic_response&, route_result const& ec) const;
-    BOOST_BEAST2_DECL route_result dispatch_impl(http_proto::method,
-        core::string_view, urls::url_view const&,
-            basic_request&, basic_response&) const;
-    BOOST_BEAST2_DECL route_result dispatch_impl(
-        basic_request&, basic_response&) const;
-    route_result do_dispatch(basic_request&, basic_response&) const;
-
-    impl* impl_ = nullptr;
-};
-
-} // detail
-
-//-----------------------------------------------
-
-/** A container for HTTP route handlers.
-
-    `basic_router` objects store and dispatch route handlers based on the
-    HTTP method and path of an incoming request. Routes are added with a
-    path pattern and an associated handler, and the router is then used to
-    dispatch the appropriate handler.
-
-    Patterns used to create route definitions have percent-decoding applied
-    when handlers are mounted. A literal "%2F" in the pattern string is
-    indistinguishable from a literal '/'. For example, "/x%2Fz" is the
-    same as "/x/z" when used as a pattern.
-
-    @par Example
-    @code
-    using router_type = basic_router;
-    router_type router;
-    router.get("/hello",
-        [](Req& req, Res& res)
-        {
-            res.status(http_proto::status::ok);
-            res.set_body("Hello, world!");
-            return system::error_code{};
-        });
-    @endcode
-
-    Router objects are lightweight, shared references to their contents.
-    Copies of a router obtained through construction, conversion, or
-    assignment do not create new instances; they all refer to the same
-    underlying data.
-
-    @par Handlers
-    Regular handlers are invoked for matching routes and have this
-    equivalent signature:
-    @code
-    route_result handler( Req& req, Res& res )
-    @endcode
-
-    The return value is a @ref route_result used to indicate the desired
-    action through @ref route enum values, or to indicate that a failure
-    occurred. Failures are represented by error codes for which
-    `system::error_code::failed()` returns `true`.
-
-    When a failing error code is produced and remains unhandled, the
-    router enters error-dispatching mode. In this mode, only error
-    handlers are invoked. Error handlers are registered globally or
-    for specific paths and execute in the order of registration whenever
-    a failing error code is present in the response.
-
-    Error handlers have this equivalent signature:
-    @code
-    route_result error_handler( Req& req, Res& res, system::error_code ec )
-    @endcode
-
-    Each error handler may return any failing @ref system::error_code,
-    which is equivalent to calling:
-    @code
-    res.next(ec); // with ec.failed() == true
-    @endcode
-    Returning @ref route::next indicates that control should proceed to
-    the next matching error handler. Returning a different failing code
-    replaces the current error and continues dispatch in error mode using
-    that new code. Error handlers are invoked until one returns a result
-    other than @ref route::next.
-
-    @par Handler requirements
-    Regular handlers must be callable with:
-    @code
-    route_result( Req&, Res& )
-    @endcode
-
-    Error handlers must be callable with:
-    @code
-    route_result( Req&, Res&, system::error_code )
-    @endcode
-    Error handlers are invoked only when the response has a failing
-    error code, and execute in sequence until one returns a result
-    other than @ref route::next.
-
-    The prefix match is not strict: middleware attached to `"/api"`
-    will also match `"/api/users"` and `"/api/data"`. When registered
-    before route handlers for the same prefix, middleware runs before
-    those routes. This is analogous to `app.use(path, ...)` in
-    Express.js.
-
-    @par Thread Safety
-    Member functions marked `const` such as @ref dispatch and @ref resume
-    may be called concurrently on routers that refer to the same data.
-    Modification of routers through calls to non-`const` member functions
-    is not thread-safe and must not be performed concurrently with any
-    other member function.
-
-    @par Constraints
-    `Req` must be publicly derived from @ref basic_request.
-    `Res` must be publicly derived from @ref basic_response.
-
-    @tparam Req The type of request object.
-    @tparam Res The type of response object.
-*/
-template
-class basic_router : public detail::any_router
-{
-    // Req must be publicly derived from basic_request
-    BOOST_CORE_STATIC_ASSERT(
-        detail::derived_from::value);
-
-    // Res must be publicly derived from basic_response
-    BOOST_CORE_STATIC_ASSERT(
-        detail::derived_from::value);
-
-    // 0 = unrecognized
-    // 1 = normal handler (Req&, Res&)
-    // 2 = error handler  (Req&, Res&, error_code)
-    // 4 = basic_router
-
-    template
-    struct handler_type
-        : std::integral_constant
-    {
-    };
-
-    // route_result( Req&, Res& ) const
-    template
-    struct handler_type()(
-                std::declval(),
-                std::declval())),
-            route_result>::value
-        >::type> : std::integral_constant {};
-
-    // route_result( Req&, Res&, system::error_code const& ) const
-    template
-    struct handler_type()(
-                std::declval(),
-                std::declval(),
-                std::declval())),
-            route_result>::value
-        >::type> : std::integral_constant {};
-
-    // basic_router
-    template
-    struct handler_type::value &&
-            std::is_convertible::value &&
-            std::is_constructible>::value
-        >::type> : std::integral_constant {};
-
-    template
-    struct handler_check : std::true_type {};
-
-    template
-    struct handler_check
-        : std::conditional<
-              ( (handler_type::value & Mask) != 0 ),
-              handler_check,
-              std::false_type
-          >::type {};
-
-public:
-    /** The type of request object used in handlers
-    */
-    using request_type = Req;
-
-    /** The type of response object used in handlers
-    */
-    using response_type = Res;
-
-    /** A fluent interface for defining handlers on a specific route.
-
-        This type represents a single route within the router and
-        provides a chainable API for registering handlers associated
-        with particular HTTP methods or for all methods collectively.
-
-        Typical usage registers one or more handlers for a route:
-        @code
-        router.route("/users/:id")
-            .get(show_user)
-            .put(update_user)
-            .all(log_access);
-        @endcode
-
-        Each call appends handlers in registration order.
-    */
-    class fluent_route;
-
-    /** Constructor
-
-        Creates an empty router with the specified configuration.
-        Routers constructed with default options inherit the values
-        of @ref router_options::case_sensitive and
-        @ref router_options::strict from the parent router, or default
-        to `false` if there is no parent. The value of
-        @ref router_options::merge_params defaults to `false` and
-        is never inherited.
-
-        @param options The configuration options to use.
-    */
-    explicit
-    basic_router(router_options options = {})
-        : any_router(options.v_)
-    {
-    }
-
-    /** Construct a router from another router with compatible types.
-
-        This constructs a router that shares the same underlying routing
-        state as another router whose request and response types are base
-        classes of `Req` and `Res`, respectively.
-
-        The resulting router participates in shared ownership of the
-        implementation; copying the router does not duplicate routes or
-        handlers, and changes visible through one router are visible
-        through all routers that share the same underlying state.
-
-        @par Constraints
-        `Req` must be derived from `OtherReq`, and `Res` must be
-        derived from `OtherRes`.
-
-        @tparam OtherReq The request type of the source router.
-        @tparam OtherRes The response type of the source router.
-        @param other The router to copy.
-    */
-    template<
-        class OtherReq, class OtherRes,
-        class = typename std::enable_if<
-            detail::derived_from::value &&
-            detail::derived_from::value>::type
-    >
-    basic_router(
-        basic_router const& other)
-        : any_router(other)
-    {
-    }
-
-    /** Add one or more middleware handlers for a path prefix.
-
-        Each handler registered with this function participates in the
-        routing and error-dispatch process for requests whose path begins
-        with the specified prefix, as described in the @ref basic_router
-        class documentation. Handlers execute in the order they are added
-        and may return @ref route::next to transfer control to the
-        subsequent handler in the chain.
-
-        @par Example
-        @code
-        router.use("/api",
-            [](Request& req, Response& res)
-            {
-                if (!authenticate(req))
-                {
-                    res.status(401);
-                    res.set_body("Unauthorized");
-                    return route::send;
-                }
-                return route::next;
-            },
-            [](Request&, Response& res)
-            {
-                res.set_header("X-Powered-By", "MyServer");
-                return route::next;
-            });
-        @endcode
-
-        @par Preconditions
-        @p `pattern` must be a valid path prefix; it may be empty to
-            indicate the root scope.
-
-        @param pattern The pattern to match.
-        @param h1 The first handler to add.
-        @param hn Additional handlers to add, invoked after @p h1 in
-            registration order.
-    */
-    template
-    void use(
-        core::string_view pattern,
-        H1&& h1, HN... hn)
-    {
-        // If you get a compile error on this line it means that
-        // one or more of the provided types is not a valid handler,
-        // error handler, or router.
-        BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
-        add_impl(pattern, make_handler_list(
-            std::forward

(h1), std::forward(hn)...)); - } - - /** Add one or more global middleware handlers. - - Each handler registered with this function participates in the - routing and error-dispatch process as described in the - @ref basic_router class documentation. Handlers execute in the - order they are added and may return @ref route::next to transfer - control to the next handler in the chain. - - This is equivalent to writing: - @code - use( "/", h1, hn... ); - @endcode - - @par Example - @code - router.use( - [](Request&, Response& res) - { - res.message.erase("X-Powered-By"); - return route::next; - }); - @endcode - - @par Constraints - @li `h1` must not be convertible to @ref core::string_view. - - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - */ - template::value>::type> - void use(H1&& h1, HN&&... hn) - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler, - // error handler, or router. - BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value); - use(core::string_view(), - std::forward

(h1), std::forward(hn)...); - } - - /** Add handlers for all HTTP methods matching a path pattern. - - This registers regular handlers for the specified path pattern, - participating in dispatch as described in the @ref basic_router - class documentation. Handlers run when the route matches, - regardless of HTTP method, and execute in registration order. - Error handlers and routers cannot be passed here. A new route - object is created even if the pattern already exists. - - @code - router.route("/status") - .head(check_headers) - .get(send_status) - .all(log_access); - @endcode - - @par Preconditions - @p `pattern` must be a valid path pattern; it must not be empty. - - @param pattern The path pattern to match. - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - */ - template - void all( - core::string_view pattern, - H1&& h1, HN&&... hn) - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - this->route(pattern).all( - std::forward

(h1), std::forward(hn)...); - } - - /** Add one or more route handlers for a method and pattern. - - This registers regular handlers for the specified HTTP verb and - path pattern, participating in dispatch as described in the - @ref basic_router class documentation. Error handlers and - routers cannot be passed here. - - @param verb The known HTTP method to match. - @param pattern The path pattern to match. - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - */ - template - void add( - http_proto::method verb, - core::string_view pattern, - H1&& h1, HN&&... hn) - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - this->route(pattern).add(verb, - std::forward

(h1), std::forward(hn)...); - } - - /** Add one or more route handlers for a method and pattern. - - This registers regular handlers for the specified HTTP verb and - path pattern, participating in dispatch as described in the - @ref basic_router class documentation. Error handlers and - routers cannot be passed here. - - @param verb The HTTP method string to match. - @param pattern The path pattern to match. - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - */ - template - void add( - core::string_view verb, - core::string_view pattern, - H1&& h1, HN&&... hn) - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - this->route(pattern).add(verb, - std::forward

(h1), std::forward(hn)...); - } - - /** Return a fluent route for the specified path pattern. - - Adds a new route to the router for the given pattern. - A new route object is always created, even if another - route with the same pattern already exists. The returned - @ref fluent_route reference allows method-specific handler - registration (such as GET or POST) or catch-all handlers - with @ref fluent_route::all. - - @param pattern The path expression to match against request - targets. This may include parameters or wildcards following - the router's pattern syntax. May not be empty. - @return A fluent route interface for chaining handler registrations. - */ - auto - route( - core::string_view pattern) -> fluent_route - { - return fluent_route(*this, pattern); - } - - //-------------------------------------------- - - /** Dispatch a request to the appropriate handler. - - This runs the routing and error-dispatch logic for the given HTTP - method and target URL, as described in the @ref basic_router class - documentation. - - @par Thread Safety - This function may be called concurrently on the same object along - with other `const` member functions. Each concurrent invocation - must use distinct request and response objects. - - @param verb The HTTP method to match. This must not be - @ref http_proto::method::unknown. - @param url The full request target used for route matching. - @param req The request to pass to handlers. - @param res The response to pass to handlers. - @return The @ref route_result describing how routing completed. - @throws std::invalid_argument If @p verb is - @ref http_proto::method::unknown. - */ - auto - dispatch( - http_proto::method verb, - urls::url_view const& url, - Req& req, Res& res) const -> - route_result - { - if(verb == http_proto::method::unknown) - detail::throw_invalid_argument(); - return dispatch_impl(verb, - core::string_view(), url, req, res); - } - - /** Dispatch a request to the appropriate handler using a method string. - - This runs the routing and error-dispatch logic for the given HTTP - method string and target URL, as described in the @ref basic_router - class documentation. This overload is intended for method tokens - that are not represented by @ref http_proto::method. - - @par Thread Safety - This function may be called concurrently on the same object along - with other `const` member functions. Each concurrent invocation - must use distinct request and response objects. - - @param verb The HTTP method string to match. This must not be empty. - @param url The full request target used for route matching. - @param req The request to pass to handlers. - @param res The response to pass to handlers. - @return The @ref route_result describing how routing completed. - @throws std::invalid_argument If @p verb is empty. - */ - auto - dispatch( - core::string_view verb, - urls::url_view const& url, - Req& req, Res& res) -> - route_result - { - // verb cannot be empty - if(verb.empty()) - detail::throw_invalid_argument(); - return dispatch_impl( - http_proto::method::unknown, - verb, url, req, res); - } - - /** Resume dispatch after a detached handler. - - This continues routing after a previous call to @ref dispatch - returned @ref route::detach. It recreates the routing state and - resumes as if the handler that detached had instead returned - the specified @p ec from its body. The regular routing and - error-dispatch logic then proceeds as described in the - @ref basic_router class documentation. For example, if @p ec is - @ref route::next, the next matching handlers are invoked. - - @par Thread Safety - This function may be called concurrently on the same object along - with other `const` member functions. Each concurrent invocation - must use distinct request and response objects. - - @param req The request to pass to handlers. - @param res The response to pass to handlers. - @param rv The @ref route_result to resume with, as if returned - by the detached handler. - @return The @ref route_result describing how routing completed. - */ - auto - resume( - Req& req, Res& res, - route_result const& rv) const -> - route_result - { - return resume_impl(req, res, rv); - } - -private: - // wrapper for route handlers - template - struct handler_impl : any_handler - { - typename std::decay::type h; - - template - explicit handler_impl(Args&&... args) - : h(std::forward(args)...) - { - } - - std::size_t - count() const noexcept override - { - return count( - handler_type{}); - } - - route_result - invoke( - basic_request& req, - basic_response& res) const override - { - return invoke( - static_cast(req), - static_cast(res), - handler_type{}); - } - - private: - std::size_t count( - std::integral_constant) = delete; - - std::size_t count( - std::integral_constant) const noexcept - { - return 1; - } - - std::size_t count( - std::integral_constant) const noexcept - { - return 1; - } - - std::size_t count( - std::integral_constant) const noexcept - { - return 1 + h.count(); - } - - route_result invoke(Req&, Res&, - std::integral_constant) const = delete; - - // (Req, Res) - route_result invoke(Req& req, Res& res, - std::integral_constant) const - { - auto const& ec = static_cast< - basic_response const&>(res).ec_; - if(ec.failed()) - return beast2::route::next; - // avoid racing on res.resume_ - res.resume_ = res.pos_; - auto rv = h(req, res); - if(rv == beast2::route::detach) - return rv; - res.resume_ = 0; // revert - return rv; - } - - // (Req&, Res&, error_code) - route_result - invoke(Req& req, Res& res, - std::integral_constant) const - { - auto const& ec = static_cast< - basic_response const&>(res).ec_; - if(! ec.failed()) - return beast2::route::next; - // avoid racing on res.resume_ - res.resume_ = res.pos_; - auto rv = h(req, res, ec); - if(rv == beast2::route::detach) - return rv; - res.resume_ = 0; // revert - return rv; - } - - // any_router - route_result invoke(Req& req, Res& res, - std::integral_constant) const - { - auto const& ec = static_cast< - basic_response const&>(res).ec_; - if(! ec.failed()) - return h.dispatch_impl(req, res); - return beast2::route::next; - } - }; - - template - struct handler_list_impl : handler_list - { - template - explicit handler_list_impl(HN&&... hn) - { - n = sizeof...(HN); - p = v; - assign<0>(std::forward(hn)...); - } - - private: - template - void assign(H1&& h1, HN&&... hn) - { - v[I] = handler_ptr(new handler_impl

( - std::forward

(h1))); - assign(std::forward(hn)...); - } - - template - void assign() - { - } - - handler_ptr v[N]; - }; - - template - static auto - make_handler_list(HN&&... hn) -> - handler_list_impl - { - return handler_list_impl( - std::forward(hn)...); - } - - void append(layer& e, - http_proto::method verb, - handler_list const& handlers) - { - add_impl(e, verb, handlers); - } -}; - -//----------------------------------------------- - -template -class basic_router:: - fluent_route -{ -public: - fluent_route(fluent_route const&) = default; - - /** Add handlers that apply to all HTTP methods. - - This registers regular handlers that run for any request matching - the route's pattern, regardless of HTTP method. Handlers are - appended to the route's handler sequence and are invoked in - registration order whenever a preceding handler returns - @ref route::next. Error handlers and routers cannot be passed here. - - This function returns a @ref fluent_route, allowing additional - method registrations to be chained. For example: - @code - router.route("/resource") - .all(log_request) - .get(show_resource) - .post(update_resource); - @endcode - - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - @return The @ref fluent_route for further chained registrations. - */ - template - auto all( - H1&& h1, HN&&... hn) -> - fluent_route - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - owner_.add_impl(e_, core::string_view(), make_handler_list( - std::forward

(h1), std::forward(hn)...)); - return *this; - } - - /** Add handlers for a specific HTTP method. - - This registers regular handlers for the given method on the - current route, participating in dispatch as described in the - @ref basic_router class documentation. Handlers are appended - to the route's handler sequence and invoked in registration - order whenever a preceding handler returns @ref route::next. - Error handlers and routers cannot be passed here. - - @param verb The HTTP method to match. - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - @return The @ref fluent_route for further chained registrations. - */ - template - auto add( - http_proto::method verb, - H1&& h1, HN&&... hn) -> - fluent_route - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - owner_.add_impl(e_, verb, make_handler_list( - std::forward

(h1), std::forward(hn)...)); - return *this; - } - - /** Add handlers for a method name. - - This registers regular handlers for the given HTTP method string - on the current route, participating in dispatch as described in - the @ref basic_router class documentation. This overload is - intended for methods not represented by @ref http_proto::method. - Handlers are appended to the route's handler sequence and invoked - in registration order whenever a preceding handler returns - @ref route::next. - - @par Constraints - @li Each handler must be a regular handler; error handlers and - routers cannot be passed. - - @param verb The HTTP method string to match. - @param h1 The first handler to add. - @param hn Additional handlers to add, invoked after @p h1 in - registration order. - @return The @ref fluent_route for further chained registrations. - */ - template - auto add( - core::string_view verb, - H1&& h1, HN&&... hn) -> - fluent_route - { - // If you get a compile error on this line it means that - // one or more of the provided types is not a valid handler. - // Error handlers and routers cannot be passed here. - BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value); - owner_.add_impl(e_, verb, make_handler_list( - std::forward

(h1), std::forward(hn)...)); - return *this; - } - -private: - friend class basic_router; - fluent_route( - basic_router& owner, - core::string_view pattern) - : e_(owner.new_layer(pattern)) - , owner_(owner) - { - } - - layer& e_; - basic_router& owner_; -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/body_source.hpp b/include/boost/beast2/server/body_source.hpp new file mode 100644 index 00000000..09cc805c --- /dev/null +++ b/include/boost/beast2/server/body_source.hpp @@ -0,0 +1,432 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/beast2 +// + +#ifndef BOOST_BEAST2_SERVER_BODY_SOURCE_HPP +#define BOOST_BEAST2_SERVER_BODY_SOURCE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast2 { + +/** Tag for customizing body construction +*/ +struct construct_body_tag {}; + +//----------------------------------------------- + +/** A type erased HTTP message body. + + This type erases the specific body type used to represent the message body. + It provides a uniform interface for reading the body data regardless of + how the body is implemented. Accessing the bytes is achieved by calling + @ref read which reads data into a caller-provided buffer. Alternatively, + when @ref has_buffers returns `true` the body consists of buffers in memory, + and they can be accessed directly by calling @ref get_buffers. + + Example bodies include: + - in-memory buffers + - file-backed bodies + - generated bodies + + @note A body_source is movable but not copyable. + + Type-erased bodies can always be rewound to the beginning by calling + @ref rewind. Therefore, bodies can be read multiple times. + + @par Thread Safety + Unsafe. +*/ +class body_source +{ +public: + /** Destructor. + */ + BOOST_BEAST2_DECL ~body_source(); + + /** Constructor + + Default-constructed bodies are empty. + */ + body_source() = default; + + /** Constructor + + The content of @p other is transferred to `*this`. The + moved-from body is left empty, as if default-constructed. + */ + body_source(body_source&& other) noexcept + : impl_(other.impl_) + { + other.impl_ = nullptr; + } + + /** Assignment + + The previous body, if any, is released and the + content of @p other is transferred to `*this`. + */ + BOOST_BEAST2_DECL + body_source& operator=(body_source&& other) noexcept; + + /** Construct a streaming body source. + */ + template::type, body_source>::value && + capy::is_read_source::type>::value + , int>::type = 0> + body_source(ReadSource&& body); + + /** Construct a streaming body source with a known size. + */ + template::type, body_source>::value && + capy::is_read_source::type>::value + , int>::type = 0> + body_source( + std::size_t known_size, + ReadSource&& body); + + /** Construct a buffers body source. + */ + template::type, body_source>::value && + capy::is_data_source::type>::value + , int>::type = 0> + body_source(DataSource&& body); + + //template + //body(T&& t); + + /** Return `true` if the size of the body is known. + */ + bool has_size() const noexcept + { + if(impl_) + return impl_->has_size(); + return true; // empty + } + + /** Return `true` if the body consists of buffers in memory. + When the body consists of buffers in memory, they can + also be accessed directly using @ref get_buffers. + */ + bool has_buffers() const noexcept + { + if(impl_) + return impl_->has_buffers(); + return true; // empty + } + + /** Return the size of the body, if available. + @throw std::invalid_argument if @ref has_size returns `false`. + @return The size of the body in bytes. + */ + auto size() const -> std::size_t + { + if(impl_) + return impl_->size(); + return 0; // empty + } + + /** Return the buffers representing the body, if available. + @throw std::invalid_argument if @ref has_buffers returns `false`. + @return A span of buffers representing the body. + */ + auto data() const -> + span + { + if(impl_) + return impl_->data(); + return {}; // empty + } + + /** Rewind the body to the beginning. + This allows the body to be accessed from the start when calling @read. + */ + void rewind() + { + if(impl_) + return impl_->rewind(); + // empty + } + + /** Read from the body into a caller-provided buffer. + + @param dest A pointer to the buffer to read into. + @param n The maximum number of bytes to read. + @param ec Set to the error, if any occurred. + @return The number of bytes read, which may be + less than the number requested. A return value of + zero indicates end-of-body. + */ + auto + read(void* dest, std::size_t n, + system::error_code& ec) -> + std::size_t + { + if(impl_) + return impl_->read(dest, n, ec); + // empty + ec = http::error::end_of_stream; + return 0; + } + +private: + struct impl + { + virtual ~impl() = default; + virtual bool has_size() const noexcept { return false; } + virtual bool has_buffers() const noexcept { return false; } + virtual std::size_t size() const + { + detail::throw_invalid_argument(); + } + virtual auto data() const -> + span + { + detail::throw_invalid_argument(); + } + virtual void rewind() = 0; + virtual std::size_t read( + void* dest, std::size_t n, + system::error_code& ec) = 0; + }; + + impl* impl_ = nullptr; +}; + +//----------------------------------------------- + +template::type, body_source>::value && + capy::is_read_source::type>::value + , int>::type> +body_source:: +body_source( + ReadSource&& body) +{ + struct model : impl + { + system::error_code ec_; + typename std::decay::type body_; + + explicit model(ReadSource&& body) + : body_(std::forward(body)) + { + } + + void rewind() override + { + ec_ = {}; + body_.rewind(); + } + + std::size_t read( + void* dest, + std::size_t size, + system::error_code& ec) override + { + if(ec_.failed()) + { + ec = ec_; + return 0; + } + auto nread = body_.read( + capy::mutable_buffer(dest, size), ec); + ec_ = ec; + return nread; + } + }; + + auto p = ::operator new(sizeof(model)); + impl_ = ::new(p) model( + std::forward(body)); +} + +/** Construct a streaming body source with a known size. +*/ +template::type, body_source>::value && + capy::is_read_source::type>::value + , int>::type> +body_source:: +body_source( + std::size_t known_size, + ReadSource&& body) +{ + struct model : impl + { + std::size_t size_; + system::error_code ec_; + typename std::decay::type body_; + + model( + ReadSource&& body, + std::size_t known_size) + : size_(known_size) + , body_(std::forward(body)) + { + } + + bool has_size() const noexcept override + { + return true; + } + + std::size_t size() const override + { + return size_; + } + + void rewind() override + { + ec_ = {}; + body_.rewind(); + } + + std::size_t read( + void* dest, + std::size_t size, + system::error_code& ec) override + { + if(ec_.failed()) + { + ec = ec_; + return 0; + } + auto nread = body_.read( + capy::mutable_buffer(dest, size), ec); + ec_ = ec; + return nread; + } + }; + + auto p = ::operator new(sizeof(model)); + impl_ = ::new(p) model( + std::forward(body), known_size); +} + +/** Construct a buffers body source. +*/ +template::type, body_source>::value && + capy::is_data_source::type>::value + , int>::type> +body_source:: +body_source( + DataSource&& body) +{ + struct model : impl + { + typename std::decay::type body_; + std::size_t size_ = 0; + span bs_; + std::size_t nread_ = 0; + + explicit model( + DataSource&& body) noexcept + : body_(std::forward(body)) + { + auto const& data = body_.data(); + auto const& end = capy::end(data); + auto p = reinterpret_cast< + capy::const_buffer*>(this+1); + std::size_t length = 0; + for(auto it = capy::begin(data); it != end; ++it) + { + boost::capy::const_buffer cb(*it); + size_ += cb.size(); + *p++ = cb; + ++length; + } + bs_ = { reinterpret_cast< + capy::const_buffer*>(this + 1), length }; + } + + bool has_size() const noexcept override + { + return true; + } + + bool has_buffers() const noexcept override + { + return true; + } + + std::size_t size() const override + { + return size_; + } + + span + data() const override + { + return bs_; + } + + void rewind() override + { + nread_ = 0; + } + + std::size_t read( + void* dest, + std::size_t n0, + system::error_code& ec) override + { + std::size_t n = capy::copy( + capy::mutable_buffer(dest, n0), + capy::sans_prefix(bs_, nread_)); + nread_ += n; + if(nread_ >= size_) + ec = http::error::end_of_stream; + else + ec = {}; + return n; + } + }; + + std::size_t length = 0; + auto const& data = body.data(); + auto const& end = capy::end(data); + for(auto it = capy::begin(data); + it != end; ++it) + { + ++length; + } + + // VFALCO this requires DataSource to be nothrow + // move constructible for strong exception safety. + auto p = ::operator new(sizeof(model) + + length * sizeof(capy::const_buffer)); + impl_ = ::new(p) model( + std::forward(body)); +} + +} // beast2 +} // boost + +#endif diff --git a/include/boost/beast2/server/call_mf.hpp b/include/boost/beast2/server/call_mf.hpp deleted file mode 100644 index 6e470b34..00000000 --- a/include/boost/beast2/server/call_mf.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_CALL_MF_HPP -#define BOOST_BEAST2_SERVER_CALL_MF_HPP - -#include -#include -#include - -namespace boost { -namespace beast2 { - -namespace detail { -// Primary template -template -struct result_of; - -// Partial specialization for callable types -template -struct result_of -{ -private: - template - static auto test(int) -> decltype(std::declval()(std::declval()...)); - - template - static void test(...); - -public: - using type = decltype(test(0)); -}; -template -using result_of_t = typename result_of::type; -} // detail - -template -class call_mf_impl -{ - T* obj_; - MemFn memfn_; - -public: - call_mf_impl(T* obj, MemFn memfn) - : obj_(obj), memfn_(memfn) - { - } - - template - auto operator()(Args&&... args) const - -> detail::result_of_t - { - return (obj_->*memfn_)(std::forward(args)...); - } -}; - -template -inline call_mf_impl call_mf(MemFn memfn, T* obj) -{ - return call_mf_impl(obj, memfn); -} - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/http_server.hpp b/include/boost/beast2/server/http_server.hpp deleted file mode 100644 index d1283914..00000000 --- a/include/boost/beast2/server/http_server.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_HTTP_SERVER_HPP -#define BOOST_BEAST2_SERVER_HTTP_SERVER_HPP - -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -template -class http_server -{ -public: - ~http_server() = default; - - http_server() = default; - - router_asio wwwroot; - - /** Run the server - - This function attaches the current thread to I/O context - so that it may be used for executing submitted function - objects. Blocks the calling thread until the part is stopped - and has no outstanding work. - */ - virtual void attach() = 0; -}; - -//------------------------------------------------ - -BOOST_BEAST2_DECL -auto -install_plain_http_server( - capy::application& app, - char const* addr, - unsigned short port, - std::size_t num_workers) -> - http_server>&; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/http_stream.hpp b/include/boost/beast2/server/http_stream.hpp index e6448040..f48cf853 100644 --- a/include/boost/beast2/server/http_stream.hpp +++ b/include/boost/beast2/server/http_stream.hpp @@ -10,129 +10,111 @@ #ifndef BOOST_BEAST2_SERVER_HTTP_STREAM_HPP #define BOOST_BEAST2_SERVER_HTTP_STREAM_HPP +#if 0 + #include #include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include namespace boost { namespace beast2 { //------------------------------------------------ -/** An HTTP server stream which routes requests to handlers and sends reesponses. +/** An HTTP server stream which routes requests to handlers and sends responses. - An object of this type wraps an asynchronous Boost.ASIO stream and implements - a high level server connection which reads HTTP requests, routes them to - handlers installed in a router, and sends the HTTP response. + This class provides a coroutine-based HTTP server session that reads + HTTP requests, routes them to handlers installed in a router, and + sends the HTTP response. - @par Requires - `AsyncStream` must satisfy AsyncReadStream and AsyncWriteStream - - @tparam AsyncStream The type of asynchronous stream. + The session runs as a coroutine task and uses Corosio for async I/O. */ -template class http_stream - : private detacher::owner { public: /** Constructor. - This initializes a new HTTP connection object that operates on - the given stream, uses the specified router to dispatch incoming - requests, and calls the supplied completion function when the - connection closes or fails. - - Construction does not start any I/O; call @ref on_stream_begin when - the stream is connected to the remote peer to begin reading - requests and processing them. + Initializes a new HTTP connection object that operates on + the given socket and uses the specified router to dispatch + incoming requests. @param app The owning application, used to access shared services such as logging and protocol objects. - @param stream The underlying asynchronous stream to read from - and write to. The caller is responsible for maintaining its - lifetime for the duration of the session. + @param sock The socket to read from and write to. @param routes The router used to dispatch incoming HTTP requests. - @param close_fn The function invoked when the connection is closed - or an unrecoverable error occurs. */ http_stream( capy::application& app, - AsyncStream& stream, - router_asio routes, - any_lambda close_fn); + corosio::socket& sock, + http::flat_router& routes); - /** Called to start a new HTTP session + /** Run the HTTP session as a coroutine. - The stream must be in a connected, - correct state for a new session. - */ - void on_stream_begin(acceptor_config const& config); + Reads HTTP requests, dispatches them through the router, + and writes responses until the connection closes or an + error occurs. -private: - void do_read(); - - void on_read( - system::error_code ec, - std::size_t bytes_transferred); + @param config The acceptor configuration for this connection. - void on_write( - system::error_code const& ec, - std::size_t bytes_transferred); + @return A task that completes when the session ends. + */ + capy::task + run(http::acceptor_config const& config); - void do_fail(core::string_view s, - system::error_code const& ec); +private: + capy::task> + read_header(); - resumer do_detach() override; + capy::task> + read_body(); - void do_resume(system::error_code const& ec) override; + capy::task> + write_response(); - void do_resume2(system::error_code ec); + void on_headers(); + http::route_result do_dispatch(); + void do_respond(http::route_result rv); -protected: std::string id() const { return std::string("[") + std::to_string(id_) + "] "; } -protected: + void clear() noexcept; + section sect_; std::size_t id_ = 0; - AsyncStream& stream_; - router_asio routes_; - any_lambda close_; - acceptor_config const* pconfig_ = nullptr; - - using work_guard = asio::executor_work_guard().get_executor())>; - std::unique_ptr pwg_; - Request req_; - ResponseAsio res_; + corosio::socket& sock_; + //http::flat_router& routes_; + http::acceptor_config const* pconfig_ = nullptr; + corosio_route_params rp_; }; //------------------------------------------------ -template -http_stream:: +inline +http_stream:: http_stream( capy::application& app, - AsyncStream& stream, - router_asio routes, - any_lambda close) + corosio::socket& sock, + http::flat_router& /*routes*/) : sect_(use_log_service(app).get_section("http_stream")) , id_( []() noexcept @@ -140,247 +122,301 @@ http_stream( static std::size_t n = 0; return ++n; }()) - , stream_(stream) - , routes_(std::move(routes)) - , close_(close) - , res_(stream_) + , sock_(sock) + //, routes_(routes) + , rp_(sock_) { - req_.parser = http_proto::request_parser(app); - - res_.serializer = http_proto::serializer(app); - res_.detach = detacher(*this); + rp_.parser = http::request_parser(app); + rp_.serializer = http::serializer(app); + // Note: suspend mechanism removed - handlers must complete synchronously } -/** Called to start a new HTTP session - - The stream must be in a connected, - correct state for a new session. -*/ -template -void -http_stream:: -on_stream_begin( - acceptor_config const& config) +inline +capy::task +http_stream:: +run(http::acceptor_config const& config) { pconfig_ = &config; - req_.parser.reset(); - do_read(); -} -template -void -http_stream:: -do_read() -{ - req_.parser.start(); - res_.serializer.reset(); - beast2::async_read(stream_, req_.parser, - call_mf(&http_stream::on_read, this)); -} - -template -void -http_stream:: -on_read( - system::error_code ec, - std::size_t bytes_transferred) -{ - (void)bytes_transferred; + for (;;) + { + // Reset parser for new request + rp_.parser.reset(); + rp_.session_data.clear(); + rp_.parser.start(); + + // Read HTTP request header + auto [ec, n] = co_await read_header(); + if (ec) + { + LOG_TRC(sect_)("{} read_header: {}", id(), ec.message()); + break; + } - if(ec.failed()) - return do_fail("http_stream::on_read", ec); + LOG_TRC(sect_)("{} read_header bytes={}", id(), n); - LOG_TRC(this->sect_)( - "{} http_stream::on_read bytes={}", - this->id(), bytes_transferred); + // Process headers and dispatch + on_headers(); - BOOST_ASSERT(req_.parser.is_complete()); + auto rv = do_dispatch(); + do_respond(rv); - //---------------------------------------- - // - // set up Request and Response objects - // + // Write response + if (!rp_.serializer.is_done()) + { + auto [wec, wn] = co_await write_response(); + if (wec) + { + LOG_TRC(sect_)("{} write_response: {}", id(), wec.message()); + break; + } + LOG_TRC(sect_)("{} write_response bytes={}", id(), wn); + } - // VFALCO HACK for now we make a copy of the message - req_.message = req_.parser.get(); + // Check keep-alive + if (!rp_.res.keep_alive()) + break; + } - // copy version - res_.message.set_version(req_.message.version()); + clear(); +} - // copy keep-alive setting - res_.message.set_start_line( - http_proto::status::ok, req_.parser.get().version()); - res_.message.set_keep_alive(req_.parser.get().keep_alive()); +inline +capy::task> +http_stream:: +read_header() +{ + std::size_t total_bytes = 0; + system::error_code ec; - // parse the URL + for (;;) { - auto rv = urls::parse_uri_reference(req_.parser.get().target()); - if(rv.has_value()) + // Try to parse what we have + rp_.parser.parse(ec); + + if (ec == http::condition::need_more_input) { - req_.url = rv.value(); - req_.base_path = ""; - req_.path = std::string(rv->encoded_path()); + // Need to read more data + auto buf = rp_.parser.prepare(); + auto [read_ec, n] = co_await sock_.read_some(buf); + + if (read_ec) + { + co_return {read_ec, total_bytes}; + } + + if (n == 0) + { + // EOF + rp_.parser.commit_eof(); + ec = {}; + } + else + { + rp_.parser.commit(n); + total_bytes += n; + } + continue; } - else + + if (ec.failed()) { - // error parsing URL - res_.status( - http_proto::status::bad_request); - res_.set_body( - "Bad Request: " + rv.error().message()); - goto do_write; + co_return {ec, total_bytes}; } - } - - // invoke handlers for the route - BOOST_ASSERT(! pwg_); - ec = routes_.dispatch(req_.message.method(), req_.url, req_, res_); - if(ec == route::send) - goto do_write; - if(ec == route::next) - { - // unhandled - res_.status(http_proto::status::not_found); - std::string s; - format_to(s, "The requested URL {} was not found on this server.", req_.url); - //res_.message.set_keep_alive(false); // VFALCO? - res_.set_body(s); - goto do_write; + // Header complete + if (rp_.parser.got_header()) + { + co_return {{}, total_bytes}; + } } +} - if(ec == route::detach) - { - // make sure they called detach() - BOOST_ASSERT(pwg_); - return; - } +inline +capy::task> +http_stream:: +read_body() +{ + std::size_t total_bytes = 0; + system::error_code ec; - // error message of last resort + while (!rp_.parser.is_complete()) { - BOOST_ASSERT(ec.failed()); - res_.status(http_proto::status::internal_server_error); - std::string s; - format_to(s, "An internal server error occurred: {}", ec.message()); - //res_.message.set_keep_alive(false); // VFALCO? - res_.set_body(s); - } + rp_.parser.parse(ec); + + if (ec == http::condition::need_more_input) + { + auto buf = rp_.parser.prepare(); + auto [read_ec, n] = co_await sock_.read_some(buf); + + if (read_ec) + { + co_return {read_ec, total_bytes}; + } + + if (n == 0) + { + rp_.parser.commit_eof(); + } + else + { + rp_.parser.commit(n); + total_bytes += n; + } + continue; + } -do_write: - if(res_.serializer.is_done()) - { - // happens when the handler sends the response - return on_write(system::error_code(), 0); + if (ec.failed()) + { + co_return {ec, total_bytes}; + } } - beast2::async_write(stream_, res_.serializer, - call_mf(&http_stream::on_write, this)); + co_return {{}, total_bytes}; } -template -void -http_stream:: -on_write( - system::error_code const& ec, - std::size_t bytes_transferred) +inline +capy::task> +http_stream:: +write_response() { - (void)bytes_transferred; - - if(ec.failed()) - return do_fail("http_stream::on_write", ec); - - BOOST_ASSERT(res_.serializer.is_done()); + std::size_t total_bytes = 0; - LOG_TRC(this->sect_)( - "{} http_stream::on_write bytes={}", - this->id(), bytes_transferred); - - if(res_.message.keep_alive()) - return do_read(); + while (!rp_.serializer.is_done()) + { + auto rv = rp_.serializer.prepare(); + if (!rv) + { + co_return {rv.error(), total_bytes}; + } - // tidy up lingering objects - req_.parser.reset(); - res_.serializer.reset(); - res_.message.clear(); + auto bufs = *rv; + std::size_t buf_size = 0; + for (auto const& buf : bufs) + buf_size += buf.size(); - close_({}); -} + if (buf_size == 0) + { + // Serializer done + break; + } -template -void -http_stream:: -do_fail( - core::string_view s, system::error_code const& ec) -{ - LOG_TRC(this->sect_)("{}: {}", s, ec.message()); + // Write buffers - we need to write them all + std::size_t written = 0; + for (auto const& buf : bufs) + { + auto [ec, n] = co_await sock_.write_some( + capy::const_buffer(buf.data(), buf.size())); + if (ec) + { + co_return {ec, total_bytes + written}; + } + written += n; + } - // tidy up lingering objects - req_.parser.reset(); - res_.serializer.reset(); - //res_.clear(); - //preq_.reset(); + rp_.serializer.consume(written); + total_bytes += written; + } - close_(ec); + co_return {{}, total_bytes}; } -template -auto -http_stream:: -do_detach() -> - resumer +inline +void +http_stream:: +on_headers() { - BOOST_ASSERT(stream_.get_executor().running_in_this_thread()); - - // can't call twice - BOOST_ASSERT(! pwg_); - pwg_.reset(new work_guard(stream_.get_executor())); - - // VFALCO cancel timer + // Set up Request and Response objects + rp_.req = rp_.parser.get(); + rp_.route_data.clear(); + rp_.res.set_start_line( + http::status::ok, rp_.req.version()); + rp_.res.set_keep_alive(rp_.req.keep_alive()); + rp_.serializer.reset(); + + // Parse the URL + auto rv = urls::parse_uri_reference(rp_.req.target()); + if (rv.has_error()) + { + rp_.status(http::status::bad_request); + rp_.set_body("Bad Request: " + rv.error().message()); + return; + } - return resumer(*this); + rp_.url = rv.value(); } -template -void -http_stream:: -do_resume(system::error_code const& ec) +inline +http::route_result +http_stream:: +do_dispatch() { - asio::dispatch( - stream_.get_executor(), - asio::prepend(call_mf( - &http_stream::do_resume2, this), ec)); + return {}; + //return routes_.dispatch( + //rp_.req.method(), rp_.url, rp_); } -template +inline void -http_stream:: -do_resume2(system::error_code ec) +http_stream:: +do_respond(http::route_result rv) { - BOOST_ASSERT(stream_.get_executor().running_in_this_thread()); + if (rv == http::route::close) + { + rp_.res.set_keep_alive(false); + return; + } - BOOST_ASSERT(pwg_.get() != nullptr); - pwg_.reset(); + if (rv == http::route::complete) + { + // Handler sent the response directly + return; + } - // invoke handlers for the route - BOOST_ASSERT(! pwg_); - ec = routes_.resume(req_, res_, ec); + if (rv == http::route::suspend) + { + // Suspend not supported - treat as internal error + rp_.status(http::status::internal_server_error); + rp_.set_body("Handler suspend not supported"); + rp_.res.set_keep_alive(false); + return; + } - if(ec == route::detach) + if (rv == http::route::next) { - // make sure they called detach() - BOOST_ASSERT(pwg_); + // Unhandled request + auto const status = http::status::not_found; + rp_.status(status); + rp_.set_body(http::to_string(status)); return; } - if(ec.failed()) + if (rv != http::route::send) { - // give a default error response? + // Error message of last resort + BOOST_ASSERT(rv.failed()); + BOOST_ASSERT(!http::is_route_result(rv)); + rp_.status(http::status::internal_server_error); + std::string s; + format_to(s, "An internal server error occurred: {}", rv.message()); + rp_.res.set_keep_alive(false); + rp_.set_body(s); } - beast2::async_write(stream_, res_.serializer, - call_mf(&http_stream::on_write, this)); +} + +inline +void +http_stream:: +clear() noexcept +{ + rp_.parser.reset(); + rp_.serializer.reset(); + rp_.res.clear(); } } // beast2 } // boost #endif + +#endif diff --git a/include/boost/beast2/server/https_server.hpp b/include/boost/beast2/server/https_server.hpp deleted file mode 100644 index 640f072d..00000000 --- a/include/boost/beast2/server/https_server.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_HTTPS_SERVER_HPP -#define BOOST_BEAST2_SERVER_HTTPS_SERVER_HPP - -#include -//#include -#include - -namespace boost { -namespace beast2 { - -class https_server -{ -public: - BOOST_BEAST2_DECL - https_server(); - - -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/plain_worker.hpp b/include/boost/beast2/server/plain_worker.hpp deleted file mode 100644 index f6ebb13e..00000000 --- a/include/boost/beast2/server/plain_worker.hpp +++ /dev/null @@ -1,182 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_PLAIN_WORKER_HPP -#define BOOST_BEAST2_SERVER_PLAIN_WORKER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -template -class plain_worker - : public http_stream< - asio::basic_stream_socket - > -{ -public: - using executor_type = Executor; - using protocol_type = Protocol; - using socket_type = - asio::basic_stream_socket; - using stream_type = socket_type; - using acceptor_config = beast2::acceptor_config; - - template - plain_worker( - workers_base& wb, - Executor0 const& ex, - router_asio rr); - - capy::application& app() noexcept - { - return wb_.app(); - } - - socket_type& socket() noexcept - { - return stream_; - } - - typename Protocol::endpoint& - endpoint() noexcept - { - return ep_; - } - - /** Cancel all outstanding I/O - */ - void cancel(); - - // Called when an incoming connection is accepted - void on_accept(acceptor_config const* pconfig); - - void do_fail( - core::string_view s, system::error_code const& ec); - - void reset(); - -private: - /** Called when the logical session ends - */ - void do_close(system::error_code const& ec); - - workers_base& wb_; - stream_type stream_; - typename Protocol::endpoint ep_; -}; - -//------------------------------------------------ - -template -template -plain_worker:: -plain_worker( - workers_base& wb, - Executor0 const& ex, - router_asio rr) - : http_stream( - wb.app(), - stream_, - std::move(rr), - [this](system::error_code const& ec) - { - this->do_close(ec); - }) - , wb_(wb) - , stream_(Executor(ex)) -{ -} - -template -void -plain_worker:: -cancel() -{ - system::error_code ec; - stream_.cancel(ec); -} - -//-------------------------------------------- - -// Called when an incoming connection is accepted -template -void -plain_worker:: -on_accept(acceptor_config const* pconfig) -{ - BOOST_ASSERT(stream_.get_executor().running_in_this_thread()); - // VFALCO TODO timeout - this->on_stream_begin(*pconfig); -} - -template -void -plain_worker:: -do_fail( - core::string_view s, system::error_code const& ec) -{ - reset(); - - if(ec == asio::error::operation_aborted) - { - LOG_TRC(this->sect_)( - "{} {}: {}", - this->id(), s, ec.message()); - // this means the worker was stopped, don't submit new work - return; - } - - LOG_DBG(this->sect_)( - "{} {}: {}", - this->id(), s, ec.message()); - wb_.do_idle(this); -} - -template -void -plain_worker:: -reset() -{ - // Clean up any previous connection. - system::error_code ec; - stream_.close(ec); -} - -/** Close the connection to end the session -*/ -template -void -plain_worker:: -do_close(system::error_code const& ec) -{ - if(! ec.failed()) - { - reset(); - wb_.do_idle(this); - return; - } - - do_fail("plain_worker::do_close", ec); -} - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/route_handler.hpp b/include/boost/beast2/server/route_handler.hpp deleted file mode 100644 index 1bb8d382..00000000 --- a/include/boost/beast2/server/route_handler.hpp +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_ROUTE_HANDLER_HPP -#define BOOST_BEAST2_SERVER_ROUTE_HANDLER_HPP - -#include -#include -#include -#include // VFALCO forward declare? -#include // VFALCO forward declare? -#include // VFALCO forward declare? -#include // VFALCO forward declare? -#include -#include - -namespace boost { -namespace beast2 { - -struct acceptor_config -{ - bool is_ssl; - bool is_admin; -}; - -/** Request object for HTTP route handlers -*/ -struct Request : basic_request -{ - /** The complete request target - - This is the parsed directly from the start - line contained in the HTTP request and is - never modified. - */ - urls::url_view url; - - /** The HTTP request message - */ - http_proto::request message; - - /** The HTTP request parser - This can be used to take over reading the body. - */ - http_proto::request_parser parser; - - /** A container for storing arbitrary data associated with the request. - This starts out empty for each new request. - */ - capy::polystore data; -}; - -//----------------------------------------------- - -/** Response object for HTTP route handlers -*/ -struct Response : basic_response -{ - /** The HTTP response message - */ - http_proto::response message; - - /** The HTTP response serializer - */ - http_proto::serializer serializer; - - /** The detacher for this session. - This can be used to detach from the - session and take over managing the - connection. - */ - detacher detach; - - /** A container for storing arbitrary data associated with the session. - - This starts out empty for each new session. - */ - capy::polystore data; - - route_result close() const noexcept - { - return route::close; - } - - route_result complete() const noexcept - { - return route::complete; - } - - route_result next() const noexcept - { - return route::next; - } - - route_result next_route() const noexcept - { - return route::next_route; - } - - route_result send() const noexcept - { - return route::send; - } - - BOOST_BEAST2_DECL - route_result - fail(system::error_code const& ec); - - // route_result send(core::string_view); - - /** Set the status code of the response. - @par Example - @code - res.status(http_proto::status::not_found); - @endcode - @param code The status code to set. - @return A reference to this response. - */ - BOOST_BEAST2_DECL - Response& - status(http_proto::status code); - - BOOST_BEAST2_DECL - Response& - set_body(std::string s); -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/route_handler_asio.hpp b/include/boost/beast2/server/route_handler_asio.hpp deleted file mode 100644 index 793240ac..00000000 --- a/include/boost/beast2/server/route_handler_asio.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_ROUTE_HANDLER_ASIO_HPP -#define BOOST_BEAST2_SERVER_ROUTE_HANDLER_ASIO_HPP - -#include -#include -#include - -namespace boost { -namespace beast2 { - -/** Response object for Asio HTTP route handlers -*/ -template -struct ResponseAsio : Response -{ - using stream_type = typename std::decay::type; - - AsyncStream stream; - - template - explicit - ResponseAsio( - Args&&... args) - : stream(std::forward(args)...) - { - } -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/route_handler_corosio.hpp b/include/boost/beast2/server/route_handler_corosio.hpp new file mode 100644 index 00000000..dcda4305 --- /dev/null +++ b/include/boost/beast2/server/route_handler_corosio.hpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/beast2 +// + +#ifndef BOOST_BEAST2_SERVER_ROUTE_HANDLER_COROSIO_HPP +#define BOOST_BEAST2_SERVER_ROUTE_HANDLER_COROSIO_HPP + +#include +#include +#include + +namespace boost { +namespace beast2 { + +/** Route parameters object for Corosio HTTP route handlers +*/ +class corosio_route_params + : public http::route_params +{ +public: + using stream_type = corosio::socket; + + corosio::socket& stream; + + explicit + corosio_route_params( + corosio::socket& s) + : stream(s) + { + } +}; + +} // beast2 +} // boost + +#endif diff --git a/include/boost/beast2/server/router.hpp b/include/boost/beast2/server/router.hpp index f6eb7061..ed94d146 100644 --- a/include/boost/beast2/server/router.hpp +++ b/include/boost/beast2/server/router.hpp @@ -11,15 +11,15 @@ #define BOOST_BEAST2_SERVER_ROUTER_HPP #include -#include -#include +#include +#include namespace boost { namespace beast2 { /** The sans-IO router type */ -using router = basic_router; +using router = http::router; } // beast2 } // boost diff --git a/include/boost/beast2/server/router_asio.hpp b/include/boost/beast2/server/router_asio.hpp deleted file mode 100644 index e43f1d44..00000000 --- a/include/boost/beast2/server/router_asio.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_ROUTER_ASIO_HPP -#define BOOST_BEAST2_SERVER_ROUTER_ASIO_HPP - -#include -#include -#include - -namespace boost { -namespace beast2 { - -/** The Asio-aware router type -*/ -template -using router_asio = basic_router>; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/serve_redirect.hpp b/include/boost/beast2/server/router_corosio.hpp similarity index 59% rename from include/boost/beast2/server/serve_redirect.hpp rename to include/boost/beast2/server/router_corosio.hpp index 61cf9356..d091128d 100644 --- a/include/boost/beast2/server/serve_redirect.hpp +++ b/include/boost/beast2/server/router_corosio.hpp @@ -7,21 +7,19 @@ // Official repository: https://github.com/cppalliance/beast2 // -#ifndef BOOST_BEAST2_SERVER_SERVE_REDIRECT_HPP -#define BOOST_BEAST2_SERVER_SERVE_REDIRECT_HPP +#ifndef BOOST_BEAST2_SERVER_ROUTER_COROSIO_HPP +#define BOOST_BEAST2_SERVER_ROUTER_COROSIO_HPP #include -#include +#include +#include namespace boost { namespace beast2 { -struct serve_redirect -{ - BOOST_BEAST2_DECL - system::error_code - operator()(Request&, Response&) const; -}; +/** The Corosio-aware router type +*/ +using router_corosio = http::router; } // beast2 } // boost diff --git a/include/boost/beast2/server/router_types.hpp b/include/boost/beast2/server/router_types.hpp deleted file mode 100644 index 3ee8df91..00000000 --- a/include/boost/beast2/server/router_types.hpp +++ /dev/null @@ -1,327 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_ROUTER_STATE_HPP -#define BOOST_BEAST2_SERVER_ROUTER_STATE_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -/** The result type returned by a route handler. - - Route handlers use this type to report errors that prevent - normal processing. A handler must never return a non-failing - (i.e. `ec.failed() == false`) value. Returning a default-constructed - `system::error_code` is disallowed; handlers that complete - successfully must instead return a valid @ref route result. -*/ -using route_result = system::error_code; - -/** Route handler return values - - These values determine how the caller proceeds after invoking - a route handler. Each enumerator represents a distinct control - action—whether the request was handled, should continue to the - next route, transfers ownership of the session, or signals that - the connection should be closed. -*/ -enum class route -{ - /** The handler requests that the connection be closed. - - No further requests will be processed. The caller should - close the connection once the current response, if any, - has been sent. - */ - close = 1, - - /** The handler completed the request. - - The response has been fully transmitted, and no further - handlers or routes will be invoked. The caller should continue - by either reading the next request on a persistent connection - or closing the session if it is not keep-alive. - */ - complete, - - /** The handler detached from the session. - - Ownership of the session or stream has been transferred to - the handler. The caller will not perform further I/O or manage - the connection after this return value. - */ - detach, - - /** The handler declined to process the request. - - The handler chose not to generate a response. The caller - continues invoking the remaining handlers in the same route - until one returns @ref send. If none do, the caller proceeds - to evaluate the next matching route. - - This value is returned by @ref basic_router::dispatch if no - handlers in any route handle the request. - */ - next, - - /** The handler declined the current route. - - The handler wishes to skip any remaining handlers in the - current route and move on to the next matching route. The - caller stops invoking handlers in this route and resumes - evaluation with the next candidate route. - */ - next_route, - - /** The request was handled. - - The route handler processed the request and prepared - the response serializer. The caller will send the response - before reading the next request or closing the connection. - */ - send -}; - -//------------------------------------------------ - -} // beast2 -namespace system { -template<> -struct is_error_code_enum< - ::boost::beast2::route> -{ - static bool const value = true; -}; -} // system -namespace beast2 { - -namespace detail { -struct BOOST_SYMBOL_VISIBLE route_cat_type - : system::error_category -{ - BOOST_BEAST2_DECL const char* name( - ) const noexcept override; - BOOST_BEAST2_DECL std::string message( - int) const override; - BOOST_BEAST2_DECL char const* message( - int, char*, std::size_t - ) const noexcept override; - bool failed( int ) const noexcept override - { return false; } - BOOST_SYSTEM_CONSTEXPR route_cat_type() - : error_category(0x51c90d393754ecdf ) - { - } -}; -BOOST_BEAST2_DECL extern route_cat_type route_cat; -} // detail - -inline -BOOST_SYSTEM_CONSTEXPR -system::error_code -make_error_code(route ev) noexcept -{ - return system::error_code{static_cast< - std::underlying_type::type>(ev), - detail::route_cat}; -} - -//------------------------------------------------ - -class resumer; - -/** Function to detach a route handler from its session - - This holds an reference to an implementation - which detaches the handler from its session. -*/ -class detacher -{ -public: - /** Base class of the implementation - */ - struct owner - { - virtual resumer do_detach() = 0; - virtual void do_resume(route_result const&) = 0; - }; - - detacher() = default; - detacher(detacher const&) = default; - detacher& operator=(detacher const&) = default; - - explicit - detacher( - owner& who) noexcept - : p_(&who) - { - } - - /** Detach and invoke the given function - - The function will be invoked with this equivalent signature: - @code - void( resumer ); - @endcode - - @return A @ref route_result equal to @ref route::detach - */ - template - route_result - operator()(F&& f); - -private: - friend resumer; - owner* p_ = nullptr; -}; - -//------------------------------------------------ - -/** Function to resume a route handler's session - - This holds a reference to an implementation - which resumes the handler's session. The resume - function is returned by calling @ref detach. -*/ -class resumer -{ -public: - /** Constructor - - Default constructed resume functions will - be empty. Invoking an empty resume function - yields undefined behavior. - */ - resumer() = default; - - /** Constructor - - Copies of resume functions behave the same - as the original - */ - resumer(resumer const&) = default; - - /** Assignment - - Copies of resume functions behave the same - as the original - */ - resumer& operator=(resumer const&) = default; - - /** Constructor - */ - explicit - resumer( - detacher::owner& who) noexcept - : p_(&who) - { - } - - /** Resume the session - - A session is resumed as if the detached - handler returned the error code in `ec`. - - @param ec The error code to resume with. - */ - void operator()( - system::error_code const& ec = {}) const - { - p_->do_resume(ec); - } - -private: - detacher::owner* p_ = nullptr; -}; - -template -auto -detacher:: -operator()(F&& f) -> - route_result -{ - if(! p_) - detail::throw_logic_error(); - std::forward(f)(p_->do_detach()); - return route::detach; -} - -//------------------------------------------------ - -namespace detail { -class any_router; -} // detail -template -class basic_router; - -/** Base class for request objects - - This is a required public base for any `Request` - type used with @ref basic_router. -*/ -class basic_request -{ -public: - /** The mount path of the current router - - This is the portion of the request path - which was matched to select the handler. - The remaining portion is available in - @ref path. - */ - core::string_view base_path; - - /** The current pathname, relative to the base path - */ - core::string_view path; - -private: - friend class detail::any_router; - struct match_result; - http_proto::method verb_ = - http_proto::method::unknown; - std::string verb_str_; - std::string decoded_path_; - bool addedSlash_ = false; - bool case_sensitive = false; - bool strict = false; -}; - -//----------------------------------------------- - -/** Base class for response objects - - This is a required public base for any `Response` - type used with @ref basic_router. -*/ -class basic_response -{ -private: - friend class detail::any_router; - template - friend class basic_router; - - std::size_t pos_ = 0; - std::size_t resume_ = 0; - system::error_code ec_; - unsigned int opt_ = 0; -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/serve_static.hpp b/include/boost/beast2/server/serve_static.hpp index bba18c16..abe09da5 100644 --- a/include/boost/beast2/server/serve_static.hpp +++ b/include/boost/beast2/server/serve_static.hpp @@ -11,7 +11,7 @@ #define BOOST_BEAST2_SERVER_SERVE_STATIC_HPP #include -#include +#include namespace boost { namespace beast2 { @@ -164,7 +164,8 @@ struct serve_static indicate the request was not handled. */ BOOST_BEAST2_DECL - system::error_code operator()(Request&, Response&) const; + system::error_code operator()( + http::route_params&) const; private: struct impl; diff --git a/include/boost/beast2/server/worker_ssl.hpp b/include/boost/beast2/server/worker_ssl.hpp deleted file mode 100644 index 0d0f38af..00000000 --- a/include/boost/beast2/server/worker_ssl.hpp +++ /dev/null @@ -1,264 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_WORKER_SSL_HPP -#define BOOST_BEAST2_SERVER_WORKER_SSL_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { - -namespace asio { -namespace ip { -class tcp; // forward declaration -} // ip -} // asio - -namespace beast2 { - -template< - class Executor, - class Protocol = asio::ip::tcp -> -class worker_ssl : public http_stream< - ssl_stream> -> -{ -public: - using executor_type = Executor; - using protocol_type = Protocol; - using socket_type = - asio::basic_stream_socket; - using stream_type = ssl_stream; - - using acceptor_config = beast2::acceptor_config; - - template< - class Executor0, - class = typename std::enable_if::value>::type - > - worker_ssl( - workers_base& wb, - Executor0 const& ex, - asio::ssl::context& ssl_ctx, - router_asio rr); - - application& app() noexcept - { - return wb_.app(); - } - - socket_type& socket() noexcept - { - return stream_.next_layer(); - } - - typename Protocol::endpoint& - endpoint() noexcept - { - return ep_; - } - - /** Cancel all outstanding I/O - */ - void cancel(); - - // Called when an incoming connection is accepted - void on_accept(acceptor_config const* pconfig); - - void on_handshake( - acceptor_config const* pconfig, - system::error_code const& ec); - - void on_shutdown(system::error_code ec); - - void do_fail( - core::string_view s, system::error_code const& ec); - - void reset(); - -private: - /** Called when the logical session ends - */ - void do_close(system::error_code const& ec); - - workers_base& wb_; - asio::ssl::context& ssl_ctx_; - stream_type stream_; - typename Protocol::endpoint ep_; -}; - -//------------------------------------------------ - -template -template -worker_ssl:: -worker_ssl( - workers_base& wb, - Executor0 const& ex, - asio::ssl::context& ssl_ctx, - router_asio rr) - : http_stream( - wb.app(), - stream_, - std::move(rr), - [this](system::error_code const& ec) - { - this->do_close(ec); - }) - , wb_(wb) - , ssl_ctx_(ssl_ctx) - , stream_(Executor(ex), ssl_ctx) -{ -} - -template -void -worker_ssl:: -cancel() -{ - system::error_code ec; - stream_.next_layer().cancel(ec); -} - -//-------------------------------------------- - -// Called when an incoming connection is accepted -template -void -worker_ssl:: -on_accept(acceptor_config const* pconfig) -{ - BOOST_ASSERT(stream_.get_executor().running_in_this_thread()); - // VFALCO TODO timeout - stream_.set_ssl(pconfig->is_ssl); - if(! pconfig->is_ssl) - return this->on_accept(*pconfig); - return stream_.stream().async_handshake( - asio::ssl::stream_base::server, - asio::prepend(call_mf( - &worker_ssl::on_handshake, this), pconfig)); -} - -template -void -worker_ssl:: -on_handshake( - acceptor_config const* pconfig, - system::error_code const& ec) -{ - if(ec.failed()) - return do_fail("worker_ssl::on_handshake", ec); - - LOG_TRC(this->sect_)( - "{} worker_ssl::on_handshake", - this->id()); - - this->on_accept(*pconfig); -} - -template -void -worker_ssl:: -on_shutdown(system::error_code ec) -{ - if(ec.failed()) - return do_fail("worker_ssl::on_shutdown", ec); - - LOG_TRC(this->sect_)( - "{} worker_ssl::on_shutdown", - this->id()); - - stream_.next_layer().shutdown( - asio::socket_base::shutdown_both, ec); - // error ignored - - reset(); - wb_.do_idle(this); -} - -template -void -worker_ssl:: -do_fail( - core::string_view s, system::error_code const& ec) -{ - reset(); - - if(ec == asio::error::operation_aborted) - { - LOG_TRC(this->sect_)( - "{} {}: {}", - this->id(), s, ec.message()); - // this means the worker was stopped, don't submit new work - return; - } - - LOG_DBG(this->sect_)( - "{} {}: {}", - this->id(), s, ec.message()); - wb_.do_idle(this); -} - -template -void -worker_ssl:: -reset() -{ - // Clean up any previous connection. - system::error_code ec; - stream_.next_layer().close(ec); - - // asio::ssl::stream has an internal state which cannot be reset. - // In order to perform the handshake again, we destroy the old - // object and assign a new one, in a way that preserves the - // original socket to avoid churning file handles. - // - stream_ = stream_type(std::move(stream_.next_layer()), ssl_ctx_); -} - -/** Close the connection to end the session -*/ -template -void -worker_ssl:: -do_close(system::error_code const& ec) -{ - if(! ec.failed()) - { - if(! this->pconfig_->is_ssl) - { - reset(); - wb_.do_idle(this); - return; - } - stream_.stream().async_shutdown(call_mf( - &worker_ssl::on_shutdown, this)); - return; - } - - do_fail("worker_ssl::do_close", ec); -} - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/server/workers.hpp b/include/boost/beast2/server/workers.hpp deleted file mode 100644 index 40cd76c3..00000000 --- a/include/boost/beast2/server/workers.hpp +++ /dev/null @@ -1,316 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SERVER_WORKERS_HPP -#define BOOST_BEAST2_SERVER_WORKERS_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -class BOOST_SYMBOL_VISIBLE - workers_base -{ -public: - BOOST_BEAST2_DECL - virtual ~workers_base(); - - virtual capy::application& app() noexcept = 0; - virtual void do_idle(void* worker) = 0; -}; - -/** A set of accepting sockets and their workers. - - This implements a set of listening ports as a server service. An array of - workers created upon construction is used to accept incoming connections - and handle their sessions. - - @par Worker exemplar - @code - template< class Executor > - struct Worker - { - using executor_type = Executor; - using protocol_type = asio::ip::tcp; - using socket_type = asio::basic_stream_socket; - using acceptor_config = beast2::acceptor_config; - - asio::basic_stream_socket& socket() noexcept; - typename protocol_type::endpoint& endpoint() noexcept; - - void on_accept(); - }; - @endcode - - @tparam Executor The type of executor used by acceptor sockets. - @tparam Worker The type of worker to use. -*/ -template -class workers - : public workers_base -{ -public: - using executor_type = typename Worker::executor_type; - using protocol_type = typename Worker::protocol_type; - using acceptor_type = asio::basic_socket_acceptor; - using acceptor_config = typename Worker::acceptor_config; - using socket_type = asio::basic_stream_socket; - - ~workers() - { - } - - /** Constructor - - @param app The @ref application which holds this part - @param ex The executor to use for acceptor sockets - @param concurrency The number of threads calling io_context::run - @param num_workers The number of workers to construct - @param args Arguments forwarded to each worker's constructor - */ - template - workers( - capy::application& app, - Executor1 const& ex, - unsigned concurrency, - std::size_t num_workers, - Args&&... args); - - /** Add an acceptor - */ - template - void - emplace( - acceptor_config&& config, - Args&&... args) - { - va_.emplace_back( - concurrency_, - std::move(config), - acceptor_type(ex_, - std::forward(args)...)); - } - - void start(); - void stop(); - -private: - struct acceptor; - struct worker; - - capy::application& app() noexcept override; - void do_idle(void*) override; - void do_accepts(); - void on_accept(acceptor*, worker*, - system::error_code const&); - void do_stop(); - - capy::application& app_; - section sect_; - executor_type ex_; - fixed_array vw_; - std::vector va_; - worker* idle_ = nullptr; - std::size_t n_idle_ = 0; - unsigned concurrency_; - bool stop_ = false; -}; - -//------------------------------------------------ - -template -struct workers:: - acceptor -{ - template - explicit - acceptor( - std::size_t concurrency, - acceptor_config&& config_, - acceptor_type&& sock_) - : config(std::move(config_)) - , sock(std::move(sock_)) - , need(concurrency) - { - } - - acceptor_config config; - asio::basic_socket_acceptor< - protocol_type, executor_type> sock; - std::size_t need; // number of accepts we need -}; - -template -struct workers:: - worker -{ - worker* next; - Worker w; - - template - explicit worker( - worker* next_, Args&&... args) - : next(next_) - , w(std::forward(args)...) - { - } -}; - -//------------------------------------------------ - -template -template -workers:: -workers( - capy::application& app, - Executor1 const& ex, - unsigned concurrency, - std::size_t num_workers, - Args&&... args) - : app_(app) - , sect_(use_log_service(app).get_section("workers")) - , ex_(executor_type(ex)) - , vw_(num_workers) - , concurrency_(concurrency) -{ - while(! vw_.is_full()) - idle_ = &vw_.emplace_back(idle_, *this, - std::forward(args)...); - n_idle_ = vw_.size(); -} - -template -capy::application& -workers:: -app() noexcept -{ - return app_; -} - -template -void -workers:: -do_idle(void* pw) -{ - asio::dispatch(ex_, - [this, pw]() - { - // recover the `worker` pointer without using offsetof - worker* w = vw_.data() + ( - reinterpret_cast(pw) - - reinterpret_cast(vw_.data())) / - sizeof(worker); - // push - w->next = idle_; - idle_ = w; - ++n_idle_; - do_accepts(); - }); -} -template -void -workers:: -start() -{ - asio::dispatch(ex_, call_mf(&workers::do_accepts, this)); -} - -template -void -workers:: -stop() -{ - asio::dispatch(ex_, call_mf(&workers::do_stop, this)); -} - -template -void -workers:: -do_accepts() -{ - BOOST_ASSERT(ex_.running_in_this_thread()); - if(stop_) - return; - if(idle_) - { - for(auto& a : va_) - { - while(a.need > 0) - { - --a.need; - // pop - auto pw = idle_; - idle_ = idle_->next; - --n_idle_; - a.sock.async_accept(pw->w.socket(), pw->w.endpoint(), - asio::prepend(call_mf(&workers::on_accept, this), &a, pw)); - if(! idle_) - goto busy; - } - } - return; - } -busy: - // all workers are busy - // VFALCO log to warn the server admin? - return; -} - -template -void -workers:: -on_accept( - acceptor* pa, - worker* pw, - system::error_code const& ec) -{ - BOOST_ASSERT(ex_.running_in_this_thread()); - ++pa->need; - if(ec.failed()) - { - // push - pw->next = idle_; - idle_ = pw; - ++n_idle_; - LOG_DBG(sect_)("async_accept: {}", ec.message()); - return do_accepts(); - } - do_accepts(); - asio::dispatch(pw->w.socket().get_executor(), asio::prepend( - call_mf(&Worker::on_accept, &pw->w), &pa->config)); -} - -template -void -workers:: -do_stop() -{ - stop_ = true; - - for(auto& a : va_) - { - system::error_code ec; - a.sock.cancel(ec); // error ignored - } - for(auto& w : vw_) - w.w.cancel(); -} - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/ssl_stream.hpp b/include/boost/beast2/ssl_stream.hpp deleted file mode 100644 index b65fd9f4..00000000 --- a/include/boost/beast2/ssl_stream.hpp +++ /dev/null @@ -1,196 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_SSL_SOCKET_HPP -#define BOOST_BEAST2_SSL_SOCKET_HPP - -#include -#include -#include - -namespace boost { -namespace beast2 { - -/** A stream which can be either plain or SSL -*/ -template -class ssl_stream -{ - struct initiate_async_read_some; - struct initiate_async_write_some; - -public: - using ssl_stream_type = asio::ssl::stream; - - using next_layer_type = typename ssl_stream_type::next_layer_type; - - using lowest_layer_type = typename ssl_stream_type::lowest_layer_type; - - using executor_type = typename ssl_stream_type::executor_type; - - /** Destructor - - @note A @c stream object must not be destroyed while there are - pending asynchronous operations associated with it. - */ - ~ssl_stream() = default; - - ssl_stream(ssl_stream&&) = default; - - ssl_stream& operator=(ssl_stream&& other) = default; - - template - explicit - ssl_stream(Args&&... args) - : stream_(std::forward(args)...) - { - } - - executor_type get_executor() noexcept - { - return stream_.get_executor(); - } - - next_layer_type& next_layer() - { - return stream_.next_layer(); - } - - next_layer_type const& next_layer() const - { - return stream_.next_layer(); - } - - lowest_layer_type& lowest_layer() - { - return stream_.lowest_layer(); - } - - lowest_layer_type const& lowest_layer() const - { - return stream_.lowest_layer(); - } - - ssl_stream_type& stream() - { - return stream_; - } - - ssl_stream_type const& stream() const - { - return stream_; - } - - void set_ssl(bool ssl) noexcept - { - is_ssl_ = ssl; - } - - bool is_ssl() const noexcept - { - return is_ssl_; - } - - template > - auto async_read_some(MutableBufferSequence const& buffers, - ReadToken&& token = asio::default_completion_token_t()) -> - decltype(asio::async_initiate( - std::declval(), token, buffers)) - { - return asio::async_initiate( - initiate_async_read_some(this), token, buffers); - } - - template > - auto async_write_some(ConstBufferSequence const& buffers, - WriteToken&& token = asio::default_completion_token_t()) -> - decltype(asio::async_initiate( - std::declval(), token, buffers)) - { - return asio::async_initiate( - initiate_async_write_some(this), token, buffers); - } - -private: - struct initiate_async_read_some - { - using executor_type = typename ssl_stream::executor_type; - - explicit initiate_async_read_some( - ssl_stream* self) noexcept - : self_(self) - { - } - - executor_type get_executor() const noexcept - { - return self_->get_executor(); - } - - template - void operator()(ReadHandler&& handler, - MutableBufferSequence const& buffers) const - { - if(self_->is_ssl_) - self_->stream_.async_read_some(buffers, - std::forward(handler)); - else - self_->next_layer().async_read_some(buffers, - std::forward(handler)); - } - - ssl_stream* self_; - }; - - struct initiate_async_write_some - { - using executor_type = typename ssl_stream::executor_type; - - explicit initiate_async_write_some( - ssl_stream* self) noexcept - : self_(self) - { - } - - executor_type get_executor() const noexcept - { - return self_->get_executor(); - } - - template - void operator()(WriteHandler&& handler, - ConstBufferSequence const& buffers) const - { - if(self_->is_ssl_) - self_->stream_.async_write_some(buffers, - std::forward(handler)); - else - self_->next_layer().async_write_some(buffers, - std::forward(handler)); - } - - ssl_stream* self_; - }; - - ssl_stream_type stream_; - bool is_ssl_ = false; -}; - -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/test/detail/service_base.hpp b/include/boost/beast2/test/detail/service_base.hpp deleted file mode 100644 index b82cb825..00000000 --- a/include/boost/beast2/test/detail/service_base.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppaliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_DETAIL_SERVICE_BASE_HPP -#define BOOST_BEAST2_TEST_DETAIL_SERVICE_BASE_HPP - -#include - -namespace boost { -namespace beast2 { -namespace test { -namespace detail { - -template -struct service_base : asio::execution_context::service -{ - static asio::execution_context::id const id; - - explicit - service_base(asio::execution_context& ctx) - : asio::execution_context::service(ctx) - { - } -}; - -template -asio::execution_context::id const service_base::id; - -} // detail -} // test -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/test/detail/stream_state.hpp b/include/boost/beast2/test/detail/stream_state.hpp deleted file mode 100644 index fc73055e..00000000 --- a/include/boost/beast2/test/detail/stream_state.hpp +++ /dev/null @@ -1,241 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// Copyright (c) 2020 Richard Hodges (hodges.r@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppaliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_DETAIL_STREAM_STATE_HPP -#define BOOST_BEAST2_TEST_DETAIL_STREAM_STATE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace boost { -namespace beast2 { -namespace test { -namespace detail { - -struct stream_state; - -struct stream_service_impl -{ - std::mutex m_; - std::vector v_; - - void - remove(stream_state& impl); -}; - -//------------------------------------------------------------------------------ - -class stream_service - : public beast2::test::detail::service_base -{ - boost::shared_ptr sp_; - - void - shutdown() override; - -public: - explicit - stream_service(asio::execution_context& ctx); - - static - auto - make_impl( - asio::any_io_executor exec, - test::fail_count* fc) -> - boost::shared_ptr; -}; - -//------------------------------------------------------------------------------ - -struct stream_read_op_base -{ - virtual ~stream_read_op_base() = default; - virtual void operator()(system::error_code ec) = 0; -}; - -//------------------------------------------------------------------------------ - -enum class stream_status -{ - ok, - eof, -}; - -//------------------------------------------------------------------------------ - -struct stream_state -{ - asio::any_io_executor exec; - boost::weak_ptr wp; - std::mutex m; - std::string storage; - buffers::string_buffer b; - std::condition_variable cv; - std::unique_ptr op; - stream_status code = stream_status::ok; - fail_count* fc = nullptr; - std::size_t nread = 0; - std::size_t nread_bytes = 0; - std::size_t nwrite = 0; - std::size_t nwrite_bytes = 0; - std::size_t read_max = - (std::numeric_limits::max)(); - std::size_t write_max = - (std::numeric_limits::max)(); - - stream_state( - asio::any_io_executor exec_, - boost::weak_ptr wp_, - fail_count* fc_); - - stream_state(stream_state&&) = delete; - - ~stream_state(); - - void - remove() noexcept; - - void - notify_read(); -}; - -//------------------------------------------------------------------------------ - -inline -stream_service:: -stream_service(asio::execution_context& ctx) - : beast2::test::detail::service_base(ctx) - , sp_(boost::make_shared()) -{ -} - -inline -void -stream_service:: -shutdown() -{ - std::vector> v; - std::lock_guard g1(sp_->m_); - v.reserve(sp_->v_.size()); - for(auto p : sp_->v_) - { - std::lock_guard g2(p->m); - v.emplace_back(std::move(p->op)); - p->code = detail::stream_status::eof; - } -} - -inline -auto -stream_service:: -make_impl( - asio::any_io_executor exec, - test::fail_count* fc) -> - boost::shared_ptr -{ -#if defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) - auto& ctx = exec.context(); -#else - auto& ctx = asio::query( - exec, - asio::execution::context); -#endif - auto& svc = asio::use_service(ctx); - auto sp = boost::make_shared(exec, svc.sp_, fc); - std::lock_guard g(svc.sp_->m_); - svc.sp_->v_.push_back(sp.get()); - return sp; -} - -//------------------------------------------------------------------------------ - -inline -void -stream_service_impl:: -remove(stream_state& impl) -{ - std::lock_guard g(m_); - *std::find( - v_.begin(), v_.end(), - &impl) = std::move(v_.back()); - v_.pop_back(); -} - -//------------------------------------------------------------------------------ - -inline -stream_state:: -stream_state( - asio::any_io_executor exec_, - boost::weak_ptr wp_, - fail_count* fc_) - : exec(std::move(exec_)) - , wp(std::move(wp_)) - , b(&storage) - , fc(fc_) -{ -} - -inline -stream_state:: -~stream_state() -{ - // cancel outstanding read - if(op != nullptr) - (*op)(asio::error::operation_aborted); -} - -inline -void -stream_state:: -remove() noexcept -{ - auto sp = wp.lock(); - - // If this goes off, it means the lifetime of a test::stream object - // extended beyond the lifetime of the associated execution context. - BOOST_ASSERT(sp); - - sp->remove(*this); -} - -inline -void -stream_state:: -notify_read() -{ - if(op) - { - auto op_ = std::move(op); - op_->operator()(system::error_code{}); - } - else - { - cv.notify_all(); - } -} -} // detail -} // test -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/test/impl/stream.hpp b/include/boost/beast2/test/impl/stream.hpp deleted file mode 100644 index 5144609d..00000000 --- a/include/boost/beast2/test/impl/stream.hpp +++ /dev/null @@ -1,585 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppaliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_IMPL_STREAM_HPP -#define BOOST_BEAST2_TEST_IMPL_STREAM_HPP - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace boost { -namespace beast2 { -namespace test { - -namespace detail -{ -template -struct extract_executor_op -{ - To operator()(asio::any_io_executor& ex) const - { - assert(ex.template target()); - return *ex.template target(); - } -}; - -template<> -struct extract_executor_op -{ - asio::any_io_executor operator()(asio::any_io_executor& ex) const - { - return ex; - } -}; -} // detail - -template -template -class basic_stream::read_op : public detail::stream_read_op_base -{ - class lambda - { - Handler h_; - boost::weak_ptr wp_; - Buffers b_; - asio::executor_work_guard< - asio::associated_executor_t> wg2_; - - class cancellation_handler - { - public: - explicit - cancellation_handler( - boost::weak_ptr wp) - : wp_(std::move(wp)) - { - } - - void - operator()(asio::cancellation_type type) const - { - if(type != asio::cancellation_type::none) - { - if(auto sp = wp_.lock()) - { - std::unique_ptr p; - { - std::lock_guard lock(sp->m); - p = std::move(sp->op); - } - if(p != nullptr) - (*p)(asio::error::operation_aborted); - } - } - } - - private: - boost::weak_ptr wp_; - }; - - public: - template - lambda( - Handler_&& h, - boost::shared_ptr const& s, - Buffers const& b) - : h_(std::forward(h)) - , wp_(s) - , b_(b) - , wg2_(asio::get_associated_executor(h_, s->exec)) - { - auto c_slot = asio::get_associated_cancellation_slot(h_); - if (c_slot.is_connected()) - c_slot.template emplace(wp_); - } - - using allocator_type = asio::associated_allocator_t; - - allocator_type get_allocator() const noexcept - { - return asio::get_associated_allocator(h_); - } - - using cancellation_slot_type = - asio::associated_cancellation_slot_t; - - cancellation_slot_type - get_cancellation_slot() const noexcept - { - return asio::get_associated_cancellation_slot(h_, - asio::cancellation_slot()); - } - - void - operator()(system::error_code ec) - { - std::size_t bytes_transferred = 0; - auto sp = wp_.lock(); - if(! sp) - { - ec = asio::error::operation_aborted; - } - if(! ec) - { - std::lock_guard lock(sp->m); - BOOST_ASSERT(! sp->op); - if(sp->b.size() > 0) - { - bytes_transferred = - buffers::copy( - b_, sp->b.data(), sp->read_max); - sp->b.consume(bytes_transferred); - sp->nread_bytes += bytes_transferred; - } - else if (buffers::size(b_) > 0) - { - ec = asio::error::eof; - } - } - - asio::dispatch(wg2_.get_executor(), - asio::append(std::move(h_), ec, bytes_transferred)); - wg2_.reset(); - } - }; - - lambda fn_; - asio::executor_work_guard wg1_; - -public: - template - read_op( - Handler_&& h, - boost::shared_ptr const& s, - Buffers const& b) - : fn_(std::forward(h), s, b) - , wg1_(s->exec) - { - } - - void - operator()(system::error_code ec) override - { - asio::post(wg1_.get_executor(), asio::append(std::move(fn_), ec)); - wg1_.reset(); - } -}; - -template -struct basic_stream::run_read_op -{ - boost::shared_ptr const& in; - - using executor_type = typename basic_stream::executor_type; - - executor_type - get_executor() const noexcept - { - return detail::extract_executor_op()(in->exec); - } - - template< - class ReadHandler, - class MutableBufferSequence> - void - operator()( - ReadHandler&& h, - MutableBufferSequence const& buffers) - { - // If you get an error on the following line it means - // that your handler does not meet the documented type - // requirements for the handler. - - initiate_read( - in, - std::unique_ptr{ - new read_op< - typename std::decay::type, - MutableBufferSequence>( - std::move(h), - in, - buffers)}, - buffers::size(buffers)); - } -}; - -template -struct basic_stream::run_write_op -{ - boost::shared_ptr const& in_; - - using executor_type = typename basic_stream::executor_type; - - executor_type - get_executor() const noexcept - { - return detail::extract_executor_op()(in_->exec); - } - - template< - class WriteHandler, - class ConstBufferSequence> - void - operator()( - WriteHandler&& h, - boost::weak_ptr out_, - ConstBufferSequence const& buffers) - { - // If you get an error on the following line it means - // that your handler does not meet the documented type - // requirements for the handler. - - ++in_->nwrite; - auto const upcall = [&](system::error_code ec, std::size_t n) - { - asio::post(in_->exec, asio::append(std::move(h), ec, n)); - }; - - // test failure - system::error_code ec; - std::size_t n = 0; - if(in_->fc && in_->fc->fail(ec)) - return upcall(ec, n); - - // A request to write 0 bytes to a stream is a no-op. - if(buffers::size(buffers) == 0) - return upcall(ec, n); - - // connection closed - auto out = out_.lock(); - if(! out) - return upcall(asio::error::connection_reset, n); - - // copy buffers - n = std::min( - buffers::size(buffers), in_->write_max); - { - std::lock_guard lock(out->m); - n = buffers::copy(out->b.prepare(n), buffers); - out->b.commit(n); - out->nwrite_bytes += n; - out->notify_read(); - } - BOOST_ASSERT(! ec); - upcall(ec, n); - } -}; - -//------------------------------------------------------------------------------ - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, void(system::error_code, std::size_t)) -basic_stream:: -async_read_some( - MutableBufferSequence const& buffers, - ReadHandler&& handler) -{ - static_assert(asio::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence type requirements not met"); - - return asio::async_initiate< - ReadHandler, - void(system::error_code, std::size_t)>( - run_read_op{in_}, - handler, - buffers); -} - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, void(system::error_code, std::size_t)) -basic_stream:: -async_write_some( - ConstBufferSequence const& buffers, - WriteHandler&& handler) -{ - static_assert(asio::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence type requirements not met"); - - return asio::async_initiate< - WriteHandler, - void(system::error_code, std::size_t)>( - run_write_op{in_}, - handler, - out_, - buffers); -} - -//------------------------------------------------------------------------------ - -template -basic_stream -connect(stream& to, Arg1&& arg1, ArgN&&... argn) -{ - stream from{ - std::forward(arg1), - std::forward(argn)...}; - from.connect(to); - return from; -} - -template -auto basic_stream::get_executor() noexcept -> executor_type -{ - return detail::extract_executor_op()(in_->exec); -} - - -//------------------------------------------------------------------------------ - -template -void basic_stream::initiate_read( - boost::shared_ptr const& in_, - std::unique_ptr&& op, - std::size_t buf_size) -{ - std::unique_lock lock(in_->m); - - ++in_->nread; - if(in_->op != nullptr) - BOOST_THROW_EXCEPTION( - std::logic_error{"in_->op != nullptr"}); - - // test failure - system::error_code ec; - if(in_->fc && in_->fc->fail(ec)) - { - lock.unlock(); - (*op)(ec); - return; - } - - // A request to read 0 bytes from a stream is a no-op. - if(buf_size == 0 || buffers::size(in_->b.data()) > 0) - { - lock.unlock(); - (*op)(ec); - return; - } - - // deliver error - if(in_->code != detail::stream_status::ok) - { - lock.unlock(); - (*op)(asio::error::eof); - return; - } - - // complete when bytes available or closed - in_->op = std::move(op); -} - -//------------------------------------------------------------------------------ - -template -basic_stream:: -~basic_stream() -{ - close(); - in_->remove(); -} - -template -basic_stream:: -basic_stream(basic_stream&& other) -{ - auto in = detail::stream_service::make_impl( - other.in_->exec, other.in_->fc); - in_ = std::move(other.in_); - out_ = std::move(other.out_); - other.in_ = in; -} - - -template -basic_stream& -basic_stream:: -operator=(basic_stream&& other) -{ - close(); - auto in = detail::stream_service::make_impl( - other.in_->exec, other.in_->fc); - in_->remove(); - in_ = std::move(other.in_); - out_ = std::move(other.out_); - other.in_ = in; - return *this; -} - -//------------------------------------------------------------------------------ - -template -basic_stream:: -basic_stream(executor_type exec) - : in_(detail::stream_service::make_impl(std::move(exec), nullptr)) -{ -} - -template -basic_stream:: -basic_stream( - asio::io_context& ioc, - fail_count& fc) - : in_(detail::stream_service::make_impl(ioc.get_executor(), &fc)) -{ -} - -template -basic_stream:: -basic_stream( - asio::io_context& ioc, - core::string_view s) - : in_(detail::stream_service::make_impl(ioc.get_executor(), nullptr)) -{ - in_->b.commit(buffers::copy( - in_->b.prepare(s.size()), - buffers::const_buffer(s.data(), s.size()))); -} - -template -basic_stream:: -basic_stream( - asio::io_context& ioc, - fail_count& fc, - core::string_view s) - : in_(detail::stream_service::make_impl(ioc.get_executor(), &fc)) -{ - in_->b.commit(buffers::copy( - in_->b.prepare(s.size()), - buffers::const_buffer(s.data(), s.size()))); -} - -template -void -basic_stream:: -connect(basic_stream& remote) -{ - BOOST_ASSERT(! out_.lock()); - BOOST_ASSERT(! remote.out_.lock()); - std::lock(in_->m, remote.in_->m); - std::lock_guard guard1{in_->m, std::adopt_lock}; - std::lock_guard guard2{remote.in_->m, std::adopt_lock}; - out_ = remote.in_; - remote.out_ = in_; - in_->code = detail::stream_status::ok; - remote.in_->code = detail::stream_status::ok; -} - -template -core::string_view -basic_stream:: -str() const -{ - auto const bs = in_->b.data(); - if(buffers::size(bs) == 0) - return {}; - buffers::const_buffer const b = *asio::buffer_sequence_begin(bs); - return {static_cast(b.data()), b.size()}; -} - -template -void -basic_stream:: -append(core::string_view s) -{ - std::lock_guard lock{in_->m}; - in_->b.commit(buffers::copy( - in_->b.prepare(s.size()), - buffers::const_buffer(s.data(), s.size()))); -} - -template -void -basic_stream:: -clear() -{ - std::lock_guard lock{in_->m}; - in_->b.consume(in_->b.size()); -} - -template -void -basic_stream:: -close() -{ - { - std::lock_guard lock(in_->m); - in_->code = detail::stream_status::eof; - in_->notify_read(); - } - - // disconnect - { - auto out = out_.lock(); - out_.reset(); - - // notify peer - if(out) - { - std::lock_guard lock(out->m); - if(out->code == detail::stream_status::ok) - { - out->code = detail::stream_status::eof; - out->notify_read(); - } - } - } -} - -template -void -basic_stream:: -close_remote() -{ - std::lock_guard lock{in_->m}; - if(in_->code == detail::stream_status::ok) - { - in_->code = detail::stream_status::eof; - in_->notify_read(); - } -} - -//------------------------------------------------------------------------------ - -template -basic_stream -connect(basic_stream& to) -{ - basic_stream from(to.get_executor()); - from.connect(to); - return from; -} - -template -void -connect(basic_stream& s1, basic_stream& s2) -{ - s1.connect(s2); -} - -} // test -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/test/stream.hpp b/include/boost/beast2/test/stream.hpp deleted file mode 100644 index a5b636ea..00000000 --- a/include/boost/beast2/test/stream.hpp +++ /dev/null @@ -1,502 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppaliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_STREAM_HPP -#define BOOST_BEAST2_TEST_STREAM_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -// Forward declarations -namespace boost { -namespace asio { -namespace ssl { -template class stream; -} // ssl -} // asio -} // boost - -namespace boost { -namespace beast2 { -namespace test { - -/** A two-way socket useful for unit testing - - An instance of this class simulates a traditional socket, - while also providing features useful for unit testing. - Each endpoint maintains an independent buffer called - the input area. Writes from one endpoint append data - to the peer's pending input area. When an endpoint performs - a read and data is present in the input area, the data is - delivered to the blocking or asynchronous operation. Otherwise - the operation is blocked or deferred until data is made - available, or until the endpoints become disconnected. - - These streams may be used anywhere an algorithm accepts a - reference to a synchronous or asynchronous read or write - stream. It is possible to use a test stream in a call to - `asio::read_until`, or in a call to - @ref boost::beast2::http::async_write for example. - - As with Boost.Asio I/O objects, a @ref stream constructs - with a reference to the `asio::io_context` to use for - handling asynchronous I/O. For asynchronous operations, the - stream follows the same rules as a traditional asio socket - with respect to how completion handlers for asynchronous - operations are performed. - - To facilitate testing, these streams support some additional - features: - - @li The input area, represented by a @ref beast2::basic_flat_buffer, - may be directly accessed by the caller to inspect the contents - before or after the remote endpoint writes data. This allows - a unit test to verify that the received data matches. - - @li Data may be manually appended to the input area. This data - will delivered in the next call to - @ref stream::read_some or @ref stream::async_read_some. - This allows predefined test vectors to be set up for testing - read algorithms. - - @li The stream may be constructed with a fail count. The - stream will eventually fail with a predefined error after a - certain number of operations, where the number of operations - is controlled by the test. When a test loops over a range of - operation counts, it is possible to exercise every possible - point of failure in the algorithm being tested. When used - correctly the technique allows the tests to reach a high - percentage of code coverage. - - @par Thread Safety - @e Distinct @e objects: Safe.@n - @e Shared @e objects: Unsafe. - The application must also ensure that all asynchronous - operations are performed within the same implicit or explicit strand. - - @par Concepts - @li SyncReadStream - @li SyncWriteStream - @li AsyncReadStream - @li AsyncWriteStream -*/ -template -class basic_stream; - -template -class basic_stream -{ -public: - /// The type of the executor associated with the object. - using executor_type = - Executor; - - /// Rebinds the socket type to another executor. - template - struct rebind_executor - { - /// The socket type when rebound to the specified executor. - typedef basic_stream other; - }; - -private: - template - friend class basic_stream; - - boost::shared_ptr in_; - boost::weak_ptr out_; - - template - class read_op; - - struct run_read_op; - struct run_write_op; - - static - void - initiate_read( - boost::shared_ptr const& in, - std::unique_ptr&& op, - std::size_t buf_size); - -#if ! BOOST_BEAST2_DOXYGEN - // boost::asio::ssl::stream needs these - // DEPRECATED - template - friend class boost::asio::ssl::stream; - // DEPRECATED - using lowest_layer_type = basic_stream; - // DEPRECATED - lowest_layer_type& - lowest_layer() noexcept - { - return *this; - } - // DEPRECATED - lowest_layer_type const& - lowest_layer() const noexcept - { - return *this; - } -#endif - -public: - using buffer_type = buffers::string_buffer; - - /** Destructor - - If an asynchronous read operation is pending, it will - simply be discarded with no notification to the completion - handler. - - If a connection is established while the stream is destroyed, - the peer will see the error `asio::error::connection_reset` - when performing any reads or writes. - */ - ~basic_stream(); - - /** Move Constructor - - Moving the stream while asynchronous operations are pending - results in undefined behavior. - */ - basic_stream(basic_stream&& other); - - /** Move Constructor - - Moving the stream while asynchronous operations are pending - results in undefined behavior. - */ - template - basic_stream(basic_stream&& other) - : in_(std::move(other.in_)) - , out_(std::move(other.out_)) - { - BOOST_ASSERT(in_->exec.template target() != nullptr); - in_->exec = executor_type(*in_->exec.template target()); - } - - /** Move Assignment - - Moving the stream while asynchronous operations are pending - results in undefined behavior. - */ - basic_stream& - operator=(basic_stream&& other); - - template - basic_stream& - operator==(basic_stream&& other); - - /** Construct a stream - - The stream will be created in a disconnected state. - - @param context The `io_context` object that the stream will use to - dispatch handlers for any asynchronous operations. - */ - template ::value>::type> - explicit - basic_stream(ExecutionContext& context) - : basic_stream(context.get_executor()) - { - } - - /** Construct a stream - - The stream will be created in a disconnected state. - - @param exec The `executor` object that the stream will use to - dispatch handlers for any asynchronous operations. - */ - explicit - basic_stream(executor_type exec); - - /** Construct a stream - - The stream will be created in a disconnected state. - - @param ioc The `io_context` object that the stream will use to - dispatch handlers for any asynchronous operations. - - @param fc The @ref fail_count to associate with the stream. - Each I/O operation performed on the stream will increment the - fail count. When the fail count reaches its internal limit, - a simulated failure error will be raised. - */ - basic_stream( - asio::io_context& ioc, - fail_count& fc); - - /** Construct a stream - - The stream will be created in a disconnected state. - - @param ioc The `io_context` object that the stream will use to - dispatch handlers for any asynchronous operations. - - @param s A string which will be appended to the input area, not - including the null terminator. - */ - basic_stream( - asio::io_context& ioc, - core::string_view s); - - /** Construct a stream - - The stream will be created in a disconnected state. - - @param ioc The `io_context` object that the stream will use to - dispatch handlers for any asynchronous operations. - - @param fc The @ref fail_count to associate with the stream. - Each I/O operation performed on the stream will increment the - fail count. When the fail count reaches its internal limit, - a simulated failure error will be raised. - - @param s A string which will be appended to the input area, not - including the null terminator. - */ - basic_stream( - asio::io_context& ioc, - fail_count& fc, - core::string_view s); - - /// Establish a connection - void - connect(basic_stream& remote); - - /// Return the executor associated with the object. - executor_type - get_executor() noexcept; - - /// Set the maximum number of bytes returned by read_some - void - read_size(std::size_t n) noexcept - { - in_->read_max = n; - } - - /// Set the maximum number of bytes returned by write_some - void - write_size(std::size_t n) noexcept - { - in_->write_max = n; - } - - /// Direct input buffer access - buffer_type& - buffer() noexcept - { - return in_->b; - } - - /// Returns a string view representing the pending input data - core::string_view - str() const; - - /// Appends a string to the pending input data - void - append(core::string_view s); - - /// Clear the pending input area - void - clear(); - - /// Return the number of reads - std::size_t - nread() const noexcept - { - return in_->nread; - } - - /// Return the number of bytes read - std::size_t - nread_bytes() const noexcept - { - return in_->nread_bytes; - } - - /// Return the number of writes - std::size_t - nwrite() const noexcept - { - return in_->nwrite; - } - - /// Return the number of bytes written - std::size_t - nwrite_bytes() const noexcept - { - return in_->nwrite_bytes; - } - - /** Close the stream. - - The other end of the connection will see - `error::eof` after reading all the remaining data. - */ - void - close(); - - /** Close the other end of the stream. - - This end of the connection will see - `error::eof` after reading all the remaining data. - */ - void - close_remote(); - - /** Start an asynchronous read. - - This function is used to asynchronously read one or more bytes of data from - the stream. The function call always returns immediately. - - @param buffers The buffers into which the data will be read. Although the - buffers object may be copied as necessary, ownership of the underlying - buffers is retained by the caller, which must guarantee that they remain - valid until the handler is called. - - @param handler The completion handler to invoke when the operation - completes. The implementation takes ownership of the handler by - performing a decay-copy. The equivalent function signature of - the handler must be: - @code - void handler( - system::error_code const& ec, // Result of operation. - std::size_t bytes_transferred // Number of bytes read. - ); - @endcode - If the handler has an associated immediate executor, - an immediate completion will be dispatched to it. - Otherwise, the handler will not be invoked from within - this function. Invocation of the handler will be performed - by dispatching to the immediate executor. If no - immediate executor is specified, this is equivalent - to using `asio::post`. - @note The `async_read_some` operation may not read all of the requested number of - bytes. Consider using the function `asio::async_read` if you need - to ensure that the requested amount of data is read before the asynchronous - operation completes. - */ - template< - class MutableBufferSequence, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(system::error_code, std::size_t)) ReadHandler - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, void(system::error_code, std::size_t)) - async_read_some( - MutableBufferSequence const& buffers, - ReadHandler&& handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); - - /** Start an asynchronous write. - - This function is used to asynchronously write one or more bytes of data to - the stream. The function call always returns immediately. - - @param buffers The data to be written to the stream. Although the buffers - object may be copied as necessary, ownership of the underlying buffers is - retained by the caller, which must guarantee that they remain valid until - the handler is called. - - @param handler The completion handler to invoke when the operation - completes. The implementation takes ownership of the handler by - performing a decay-copy. The equivalent function signature of - the handler must be: - @code - void handler( - system::error_code const& ec, // Result of operation. - std::size_t bytes_transferred // Number of bytes written. - ); - @endcode - If the handler has an associated immediate executor, - an immediate completion will be dispatched to it. - Otherwise, the handler will not be invoked from within - this function. Invocation of the handler will be performed - by dispatching to the immediate executor. If no - immediate executor is specified, this is equivalent - to using `asio::post`. - @note The `async_write_some` operation may not transmit all of the data to - the peer. Consider using the function `asio::async_write` if you need - to ensure that all data is written before the asynchronous operation completes. - */ - template< - class ConstBufferSequence, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(system::error_code, std::size_t)) WriteHandler - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, void(system::error_code, std::size_t)) - async_write_some( - ConstBufferSequence const& buffers, - WriteHandler&& handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); -}; - -#if ! BOOST_BEAST2_DOXYGEN -template -void -beast2_close_socket(basic_stream& s) -{ - s.close(); -} -#endif - -#if BOOST_BEAST2_DOXYGEN -/** Return a new stream connected to the given stream - - @param to The stream to connect to. - - @param args Optional arguments forwarded to the new stream's constructor. - - @return The new, connected stream. -*/ -template -template -basic_stream -connect(basic_stream& to, Args&&... args); - -#else -template -basic_stream -connect(basic_stream& to); - -template -void -connect(basic_stream& s1, basic_stream& s2); - -template -basic_stream -connect(basic_stream& to, Arg1&& arg1, ArgN&&... argn); -#endif - -using stream = basic_stream<>; - -} // test -} // beast2 -} // boost - -#include - -#endif diff --git a/include/boost/beast2/test/tcp.hpp b/include/boost/beast2/test/tcp.hpp deleted file mode 100644 index 6c1a1d95..00000000 --- a/include/boost/beast2/test/tcp.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppaliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_TCP_HPP -#define BOOST_BEAST2_TEST_TCP_HPP - -#include - -#include -#include - -namespace boost { -namespace beast2 { -namespace test { - -/** Connect two TCP sockets together. -*/ -template -bool -connect( - asio::io_context& ioc, - asio::basic_stream_socket& s1, - asio::basic_stream_socket& s2) - -{ - asio::basic_socket_acceptor< - asio::ip::tcp, Executor> a(s1.get_executor()); - auto ep = asio::ip::tcp::endpoint( - asio::ip::make_address_v4("127.0.0.1"), 0); - a.open(ep.protocol()); - a.set_option( - asio::socket_base::reuse_address(true)); - a.bind(ep); - a.listen(0); - ep = a.local_endpoint(); - a.async_accept(s2, asio::detached); - s1.async_connect(ep, asio::detached); - ioc.run(); - ioc.restart(); - if(! s1.remote_endpoint() == s2.local_endpoint()) - return false; - if(! s2.remote_endpoint() == s1.local_endpoint()) - return false; - return true; -} - -} // test -} // beast2 -} // boost - -#endif diff --git a/include/boost/beast2/write.hpp b/include/boost/beast2/write.hpp deleted file mode 100644 index 5dd79c57..00000000 --- a/include/boost/beast2/write.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_WRITE_HPP -#define BOOST_BEAST2_WRITE_HPP - -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -/** Write HTTP data to a stream -*/ -template< - class AsyncWriteStream, - BOOST_ASIO_COMPLETION_TOKEN_FOR( - void(system::error_code, std::size_t)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( - typename AsyncWriteStream::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (system::error_code, std::size_t)) -async_write_some( - AsyncWriteStream& dest, - http_proto::serializer& sr, - CompletionToken&& token - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( - typename AsyncWriteStream::executor_type)); - -/** Write HTTP data to a stream -*/ -template< - class AsyncWriteStream, - BOOST_ASIO_COMPLETION_TOKEN_FOR( - void(system::error_code, std::size_t)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( - typename AsyncWriteStream::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (system::error_code, std::size_t)) -async_write( - AsyncWriteStream& dest, - http_proto::serializer& sr, - CompletionToken&& token - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( - typename AsyncWriteStream::executor_type)); - -#if 0 -/** -*/ -template< - class AsyncWriteStream, - class AsyncReadStream, - class CompletionCondition, - BOOST_ASIO_COMPLETION_TOKEN_FOR( - void(system::error_code, std::size_t)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( - typename AsyncWriteStream::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (system::error_code, std::size_t)) -async_relay_some( - AsyncWriteStream& dest, - AsyncReadStream& src, - CompletionCondition const& cond, - http_proto::serializer& sr, - CompletionToken&& token - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( - typename AsyncWriteStream::executor_type)); -#endif - -} // beast2 -} // boost - -#include - -#endif diff --git a/meta/explicit-failures-markup.xml b/meta/explicit-failures-markup.xml index bc7abccb..5422b7cd 100644 --- a/meta/explicit-failures-markup.xml +++ b/meta/explicit-failures-markup.xml @@ -3,18 +3,7 @@ - - - - - - - - - - - - C++11 is the minimum requirement. + C++20 is the minimum requirement. diff --git a/src/asio_io_context.cpp b/src/asio_io_context.cpp deleted file mode 100644 index 9ed07894..00000000 --- a/src/asio_io_context.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -namespace { - -/** Asio's io_context as an application part -*/ -class asio_io_context_impl - : public asio_io_context -{ -public: - using key_type = asio_io_context; - - ~asio_io_context_impl() - { - } - - asio_io_context_impl( - capy::application& app, - int num_threads) - : app_(app) - , num_threads_(num_threads) - , ioc_(num_threads) - , sigs_(ioc_.get_executor(), SIGINT, SIGTERM) - , work_(ioc_.get_executor()) - { - if(num_threads > 1) - vt_.resize(num_threads - 1); - } - - executor_type - get_executor() noexcept override - { - return ioc_.get_executor(); - } - - std::size_t - concurrency() const noexcept override - { - return num_threads_; - } - - void attach() override - { - // VFALCO exception catcher? - ioc_.run(); - - // VFALCO can't figure out where to put this - for(auto& t : vt_) - t.join(); - } - - void start() override - { - // Capture SIGINT and SIGTERM to - // perform a clean shutdown - sigs_.async_wait(call_mf( - &asio_io_context_impl::on_signal, this)); - - for(auto& t : vt_) - { - t = std::thread( - [&] - { - // VFALCO exception catcher? - ioc_.run(); - }); - } - } - - void stop() override - { - system::error_code ec; - sigs_.cancel(ec); // VFALCO should we use the 0-arg overload? - work_.reset(); - } - -private: - void - on_signal( - system::error_code const& ec, int) - { - if(ec == asio::error::operation_aborted) - return; - app_.stop(); - } - - capy::application& app_; - int num_threads_; - asio::io_context ioc_; - asio::signal_set sigs_; - asio::executor_work_guard< - asio::io_context::executor_type> work_; - std::vector vt_; -}; - -} // (anon) - -//------------------------------------------------ - -asio_io_context:: -~asio_io_context() = default; - -auto -install_single_threaded_asio_io_context( - capy::application& app) -> - asio_io_context& -{ - return app.emplace< - asio_io_context_impl>(app, 1); -} - -auto -install_multi_threaded_asio_io_context( - capy::application& app, - int num_threads) -> - asio_io_context& -{ - return app.emplace< - asio_io_context_impl>(app, num_threads); -} - -} // beast2 -} // boost diff --git a/src/error.cpp b/src/error.cpp index feccb695..d49d9d5b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -39,9 +39,9 @@ message( { switch(static_cast(code)) { - case error::success: return "http_proto::error::success"; + case error::success: return "http::error::success"; default: - return "http_proto::error::?"; + return "http::error::?"; } } diff --git a/src/http_server.cpp b/src/http_server.cpp new file mode 100644 index 00000000..9bacd76c --- /dev/null +++ b/src/http_server.cpp @@ -0,0 +1,231 @@ +// +// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/beast2 +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace beast2 { + +struct http_server::impl +{ + http::flat_router router; + capy::application app; + + impl(http::flat_router r) + : router(std::move(r)) + { + // VFALCO These ugly incantations are needed + // for http and will hopefully go away soon. + http::install_parser_service(app, + http::request_parser::config()); + http::install_serializer_service(app, + http::serializer::config()); + } +}; + +struct http_server:: + worker : tcp_server::worker_base +{ + http_server* srv; + corosio::io_context& ctx; + capy::strand strand; + corosio::socket sock; + corosio_route_params rp; + + worker( + corosio::io_context& ctx_, + http_server* srv_) + : srv(srv_) + , ctx(ctx_) + , strand(ctx_.get_executor()) + , sock(ctx_) + , rp(sock) + { + sock.open(); + rp.parser = http::request_parser(srv->impl_->app); + rp.serializer = http::serializer(srv->impl_->app); + } + + corosio::socket& socket() override + { + return sock; + } + + void run(launcher launch) override + { + launch(ctx.get_executor(), srv->do_session(*this)); + } + + capy::task> + read_header() + { + std::size_t total_bytes = 0; + system::error_code ec; + + for (;;) + { + // Try to parse what we have + rp.parser.parse(ec); + + if (ec == http::condition::need_more_input) + { + // Need to read more data + auto buf = rp.parser.prepare(); + auto [read_ec, n] = co_await sock.read_some(buf); + + if (read_ec) + co_return {read_ec, total_bytes}; + + if (n == 0) + { + // EOF + rp.parser.commit_eof(); + ec = {}; + } + else + { + rp.parser.commit(n); + total_bytes += n; + } + continue; + } + + if (ec.failed()) + co_return {ec, total_bytes}; + + if (rp.parser.got_header()) + co_return {{}, total_bytes}; + } + } + + void on_headers() + { + // Set up Request and Response objects + rp.req = rp.parser.get(); + rp.route_data.clear(); + rp.res.set_start_line( + http::status::ok, rp.req.version()); + rp.res.set_keep_alive(rp.req.keep_alive()); + rp.serializer.reset(); + + // Parse the URL + auto rv = urls::parse_uri_reference(rp.req.target()); + if (rv.has_error()) + { + rp.status(http::status::bad_request); + rp.set_body("Bad Request: " + rv.error().message()); + return; + } + + rp.url = rv.value(); + } +}; + +http_server:: +~http_server() +{ + delete impl_; +} + +http_server:: +http_server( + corosio::io_context& ctx, + std::size_t num_workers, + http::flat_router router) + : tcp_server(ctx, ctx.get_executor()) + , impl_(new impl(std::move(router))) +{ + wv_.reserve(num_workers); + for(std::size_t i = 0; i < num_workers; ++i) + wv_.emplace(ctx, this); +} + +capy::task +http_server:: +do_session( + worker& w) +{ + struct guard + { + corosio_route_params& rp; + + guard(corosio_route_params& rp_) + : rp(rp_) + { + } + + ~guard() + { + rp.parser.reset(); + rp.session_data.clear(); + rp.parser.start(); + } + }; + + guard g(w.rp); // clear things when session ends + + for(;;) + { + w.rp.parser.reset(); + w.rp.session_data.clear(); + w.rp.parser.start(); + + // Read HTTP request header + auto [ec, n] = co_await w.read_header(); + if (ec) + { + //LOG_TRC(sect_)("{} read_header: {}", id(), ec.message()); + break; + } + + //LOG_TRC(sect_)("{} read_header bytes={}", id(), n); + + // Process headers and dispatch + w.on_headers(); + + auto rv = co_await impl_->router.dispatch( + w.rp.req.method(), w.rp.url, w.rp); +#if 0 + do_respond(rv); + + // Write response + if (!rp.serializer.is_done()) + { + auto [wec, wn] = co_await write_response(); + if (wec) + { + LOG_TRC(sect_)("{} write_response: {}", id(), wec.message()); + break; + } + LOG_TRC(sect_)("{} write_response bytes={}", id(), wn); + } + + // Check keep-alive + if (!rp.res.keep_alive()) + break; +#endif + } +} + +} // beast2 +} // boost diff --git a/src/server/basic_router.cpp b/src/server/basic_router.cpp deleted file mode 100644 index ab515e9c..00000000 --- a/src/server/basic_router.cpp +++ /dev/null @@ -1,872 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include "src/server/route_rule.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -namespace detail { - -// VFALCO Temporarily here until Boost.URL merges the fix -static -bool -ci_is_equal( - core::string_view s0, - core::string_view s1) noexcept -{ - auto n = s0.size(); - if(s1.size() != n) - return false; - auto p1 = s0.data(); - auto p2 = s1.data(); - char a, b; - // fast loop - while(n--) - { - a = *p1++; - b = *p2++; - if(a != b) - goto slow; - } - return true; - do - { - a = *p1++; - b = *p2++; - slow: - if( grammar::to_lower(a) != - grammar::to_lower(b)) - return false; - } - while(n--); - return true; -} - - -//------------------------------------------------ -/* - -pattern target path(use) path(get) -------------------------------------------------- -/ / / -/ /api /api -/api /api / /api -/api /api/ / /api/ -/api /api/ / no-match strict -/api /api/v0 /v0 no-match -/api/ /api / /api -/api/ /api / no-match strict -/api/ /api/ / /api/ -/api/ /api/v0 /v0 no-match - -*/ - -//------------------------------------------------ - -any_router::any_handler::~any_handler() = default; - -//------------------------------------------------ - -/* -static -void -make_lower(std::string& s) -{ - for(auto& c : s) - c = grammar::to_lower(c); -} -*/ - -// decode all percent escapes -static -std::string -pct_decode( - urls::pct_string_view s) -{ - std::string result; - core::string_view sv(s); - result.reserve(s.size()); - auto it = sv.data(); - auto const end = it + sv.size(); - for(;;) - { - if(it == end) - break; - if(*it != '%') - { - result.push_back(*it++); - continue; - } - ++it; -#if 0 - // pct_string_view can never have invalid pct-encodings - if(it == end) - goto invalid; -#endif - auto d0 = urls::grammar::hexdig_value(*it++); -#if 0 - // pct_string_view can never have invalid pct-encodings - if( d0 < 0 || - it == end) - goto invalid; -#endif - auto d1 = urls::grammar::hexdig_value(*it++); -#if 0 - // pct_string_view can never have invalid pct-encodings - if(d1 < 0) - goto invalid; -#endif - result.push_back(d0 * 16 + d1); - } - return result; -#if 0 -invalid: - // can't get here, as received a pct_string_view - detail::throw_invalid_argument(); -#endif -} - -// decode all percent escapes except slashes '/' and '\' -static -std::string -pct_decode_path( - urls::pct_string_view s) -{ - std::string result; - core::string_view sv(s); - result.reserve(s.size()); - auto it = sv.data(); - auto const end = it + sv.size(); - for(;;) - { - if(it == end) - break; - if(*it != '%') - { - result.push_back(*it++); - continue; - } - ++it; -#if 0 - // pct_string_view can never have invalid pct-encodings - if(it == end) - goto invalid; -#endif - auto d0 = urls::grammar::hexdig_value(*it++); -#if 0 - // pct_string_view can never have invalid pct-encodings - if( d0 < 0 || - it == end) - goto invalid; -#endif - auto d1 = urls::grammar::hexdig_value(*it++); -#if 0 - // pct_string_view can never have invalid pct-encodings - if(d1 < 0) - goto invalid; -#endif - char c = d0 * 16 + d1; - if( c != '/' && - c != '\\') - { - result.push_back(c); - continue; - } - result.append(it - 3, 3); - } - return result; -#if 0 -invalid: - // can't get here, as received a pct_string_view - detail::throw_invalid_argument(); -#endif -} - -//------------------------------------------------ - -} // detail - -struct basic_request:: - match_result -{ - void adjust_path( - basic_request& req, - std::size_t n) - { - n_ = n; - if(n_ == 0) - return; - req.base_path = { - req.base_path.data(), - req.base_path.size() + n_ }; - if(n_ < req.path.size()) - { - req.path.remove_prefix(n_); - } - else - { - // append a soft slash - req.path = { req.decoded_path_.data() + - req.decoded_path_.size() - 1, 1}; - BOOST_ASSERT(req.path == "/"); - } - } - - void restore_path( - basic_request& req) - { - if( n_ > 0 && - req.addedSlash_ && - req.path.data() == - req.decoded_path_.data() + - req.decoded_path_.size() - 1) - { - // remove soft slash - req.path = { - req.base_path.data() + - req.base_path.size(), 0 }; - } - req.base_path.remove_suffix(n_); - req.path = { - req.path.data() - n_, - req.path.size() + n_ }; - } - -private: - std::size_t n_ = 0; // chars moved from path to base_path -}; - -//------------------------------------------------ - -namespace detail { - -// Matches a path against a pattern -struct any_router::matcher -{ - bool const end; // false for middleware - - matcher( - core::string_view pat, - bool end_) - : end(end_) - , decoded_pat_( - [&pat] - { - auto s = pct_decode(pat); - if( s.size() > 1 - && s.back() == '/') - s.pop_back(); - return s; - }()) - , slash_(pat == "/") - { - if(! slash_) - pv_ = grammar::parse( - decoded_pat_, path_rule).value(); - } - - /** Return true if req.path is a match - */ - bool operator()( - basic_request& req, - match_result& mr) const - { - BOOST_ASSERT(! req.path.empty()); - if( slash_ && ( - ! end || - req.path == "/")) - { - // params = {}; - mr.adjust_path(req, 0); - return true; - } - auto it = req.path.data(); - auto pit = pv_.segs.begin(); - auto const end_ = it + req.path.size(); - auto const pend = pv_.segs.end(); - while(it != end_ && pit != pend) - { - // prefix has to match - auto s = core::string_view(it, end_); - if(! req.case_sensitive) - { - if(pit->prefix.size() > s.size()) - return false; - s = s.substr(0, pit->prefix.size()); - //if(! grammar::ci_is_equal(s, pit->prefix)) - if(! ci_is_equal(s, pit->prefix)) - return false; - } - else - { - if(! s.starts_with(pit->prefix)) - return false; - } - it += pit->prefix.size(); - ++pit; - } - if(end) - { - // require full match - if( it != end_ || - pit != pend) - return false; - } - else if(pit != pend) - { - return false; - } - // number of matching characters - auto const n = it - req.path.data(); - mr.adjust_path(req, n); - return true; - } - -private: - stable_string decoded_pat_; - path_rule_t::value_type pv_; - bool slash_; -}; - -//------------------------------------------------ - -struct any_router::layer -{ - struct entry - { - handler_ptr handler; - - // only for end routes - http_proto::method verb = - http_proto::method::unknown; - std::string verb_str; - bool all; - - explicit entry( - handler_ptr h) noexcept - : handler(std::move(h)) - , all(true) - { - } - - entry( - http_proto::method verb_, - handler_ptr h) noexcept - : handler(std::move(h)) - , verb(verb_) - , all(false) - { - BOOST_ASSERT(verb != - http_proto::method::unknown); - } - - entry( - core::string_view verb_str_, - handler_ptr h) noexcept - : handler(std::move(h)) - , verb(http_proto::string_to_method(verb_str_)) - , all(false) - { - if(verb != http_proto::method::unknown) - return; - verb_str = verb_str_; - } - - bool match_method( - basic_request const& req) const noexcept - { - if(all) - return true; - if(verb != http_proto::method::unknown) - return req.verb_ == verb; - if(req.verb_ != http_proto::method::unknown) - return false; - return req.verb_str_ == verb_str; - } - }; - - matcher match; - std::vector entries; - - // middleware layer - layer( - core::string_view pat, - handler_list handlers) - : match(pat, false) - { - entries.reserve(handlers.n); - for(std::size_t i = 0; i < handlers.n; ++i) - entries.emplace_back(std::move(handlers.p[i])); - } - - // route layer - explicit layer( - core::string_view pat) - : match(pat, true) - { - } - - std::size_t count() const noexcept - { - std::size_t n = 0; - for(auto const& e : entries) - n += e.handler->count(); - return n; - } -}; - -//------------------------------------------------ - -struct any_router::impl -{ - std::atomic refs{1}; - std::vector layers; - opt_flags opt; - - explicit impl( - opt_flags opt_) noexcept - : opt(opt_) - { - } -}; - -//------------------------------------------------ - -any_router:: -~any_router() -{ - if(! impl_) - return; - if(--impl_->refs == 0) - delete impl_; -} - -any_router:: -any_router( - opt_flags opt) - : impl_(new impl(opt)) -{ -} - -any_router:: -any_router(any_router&& other) noexcept - :impl_(other.impl_) -{ - other.impl_ = nullptr; -} - -any_router:: -any_router(any_router const& other) noexcept -{ - impl_ = other.impl_; - ++impl_->refs; -} - -any_router& -any_router:: -operator=(any_router&& other) noexcept -{ - auto p = impl_; - impl_ = other.impl_; - other.impl_ = nullptr; - if(p && --p->refs == 0) - delete p; - return *this; -} - -any_router& -any_router:: -operator=(any_router const& other) noexcept -{ - auto p = impl_; - impl_ = other.impl_; - ++impl_->refs; - if(p && --p->refs == 0) - delete p; - return *this; -} - -//------------------------------------------------ - -std::size_t -any_router:: -count() const noexcept -{ - std::size_t n = 0; - for(auto const& i : impl_->layers) - for(auto const& e : i.entries) - n += e.handler->count(); - return n; -} - -auto -any_router:: -new_layer( - core::string_view pattern) -> layer& -{ - // the pattern must not be empty - if(pattern.empty()) - detail::throw_invalid_argument(); - // delete the last route if it is empty, - // this happens if they call route() without - // adding anything - if(! impl_->layers.empty() && - impl_->layers.back().entries.empty()) - impl_->layers.pop_back(); - impl_->layers.emplace_back(pattern); - return impl_->layers.back(); -}; - -void -any_router:: -add_impl( - core::string_view pattern, - handler_list const& handlers) -{ - if( pattern.empty()) - pattern = "/"; - impl_->layers.emplace_back( - pattern, std::move(handlers)); -} - -void -any_router:: -add_impl( - layer& e, - http_proto::method verb, - handler_list const& handlers) -{ - // cannot be unknown - if(verb == http_proto::method::unknown) - detail::throw_invalid_argument(); - - e.entries.reserve(e.entries.size() + handlers.n); - for(std::size_t i = 0; i < handlers.n; ++i) - e.entries.emplace_back(verb, - std::move(handlers.p[i])); -} - -void -any_router:: -add_impl( - layer& e, - core::string_view verb_str, - handler_list const& handlers) -{ - e.entries.reserve(e.entries.size() + handlers.n); - - if(verb_str.empty()) - { - // all - for(std::size_t i = 0; i < handlers.n; ++i) - e.entries.emplace_back( - std::move(handlers.p[i])); - return; - } - - // possibly custom string - for(std::size_t i = 0; i < handlers.n; ++i) - e.entries.emplace_back(verb_str, - std::move(handlers.p[i])); -} - -//------------------------------------------------ - -auto -any_router:: -resume_impl( - basic_request& req, basic_response& res, - route_result const& ec) const -> - route_result -{ - BOOST_ASSERT(res.resume_ > 0); - if( ec == route::send || - ec == route::close || - ec == route::complete) - return ec; - if(&ec.category() != &detail::route_cat) - { - // must indicate failure - if(! ec.failed()) - detail::throw_invalid_argument(); - } - - // restore base_path and path - req.base_path = { req.decoded_path_.data(), 0 }; - req.path = req.decoded_path_; - if(req.addedSlash_) - req.path.remove_suffix(1); - - // resume_ was set in the handler's wrapper - BOOST_ASSERT(res.resume_ == res.pos_); - res.pos_ = 0; - res.ec_ = ec; - return do_dispatch(req, res); -} - -// top-level dispatch that gets called first -route_result -any_router:: -dispatch_impl( - http_proto::method verb, - core::string_view verb_str, - urls::url_view const& url, - basic_request& req, - basic_response& res) const -{ - // VFALCO we could reuse the string storage by not clearing them - // set req.case_sensitive, req.strict to default of false - req = {}; - if(verb == http_proto::method::unknown) - { - BOOST_ASSERT(! verb_str.empty()); - verb = http_proto::string_to_method(verb_str); - if(verb == http_proto::method::unknown) - req.verb_str_ = verb_str; - } - else - { - BOOST_ASSERT(verb_str.empty()); - } - req.verb_ = verb; - // VFALCO use reusing-StringToken - req.decoded_path_ = - pct_decode_path(url.encoded_path()); - BOOST_ASSERT(! req.decoded_path_.empty()); - req.base_path = { req.decoded_path_.data(), 0 }; - req.path = req.decoded_path_; - if(req.decoded_path_.back() != '/') - { - req.decoded_path_.push_back('/'); - req.addedSlash_ = true; - } - BOOST_ASSERT(req.case_sensitive == false); - BOOST_ASSERT(req.strict == false); - - res = {}; - - // we cannot do anything after do_dispatch returns, - // other than return the route_result, or else we - // could race with the detached operation trying to resume. - return do_dispatch(req, res); -} - -// recursive dispatch -route_result -any_router:: -dispatch_impl( - basic_request& req, - basic_response& res) const -{ - // options are recursive and need to be restored on - // exception or when returning to a calling router. - struct option_saver - { - option_saver( - basic_request& req) noexcept - : req_(&req) - , case_sensitive_(req.case_sensitive) - , strict_(req.strict) - { - } - - ~option_saver() - { - if(! req_) - return; - req_->case_sensitive = case_sensitive_; - req_->strict = strict_; - }; - - void cancel() noexcept - { - req_ = nullptr; - } - - private: - basic_request* req_; - bool case_sensitive_; - bool strict_; - }; - - option_saver restore_options(req); - - // inherit or apply options - if((impl_->opt & 2) != 0) - req.case_sensitive = true; - else if((impl_->opt & 4) != 0) - req.case_sensitive = false; - - if((impl_->opt & 8) != 0) - req.strict = true; - else if((impl_->opt & 16) != 0) - req.strict = false; - - match_result mr; - for(auto const& i : impl_->layers) - { - if(res.resume_ > 0) - { - auto const n = i.count(); // handlers in layer - if(res.pos_ + n < res.resume_) - { - res.pos_ += n; // skip layer - continue; - } - // repeat match to recreate the stack - bool is_match = i.match(req, mr); - BOOST_ASSERT(is_match); - (void)is_match; - } - else - { - if(i.match.end && res.ec_.failed()) - { - // routes can't have error handlers - res.pos_ += i.count(); // skip layer - continue; - } - if(! i.match(req, mr)) - { - // not a match - res.pos_ += i.count(); // skip layer - continue; - } - } - for(auto it = i.entries.begin(); - it != i.entries.end(); ++it) - { - auto const& e(*it); - if(res.resume_) - { - auto const n = e.handler->count(); - if(res.pos_ + n < res.resume_) - { - res.pos_ += n; // skip entry - continue; - } - BOOST_ASSERT(e.match_method(req)); - } - else if(i.match.end) - { - // check verb for match - if(! e.match_method(req)) - { - res.pos_ += e.handler->count(); // skip entry - continue; - } - } - - route_result rv; - // increment before invoke - ++res.pos_; - if(res.pos_ != res.resume_) - { - // call the handler - rv = e.handler->invoke(req, res); - // res.pos_ can be incremented further - // inside the above call to invoke. - if(rv == route::detach) - { - // It is essential that we return immediately, without - // doing anything after route::detach is returned, - // otherwise we could race with the detached operation - // attempting to call resume(). - restore_options.cancel(); - return rv; - } - } - else - { - // a subrouter never detaches on its own - BOOST_ASSERT(e.handler->count() == 1); - // can't detach on resume - if(res.ec_ == route::detach) - detail::throw_invalid_argument(); - // do resume - res.resume_ = 0; - rv = res.ec_; - res.ec_ = {}; - } - if( rv == route::send || - rv == route::complete || - rv == route::close) - return rv; - if(rv.failed()) - { - // error handling mode - res.ec_ = rv; - if(! i.match.end) - continue; // next entry - // routes don't have error handlers - while(++it != i.entries.end()) - res.pos_ += it->handler->count(); - break; // skip remaining entries - } - if(rv == route::next) - continue; // next entry - if(rv == route::next_route) - { - // middleware can't return next_route - if(! i.match.end) - detail::throw_invalid_argument(); - while(++it != i.entries.end()) - res.pos_ += it->handler->count(); - break; // skip remaining entries - } - // we must handle all route enums - BOOST_ASSERT(&rv.category() != &detail::route_cat); - // handler must return non-successful error_code - detail::throw_invalid_argument(); - } - - mr.restore_path(req); - } - - return route::next; -} - -route_result -any_router:: -do_dispatch( - basic_request& req, - basic_response& res) const -{ - auto rv = dispatch_impl(req, res); - BOOST_ASSERT(&rv.category() == &detail::route_cat); - BOOST_ASSERT(rv != route::next_route); - if(rv != route::next) - { - // when rv == route::detach we must return immediately, - // without attempting to perform any additional operations. - return rv; - } - if(! res.ec_.failed()) - { - // unhandled route - return route::next; - } - // error condition - return res.ec_; -} - -} // detail -} // beast2 -} // boost diff --git a/src/server/body_source.cpp b/src/server/body_source.cpp new file mode 100644 index 00000000..cfd9449c --- /dev/null +++ b/src/server/body_source.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/beast2 +// + +#include + +namespace boost { +namespace beast2 { + +body_source:: +~body_source() +{ + if(! impl_) + return; + impl_->~impl(); + ::operator delete(impl_); +} + +body_source& +body_source:: +operator=(body_source&& other) noexcept +{ + if(&other == this) + return *this; + if(impl_) + { + impl_->~impl(); + ::operator delete(impl_); + } + impl_ = other.impl_; + other.impl_ = nullptr; + return *this; +} + +} // beast2 +} // boost diff --git a/src/server/http_server.cpp b/src/server/http_server.cpp deleted file mode 100644 index d8bab14c..00000000 --- a/src/server/http_server.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -namespace { - -template -class http_server_impl - : public http_server -{ -public: - http_server_impl( - capy::application& app, - std::size_t num_workers) - : ioc_(install_single_threaded_asio_io_context(app)) - , w_(app, - ioc_.get_executor(), 1, num_workers, - ioc_.get_executor(), this->wwwroot) - { - } - - void add_port( - char const* addr, - unsigned short port) - { - w_.emplace( - acceptor_config{ false, false }, - asio::ip::tcp::endpoint( - asio::ip::make_address(addr), - port), - true); - } - - void start() - { - w_.start(); - } - - void stop() - { - w_.stop(); - } - - void attach() override - { - ioc_.attach(); - } - -private: - using workers_type = workers< plain_worker< - asio::io_context::executor_type, asio::ip::tcp> >; - - asio_io_context& ioc_; - workers_type w_; -}; - -} // (anon) - -//------------------------------------------------ - -auto -install_plain_http_server( - capy::application& app, - char const* addr, - unsigned short port, - std::size_t num_workers) -> - http_server>& -{ - using stream_type = asio::basic_stream_socket< - asio::ip::tcp, asio::io_context::executor_type>; - auto& srv = app.emplace>( - app, num_workers); - srv.add_port(addr, port); - return srv; -} - -} // beast2 -} // boost - diff --git a/src/server/route_handler.cpp b/src/server/route_handler.cpp deleted file mode 100644 index 2d548bc9..00000000 --- a/src/server/route_handler.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -route_result -Response:: -fail(system::error_code const& ec) -{ - if(! ec.failed()) - detail::throw_invalid_argument(); - return ec; -} - -Response& -Response:: -status( - http_proto::status code) -{ - message.set_start_line(code, message.version()); - return *this; -} - -Response& -Response:: -set_body(std::string s) -{ - if(! message.exists(http_proto::field::content_type)) - { - // VFALCO TODO detect Content-Type - message.set(http_proto::field::content_type, - "text/plain; charset=UTF-8"); - } - - if(!message.exists(http_proto::field::content_length)) - { - message.set_payload_size(s.size()); - } - - serializer.start(message, - http_proto::string_body(std::string(s))); - - return *this; -} - -} // beast2 -} // boost diff --git a/src/server/route_rule.hpp b/src/server/route_rule.hpp index 98cc1adb..3c5e2f03 100644 --- a/src/server/route_rule.hpp +++ b/src/server/route_rule.hpp @@ -262,6 +262,7 @@ struct route_seg core::string_view constraint; char ptype = 0; // ':' | '?' | NULL char modifier = 0; + char term; // param terminator or NULL }; struct param_segment_rule_t @@ -343,6 +344,15 @@ struct path_rule_t return rv1.error(); route_seg rs = rv1.value(); rs.prefix = { it2, it1 }; + if(it != end) + { + if( *it == ':' || + *it == '*') + { + // can't have ":id:id" + return grammar::error::syntax; + } + } rv.segs.push_back(rs); it1 = it; continue; diff --git a/src/server/router_types.cpp b/src/server/router_types.cpp deleted file mode 100644 index f66981bb..00000000 --- a/src/server/router_types.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include -#include -#include -#include - -namespace boost { -namespace beast2 { - -namespace detail { - -const char* -route_cat_type:: -name() const noexcept -{ - return "boost.http_proto.route"; -} - -std::string -route_cat_type:: -message(int code) const -{ - return message(code, nullptr, 0); -} - -char const* -route_cat_type:: -message( - int code, - char*, - std::size_t) const noexcept -{ - switch(static_cast(code)) - { - case route::close: return "http_proto::route::close"; - case route::complete: return "http_proto::route::complete"; - case route::detach: return "http_proto::route::detach"; - case route::next: return "http_proto::route::next"; - case route::next_route: return "http_proto::route::next_route"; - case route::send: return "http_proto::route::send"; - default: - return "http_proto::route::?"; - } -} - -// msvc 14.0 has a bug that warns about inability -// to use constexpr construction here, even though -// there's no constexpr construction -#if defined(_MSC_VER) && _MSC_VER <= 1900 -# pragma warning( push ) -# pragma warning( disable : 4592 ) -#endif - -#if defined(__cpp_constinit) && __cpp_constinit >= 201907L -constinit route_cat_type route_cat; -#else -route_cat_type route_cat; -#endif - -#if defined(_MSC_VER) && _MSC_VER <= 1900 -# pragma warning( pop ) -#endif - -} // detail - -} // beast2 -} // boost diff --git a/src/server/serve_redirect.cpp b/src/server/serve_redirect.cpp deleted file mode 100644 index 247439aa..00000000 --- a/src/server/serve_redirect.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace boost { -namespace beast2 { - -//------------------------------------------------ - -/// Returns the current system time formatted as an HTTP-date per RFC 9110 §5.6.7. -/// Example: "Sat, 11 Oct 2025 02:12:34 GMT" -static -std::string -make_http_date() -{ - using namespace std; - - // Get current time in UTC - std::time_t t = std::time(nullptr); - std::tm tm_utc{}; -#if defined(_WIN32) - gmtime_s(&tm_utc, &t); -#else - gmtime_r(&t, &tm_utc); -#endif - - char const* wkday[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - char const* month[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - // Format strictly according to RFC 9110 (fixed-width, English locale) - char buf[40]; - std::snprintf( - buf, sizeof(buf), - "%s, %02d %s %04d %02d:%02d:%02d GMT", - wkday[tm_utc.tm_wday], - tm_utc.tm_mday, - month[tm_utc.tm_mon], - tm_utc.tm_year + 1900, - tm_utc.tm_hour, - tm_utc.tm_min, - tm_utc.tm_sec); - - return std::string(buf); -} - -static -void -prepare_error( - http_proto::response& res, - std::string& body, - http_proto::status code, - http_proto::request_base const& req) -{ - res.set_start_line(code, req.version()); - res.append(http_proto::field::server, "boost"); - res.append(http_proto::field::date, make_http_date()); - res.append(http_proto::field::cache_control, "no-store"); - res.append(http_proto::field::content_type, "text/html"); - res.append(http_proto::field::content_language, "en"); - - // format the numeric code followed by the reason string - auto title = std::to_string( - static_cast::type>(code)); - title.push_back(' '); - title.append( res.reason() ); - - std::ostringstream ss; - ss << - "" - "" - "" << title << "" - "\n" - "" - "

" << title << "

" - "" - "" - ; - body = ss.str(); -} - -auto -serve_redirect:: -operator()( - Request& req, - Response& res) const -> - system::error_code -{ - std::string body; - prepare_error(res.message, body, - http_proto::status::moved_permanently, req.message); - urls::url u1(req.message.target()); - u1.set_scheme_id(urls::scheme::https); - u1.set_host_address("localhost"); // VFALCO WTF IS THIS! - res.message.append(http_proto::field::location, u1.buffer()); - res.serializer.start(res.message, - http_proto::string_body( std::move(body))); - return {}; -} - -} // beast2 -} // boost - diff --git a/src/server/serve_static.cpp b/src/server/serve_static.cpp index 959212d1..96616a2c 100644 --- a/src/server/serve_static.cpp +++ b/src/server/serve_static.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -170,28 +170,29 @@ serve_static( auto serve_static:: operator()( - Request& req, - Response& res) const -> - route_result + http::route_params&) const -> + http::route_result { + return {}; +#if 0 // Allow: GET, HEAD - if( req.message.method() != http_proto::method::get && - req.message.method() != http_proto::method::head) + if( p.req.method() != http::method::get && + p.req.method() != http::method::head) { if(impl_->opt.fallthrough) - return route::next; + return http::route::next; - res.message.set_status( - http_proto::status::method_not_allowed); - res.message.set(http_proto::field::allow, "GET, HEAD"); - res.set_body(""); - return route::send; + p.res.set_status( + http::status::method_not_allowed); + p.res.set(http::field::allow, "GET, HEAD"); + p.set_body(""); + return http::route::send; } // Build the path to the requested file std::string path; - path_cat(path, impl_->path, req.path); - if(req.parser.get().target().back() == '/') + path_cat(path, impl_->path, p.path); + if(p.parser.get().target().back() == '/') { path.push_back('/'); path.append("index.html"); @@ -199,34 +200,35 @@ operator()( // Attempt to open the file system::error_code ec; - http_proto::file f; + capy::file f; std::uint64_t size = 0; - f.open(path.c_str(), http_proto::file_mode::scan, ec); + f.open(path.c_str(), capy::file_mode::scan, ec); if(! ec.failed()) size = f.size(ec); if(! ec.failed()) { - res.message.set_start_line( - http_proto::status::ok, - req.message.version()); - res.message.set_payload_size(size); + p.res.set_start_line( + http::status::ok, + p.req.version()); + p.res.set_payload_size(size); auto mt = mime_type(get_extension(path)); - res.message.append( - http_proto::field::content_type, mt); + p.res.append( + http::field::content_type, mt); // send file - res.serializer.start( - res.message, std::move(f), size); - return route::send; + p.serializer.start( + p.res, std::move(f), size); + return http::route::send; } if( ec == system::errc::no_such_file_or_directory && ! impl_->opt.fallthrough) - return route::next; + return http::route::next; BOOST_ASSERT(ec.failed()); return ec; +#endif } } // beast2 diff --git a/src/server/workers.cpp b/src/server/workers.cpp deleted file mode 100644 index 2a740b87..00000000 --- a/src/server/workers.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include - -namespace boost { -namespace beast2 { - -workers_base:: -~workers_base() = default; - -} // beast2 -} // boost diff --git a/test/cmake_test/CMakeLists.txt b/test/cmake_test/CMakeLists.txt index a30f99ca..5d0b2166 100644 --- a/test/cmake_test/CMakeLists.txt +++ b/test/cmake_test/CMakeLists.txt @@ -16,73 +16,8 @@ if(BOOST_CI_INSTALL_TEST) find_package(Boost CONFIG REQUIRED COMPONENTS beast2) else() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - add_subdirectory(../.. boostorg/beast2) - - set(BOOST_URL_BUILD_TESTS OFF CACHE BOOL "" FORCE) - - set(deps - # Primary dependencies - - asio - assert - config - http_proto - system - throw_exception - - # Secondary dependencies - - align - context - date_time - buffers - core - capy - static_assert - type_traits - url - winapi - variant2 - mp11 - pool - predef - smart_ptr - algorithm - io - lexical_cast - numeric/conversion - range - tokenizer - utility - container_hash - optional - array - bind - concept_check - exception - function - iterator - mpl - regex - tuple - unordered - describe - container - conversion - preprocessor - integer - detail - intrusive - move - fusion - function_types - functional - typeof - ) - - foreach(dep IN LISTS deps) - add_subdirectory(../../../${dep} boostorg/${dep} EXCLUDE_FROM_ALL) - endforeach() + set(BOOST_INCLUDE_LIBRARIES beast2) + add_subdirectory(../../../.. boostorg/boost) endif() add_executable(main main.cpp) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 47781c44..1049228a 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -7,7 +7,9 @@ # Official repository: https://github.com/cppalliance/beast2 # -add_subdirectory(../../../url/extra/test_suite test_suite) +if(NOT TARGET boost_url_test_suite) + add_subdirectory(../../../url/extra/test_suite test_suite) +endif() file(GLOB_RECURSE PFILES CONFIGURE_DEPENDS *.cpp *.hpp) list(APPEND PFILES @@ -18,10 +20,8 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${PFILES}) add_executable(boost_beast2_tests ${PFILES}) target_include_directories(boost_beast2_tests PRIVATE . ../../) -find_package(OpenSSL REQUIRED) target_link_libraries(boost_beast2_tests PRIVATE boost_url_test_suite_with_main - OpenSSL::SSL Boost::beast2) # Register individual tests with CTest diff --git a/test/unit/Jamfile b/test/unit/Jamfile index ad442eca..0ba0832e 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -8,14 +8,6 @@ # import testing ; -import ac ; - -using openssl ; - -lib advapi32 ; -lib crypt32 ; -lib gdi32 ; -lib user32 ; project : requirements @@ -33,20 +25,7 @@ project clang-win:"-Wno-unused-private-field" ; -for local f in [ glob-tree-ex . : *.cpp : ssl_*.cpp ] +for local f in [ glob-tree-ex . : *.cpp ] { run $(f) ; } - -for local f in [ glob-tree-ex . : ssl_*.cpp ] -{ - run $(f) - : requirements - [ ac.check-library /openssl//ssl : /openssl//ssl/shared : no ] - [ ac.check-library /openssl//crypto : /openssl//crypto/shared : no ] - windows:advapi32 - windows:crypt32 - windows:gdi32 - windows:user32 - ; -} diff --git a/test/unit/asio_io_context.cpp b/test/unit/asio_io_context.cpp deleted file mode 100644 index c46a1883..00000000 --- a/test/unit/asio_io_context.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - diff --git a/test/unit/body_read_stream.cpp b/test/unit/body_read_stream.cpp deleted file mode 100644 index 773b84b7..00000000 --- a/test/unit/body_read_stream.cpp +++ /dev/null @@ -1,425 +0,0 @@ -// -// Copyright (c) 2025 Mungo Gill -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include - -#include -#include -#include -#include - -#include "test_helpers.hpp" -#include - -#include -#include - -namespace boost { -namespace beast2 { - -template -std::string -test_to_string(Buffers const& bs) -{ - std::string s(buffers::size(bs), 0); - s.resize(buffers::copy(buffers::make_buffer(&s[0], s.size()), bs)); - return s; -} - -class test_handler -{ - boost::optional ec_; - boost::optional n_; - bool pass_ = false; - boost::source_location loc_{ BOOST_CURRENT_LOCATION }; - -public: - test_handler(boost::source_location loc = BOOST_CURRENT_LOCATION) - : loc_(loc) - { - } - - explicit test_handler( - system::error_code ec, - std::size_t n, - boost::source_location loc = BOOST_CURRENT_LOCATION) - : ec_(ec) - , n_(n) - , loc_(loc) - { - } - - test_handler(test_handler&& other) - : ec_(other.ec_) - , n_(other.n_) - , pass_(boost::exchange(other.pass_, true)) - , loc_(other.loc_) - { - } - - ~test_handler() - { - test_suite::any_runner::instance().test( - pass_, "handler never invoked", "", loc_.file_name(), loc_.line()); - } - - template - void - operator()(system::error_code ec, std::size_t n, Args&&...) - { - test_suite::any_runner::instance().test( - !pass_, - "handler invoked multiple times", - "", - loc_.file_name(), - loc_.line()); - - test_suite::any_runner::instance().test( - !ec_.has_value() || ec == *ec_, - ec.message().c_str(), - "", - loc_.file_name(), - loc_.line()); - - char buf[64]; - snprintf(buf, 64, "%u", (unsigned int) n); - - test_suite::any_runner::instance().test( - !n_.has_value() || n == *n_, - buf, - "", - loc_.file_name(), - loc_.line()); - - pass_ = true; - } -}; - -// Parser service install done in a base class to avoid order-of-initialisation -// issues (this needs to happen before the parser pr_ is constructed) -struct ctx_base -{ - capy::polystore capy_ctx_; - - ctx_base() - { - http_proto::install_parser_service(capy_ctx_, {}); - } -}; - -struct single_tester : public ctx_base -{ - std::string body_ = "Hello World!"; - - std::string header_ = - "HTTP/1.1 200 OK\r\n" - "Content-Length: 12\r\n" - "\r\n"; - - std::string msg_ = header_ + body_; - - std::size_t header_length_ = header_.size(); - std::size_t body_length_ = body_.size(); - std::size_t msg_length_ = msg_.size(); - - boost::asio::io_context ioc_; - - test::stream ts_; - http_proto::response_parser pr_; - - // Create a destination buffer - std::string s_; - boost::buffers::string_buffer buf_; - - // The object under test - body_read_stream brs_; - - single_tester() - : ts_(ioc_, msg_) - , pr_(capy_ctx_) - , buf_(&s_) - , brs_(ts_, pr_) - { - pr_.reset(); - pr_.start(); - } - - void - async_read_some(std::size_t bs, system::error_code ec, std::size_t n) - { - brs_.async_read_some( - buf_.prepare(bs), - test_handler(ec, n)); - } - - - std::size_t - chunking_expected_n( - std::size_t bs, - std::size_t cs, - bool first, - std::size_t read_so_far) - { - std::size_t expected = 0; - if(read_so_far < body_length_) - { - expected = cs; - // In the first iteration we remove any of the data that was - // associcated with the headers. - if(first) - { - expected -= (header_length_ % cs); - // The `beast2::async_read_some` will always read move from - // the wire immediately after the headers, even if we have a - // partial body in memory already. This should be removable - // once `async_read_some` changes. - if(expected < cs) - { - expected += cs; - } - } - expected = std::min(expected, body_length_ - read_so_far); - expected = std::min(bs, expected); - } - return expected; - } - - struct chunking_handler - { - std::size_t n_; - system::error_code ec_; - std::size_t* total_; - boost::buffers::string_buffer* buf_; - - chunking_handler( - std::size_t n, - system::error_code ec, - std::size_t* total, - boost::buffers::string_buffer* buf) - : n_(n) - , ec_(ec) - , total_(total) - , buf_(buf) - { - } - - void - operator()(system::error_code ec, std::size_t n) - { - BOOST_TEST_EQ(ec, ec_); - BOOST_TEST_EQ(n, n_); - buf_->commit(n); - *total_ += n; - } - }; - - - // Ensure the edge case of being passed a zero-sized buffer works. - void - test_zero_sized_buffer() - { - // Ensure a read into a zero sized buffer returns with no error. - async_read_some(0, system::error_code{}, 0); - test::run(ioc_); - } - - // Test for a given buffer size (bs) and stream read size (cs) - void - test_with_chunking(std::size_t bs, std::size_t cs) - { - ts_.read_size(cs); // Limit read size to cs - - std::size_t total = 0; - for(std::size_t i = 0; i < body_length_; i++) - { - // Calculate how many bytes we expect to read on each iteration - std::size_t expected = chunking_expected_n(bs, cs, (i == 0), total); - - // Read into a buffer of size bs - brs_.async_read_some( - buf_.prepare(bs), - chunking_handler( - expected, - (total < body_length_) ? system::error_code{} - : asio::error::eof, - &total, - &buf_)); - - auto count = test::run(ioc_); - if(i > 0) // The initial run reads the header so can call multiple handlers. - BOOST_TEST_EQ(count, 1); - - BOOST_TEST(pr_.got_header()); - } - - BOOST_TEST(pr_.is_complete()); - BOOST_TEST_EQ(buf_.size(), body_length_); - BOOST_TEST_EQ(total, body_length_); - BOOST_TEST(test_to_string(buf_.data()) == body_); - } - - void - test_with_cancellation(std::size_t len) - { - ts_.clear(); - ts_.append(msg_.substr(0, len)); - - // Add a signal to test cancellation - asio::cancellation_signal c_signal; - - brs_.async_read_some( - buf_.prepare(1024), - asio::bind_cancellation_slot( - c_signal.slot(), - test_handler(asio::error::operation_aborted, 0))); - - // send a cancellation - c_signal.emit(asio::cancellation_type::total); - - // Run up until the point of cancellation. - test::run(ioc_); - - BOOST_TEST(pr_.got_header() == (len >= header_length_)); - BOOST_TEST(!pr_.is_complete()); - - // Append the remainder of the message and try again. - std::string remainder = msg_.substr(len); - ts_.append(remainder); - brs_.async_read_some( - buf_.prepare(1024), - test_handler(system::error_code{}, body_length_)); - - // Continue running until the end. - test::run(ioc_); - - BOOST_TEST(pr_.got_header()); - BOOST_TEST(pr_.is_complete()); - } - - void - test_asio_async_read(std::size_t cs, bool use_asio_buffer) - { - // limit chunk size - ts_.read_size(cs); - - if (use_asio_buffer) - { - asio::async_read( - brs_, - asio::buffer(buf_.prepare(1024).data(), 1024), - test_handler(asio::error::eof, body_length_)); - } else { - asio::async_read( - brs_, - buf_.prepare(1024), - test_handler(asio::error::eof, body_length_)); - } - - test::run(ioc_); - - buf_.commit(body_length_); - - BOOST_TEST(pr_.got_header()); - BOOST_TEST(pr_.is_complete()); - - BOOST_TEST_EQ(buf_.size(), body_length_); - BOOST_TEST(test_to_string(buf_.data()) == body_); - } - - void - test_stream_errors() - { - // Replace the test stream by one with a failure count and a single-byte - // read size. - test::fail_count fc(11, asio::error::network_down); - test::stream ts(ioc_, fc, msg_); - ts.read_size(1); - ts_ = std::move(ts); - - async_read_some(1024, asio::error::network_down, 0); - - BOOST_TEST_EQ(test::run(ioc_), 11); - } - - void - test_parser_errors() - { - // Ensure we get an error by making the body limit too small - pr_.set_body_limit(2); - - async_read_some(1024, http_proto::error::body_too_large, 0); - - test::run(ioc_); - } -}; - -struct body_read_stream_test -{ - void - run() - { - std::size_t msg_length = single_tester().msg_length_; - - // Read into a zero sized buffer should return immediately without error - { - single_tester().test_zero_sized_buffer(); - } - - // async_read_some reads the body for various chunk - // sizes. - { - // Iterate through buffer sizes - for(std::size_t bs = 1; bs < msg_length + 2; bs++) - { - // Iterate through chunk sizes - for(std::size_t cs = 1; cs < msg_length + 2; cs++) - { - single_tester().test_with_chunking(bs, cs); - } - } - } - - // Test async_read_some cancellation - { - // Iterate through the point in the message the cancellation happens. - for(std::size_t len = 1; len < msg_length; len++) - { - single_tester().test_with_cancellation(len); - } - } - - // Test asio::async_read works - { - // pick a representative chunk, as we already do the looping over - // all chunks above. - std::size_t cs = 5; - - // Perform the test using the Boost Buffers buffer directly - single_tester().test_asio_async_read(cs, false); - // And again using an asio buffer wrapper - single_tester().test_asio_async_read(cs, true); - } - - // async_read_some reports stream errors - { - single_tester().test_stream_errors(); - } - - // async_read_some reports parser errors - { - single_tester().test_parser_errors(); - - } - } -}; - -TEST_SUITE(body_read_stream_test, "boost.beast2.body_read_stream"); - -} // beast2 -} // boost diff --git a/test/unit/client.cpp b/test/unit/client.cpp deleted file mode 100644 index 405fe46f..00000000 --- a/test/unit/client.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct success_handler -{ - bool pass = false; - - void - operator()(system::error_code ec, ...) - { - pass = BOOST_TEST(! ec.failed()); - } -}; - -/** Connect two TCP sockets together. -*/ -template -bool -connect( - asio::basic_stream_socket& s1, - asio::basic_stream_socket& s2) - -{ - BOOST_ASSERT(s1.get_executor() == s2.get_executor()); - try - { - asio::basic_socket_acceptor< - asio::ip::tcp, Executor> a(s1.get_executor()); - auto ep = asio::ip::tcp::endpoint( - asio::ip::make_address_v4("127.0.0.1"), 0); - a.open(ep.protocol()); - a.set_option( - asio::socket_base::reuse_address(true)); - a.bind(ep); - a.listen(0); - ep = a.local_endpoint(); - a.async_accept(s2, success_handler()); - s1.async_connect(ep, success_handler()); - s1.get_executor().context().restart(); - s1.get_executor().context().run(); - if(! BOOST_TEST_EQ(s1.remote_endpoint(), s2.local_endpoint())) - return false; - if(! BOOST_TEST_EQ(s2.remote_endpoint(), s1.local_endpoint())) - return false; - } - catch(std::exception const&) - { - BOOST_TEST_FAIL(); - return false; - } - - return true; -} - -using socket_type = - asio::basic_stream_socket< - asio::ip::tcp, - asio::io_context::executor_type>; - -class client_test -{ -public: - void - testClient() - { - asio::io_context ioc; - socket_type s0(ioc.get_executor()); - socket_type s1(ioc.get_executor()); - connect(s0, s1); - client s(std::move(s1)); - } - - void - run() - { - testClient(); - } -}; - -TEST_SUITE(client_test, "boost.beast2.client"); - -} // beast2 -} // boost diff --git a/test/unit/format.cpp b/test/unit/format.cpp index aabf66f0..a4c506ac 100644 --- a/test/unit/format.cpp +++ b/test/unit/format.cpp @@ -28,22 +28,49 @@ struct format_test BOOST_TEST_EQ(s, match); } + template + void e( + core::string_view match, + core::string_view fs, + Args const&... args) + { + BOOST_TEST_THROWS(f(match, fs, args...), std::invalid_argument); + } + void run() { + // Bad format strings, string arg. + e("{}", "{}"); + e("{", "{"); + e("}", "}"); + e("}{", "}{"); + e("{", "{", "x"); + e("}", "}", "x"); + e("}{", "}{", "x"); + e("{", "{", "x"); + e("}", "}", "x"); + e("}{", "}{", "x"); + e("1{}2{}3","1{}2{}3"); + e("1a2{}3", "1{}2{}3", "a"); + + // Good format strings, string arg. f("x", "x"); - f("{}", "{}"); - f("{", "{"); - f("}", "}"); - f("}{", "}{"); + f("{", "{{"); + f("}", "}}"); + f("}{", "}}{{"); f("x", "x"); f("x", "{}", "x"); - f("{", "{", "x"); - f("}", "}", "x"); - f("}{", "}{", "x"); - f("1{}2{}3","1{}2{}3"); - f("1a2{}3", "1{}2{}3", "a"); + f("x", "{}", "x"); + f("{", "{{", "x"); + f("}", "}}", "x"); + f("}{", "}}{{", "x"); f("1a2b3", "1{}2{}3", "a", "b"); + f("1a2b3", "1{}2{}3", "a", "b", "c"); f("hello world!", "hello {}!", "world"); + f("hello world! {} ", "hello {}! {{}} ", "world"); + + // Good format string, char arg + f("x", "{}", 'x'); } }; diff --git a/test/unit/server/http_server.cpp b/test/unit/http_server.cpp similarity index 70% rename from test/unit/server/http_server.cpp rename to test/unit/http_server.cpp index 2c299aeb..63a8231b 100644 --- a/test/unit/server/http_server.cpp +++ b/test/unit/http_server.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,7 @@ // // Test that header file is self-contained. -#include +#include #include "test_suite.hpp" @@ -22,7 +22,7 @@ struct http_server_test } }; -TEST_SUITE(http_server_test, "boost.beast2.server.http_server"); +TEST_SUITE(http_server_test, "boost.beast2.http_server"); } // beast2 } // boost diff --git a/test/unit/read.cpp b/test/unit/read.cpp deleted file mode 100644 index 2771b6cb..00000000 --- a/test/unit/read.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// Copyright (c) 2025 Mohammad Nejati -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "test_helpers.hpp" - -namespace boost { -namespace beast2 { - -class read_test -{ - core::string_view const msg = - "HTTP/1.1 200 OK\r\n" - "Content-Length: 3\r\n" - "\r\n" - "abc"; -public: - void - testAsyncReadSome() - { - boost::asio::io_context ioc; - boost::capy::polystore capy_ctx; - http_proto::install_parser_service(capy_ctx, {}); - - // async_read_some completes when the parser reads - // the header section of the message. - { - test::stream ts(ioc, msg); - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - // limit async_read_some for better coverage - ts.read_size(1); - - // header - async_read_some( - ts, - pr, - [&](system::error_code ec, std::size_t n) - { - BOOST_TEST(! ec.failed()); - BOOST_TEST_EQ(n, msg.size() - 3); // minus body - }); - test::run(ioc); - BOOST_TEST(pr.got_header()); - BOOST_TEST(! pr.is_complete()); - - // body - for(auto i = 0; i < 3; i++) - { - async_read_some( - ts, - pr, - [&](system::error_code ec, std::size_t n) - { - BOOST_TEST(! ec.failed()); - BOOST_TEST_EQ(n, 1); // because of ts.read_size(1) - }); - BOOST_TEST_EQ(test::run(ioc), 1); - } - BOOST_TEST(pr.is_complete()); - BOOST_TEST(pr.body() == "abc"); - } - - // async_read_some reports stream errors - { - test::fail_count fc(11, asio::error::network_down); - test::stream ts(ioc, fc, msg); - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - // limit async_read_some for better coverage - ts.read_size(1); - - bool invoked = false; - async_read_some( - ts, - pr, - [&](system::error_code ec, std::size_t n) - { - invoked = true; - BOOST_TEST_EQ(ec, asio::error::network_down); - BOOST_TEST_EQ(n, 10); - }); - BOOST_TEST_EQ(test::run(ioc), 11); - BOOST_TEST(invoked); - } - - // async_read_some reports parser errors - { - test::stream ts(ioc, msg); - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - // read header - async_read_some(ts, pr, test::success_handler()); - test::run(ioc); - - // read body - pr.set_body_limit(2); - async_read_some( - ts, - pr, - test::fail_handler(http_proto::error::body_too_large)); - test::run(ioc); - } - - // async_read_some cancellation - { - test::stream ts(ioc); - asio::cancellation_signal c_signal; - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - // async_read_some cancels after reading 0 bytes - async_read_some( - ts, - pr, - asio::bind_cancellation_slot( - c_signal.slot(), - [](system::error_code ec, std::size_t n) - { - BOOST_TEST_EQ(n, 0); - BOOST_TEST_EQ(ec, asio::error::operation_aborted); - })); - c_signal.emit(asio::cancellation_type::total); - test::run(ioc); - - // append 8 bytes of the msg - ts.append(msg.substr(0, 8)); - - // async_read_some cancels after reading 8 bytes - async_read_some( - ts, - pr, - asio::bind_cancellation_slot( - c_signal.slot(), - [](system::error_code ec, std::size_t n) - { - BOOST_TEST_EQ(n, 8); - BOOST_TEST_EQ(ec, asio::error::operation_aborted); - })); - c_signal.emit(asio::cancellation_type::total); - test::run(ioc); - - // append rest of the msg - ts.append(msg.substr(8, msg.npos)); - - // async_read_some succeeds - async_read_some(ts, pr, test::success_handler()); - test::run(ioc); - BOOST_TEST(pr.got_header()); - } - } - - void - testAsyncReadHeader() - { - // currently, async_read_header and - // async_read_some are identical - } - - void - testAsyncRead() - { - boost::asio::io_context ioc; - capy::polystore capy_ctx; - http_proto::install_parser_service(capy_ctx, {}); - - // async_read completes when the parser reads - // the entire message. - { - test::stream ts(ioc, msg); - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - // limit async_read_some for better coverage - ts.read_size(1); - - async_read( - ts, - pr, - [&](system::error_code ec, std::size_t n) - { - BOOST_TEST(! ec.failed()); - BOOST_TEST_EQ(n, msg.size()); - }); - - test::run(ioc); - - BOOST_TEST_EQ(ts.nread(), msg.size()); // because of ts.read_size(1) - BOOST_TEST(pr.is_complete()); - BOOST_TEST(pr.body() == "abc"); - } - - // async_read completes immediatly when - // parser contains enough data - { - asio::post( - ioc, - [&]() - { - test::stream ts(ioc); - http_proto::response_parser pr(capy_ctx); - pr.reset(); - pr.start(); - - pr.commit( - buffers::copy( - pr.prepare(), - buffers::const_buffer( - msg.data(), - msg.size()))); - - async_read( - ts, - pr, - asio::bind_immediate_executor( - ioc.get_executor(), - test::success_handler())); - - BOOST_TEST_EQ(ts.nread(), 0); - BOOST_TEST(pr.is_complete()); - BOOST_TEST(pr.body() == "abc"); - }); - BOOST_TEST_EQ(test::run(ioc), 1); - } - } - - void - run() - { - testAsyncReadSome(); - testAsyncReadHeader(); - testAsyncRead(); - } -}; - -TEST_SUITE(read_test, "boost.beast2.read"); - -} // beast2 -} // boost diff --git a/test/unit/sandbox.cpp b/test/unit/sandbox.cpp deleted file mode 100644 index 85acf00b..00000000 --- a/test/unit/sandbox.cpp +++ /dev/null @@ -1,229 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#include "test_suite.hpp" - -#if 0 - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace http_proto { - -static std::size_t constexpr buffer_bytes = 8 * 1024 * 1024; -static std::size_t constexpr payload_size = buffer_bytes * 10; - -using tcp = asio::ip::tcp; -namespace ssl = asio::ssl; -using string_view = - boost::core::string_view; - -//static auto const& log = test_suite::log; -static std::stringstream log; - -struct logging_socket : asio::ip::tcp::socket -{ - using asio::ip::tcp::socket::socket; - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(WriteToken, - void (boost::system::error_code, std::size_t)) - async_write_some(const ConstBufferSequence& buffers, - BOOST_ASIO_MOVE_ARG(WriteToken) token - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( - async_initiate( - declval(), token, - buffers, socket_base::message_flags(0)))) - { - return asio::ip::tcp::socket::async_write_some( - buffers, - asio::deferred([](boost::system::error_code ec, std::size_t n) - { - log << "async_write_some: " << n << "\n"; - return asio::deferred.values(ec, n); - }))(std::forward(token)); - } - -}; - -class my_transfer_all_t -{ -public: - typedef std::size_t result_type; - - template - std::size_t operator()(const Error& err, std::size_t) - { - return !!err ? 0 : std::size_t(-1); - } -}; - -static constexpr my_transfer_all_t my_transfer_all{}; - -struct sandbox_test -{ - void - do_write( - asio::io_context& ioc, - asio::yield_context yield) - { - using clock_type = - std::chrono::high_resolution_clock; - - tcp::resolver dns(ioc); - logging_socket sock(ioc); - std::unique_ptr up(new char[buffer_bytes]); - asio::mutable_buffer mb(up.get(), buffer_bytes); - request req; - - asio::async_connect( - sock, - dns.resolve( - "httpbin.cpp.al", "http"), - yield); - - // header - req.set_start_line( - method::post, "/post", version::http_1_1); - req.append(field::host, "httpbin.cpp.al"); - req.append(field::accept, "application/text"); - req.append(field::user_agent, "boost"); - req.set_payload_size(payload_size); - asio::async_write( - sock, - asio::buffer(req.buffer()), - yield); - - // body - std::size_t n = 0; - std::size_t count = 0; - while(n < payload_size) - { - auto amount = payload_size - n; - if( amount > buffer_bytes) - amount = buffer_bytes; - auto const t0 = clock_type::now(); - auto bytes_transferred = - asio::async_write( - sock, - asio::mutable_buffer( - mb.data(), - amount), - my_transfer_all, - yield); - auto const ms = std::chrono::duration_cast< - std::chrono::milliseconds>( - clock_type::now() - t0).count(); - log << - "write " << bytes_transferred << - " bytes in " << ms << - "ms\n"; - - n += bytes_transferred; - ++count; - } - log << count << " writes total\n\n"; - } - - void - do_write_some( - asio::io_context& ioc, - asio::yield_context yield) - { - using clock_type = - std::chrono::high_resolution_clock; - - tcp::resolver dns(ioc); - tcp::socket sock(ioc); - std::unique_ptr up(new char[buffer_bytes]); - asio::mutable_buffer mb(up.get(), buffer_bytes); - request req; - - asio::async_connect( - sock, - dns.resolve( - "httpbin.cpp.al", "http"), - yield); - - // header - req.set_start_line( - method::post, "/post", version::http_1_1); - req.append(field::host, "httpbin.cpp.al"); - req.append(field::accept, "application/text"); - req.append(field::user_agent, "boost"); - req.set_payload_size(payload_size); - asio::async_write( - sock, - asio::buffer(req.buffer()), - yield); - - // body - std::size_t n = 0; - std::size_t count = 0; - while(n < payload_size) - { - auto amount = payload_size - n; - if( amount > buffer_bytes) - amount = buffer_bytes; - auto const t0 = clock_type::now(); - auto bytes_transferred = - sock.async_write_some( - asio::mutable_buffer( - mb.data(), - amount), - yield); - auto const ms = std::chrono::duration_cast< - std::chrono::milliseconds>( - clock_type::now() - t0).count(); - log << - "write_some " << bytes_transferred << - " bytes in " << ms << - "ms\n"; - - n += bytes_transferred; - ++count; - } - log << count << " write_somes total\n\n"; - } - - void - run() - { - asio::io_context ioc; - asio::spawn(ioc, [&]( - asio::yield_context yield) - { - do_write( ioc, yield ); - do_write_some( ioc, yield ); - }); - ioc.run(); - test_suite::log << log.str(); - } -}; - -TEST_SUITE( - sandbox_test, - "boost.beast2.sandbox"); - -} // http_proto -} // boost - -#endif diff --git a/test/unit/server/basic_router.cpp b/test/unit/server/basic_router.cpp deleted file mode 100644 index 6c0cc1e4..00000000 --- a/test/unit/server/basic_router.cpp +++ /dev/null @@ -1,1499 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include - -#include "src/server/route_rule.hpp" - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct basic_router_test -{ - void compileTimeTests() - { - struct Req : basic_request {}; - struct Res : basic_response {}; - struct OtherReq : basic_request {}; - - BOOST_CORE_STATIC_ASSERT(std::is_copy_assignable>::value); - - struct h0 { void operator()(); }; - struct h1 { system::error_code operator()(); }; - struct h2 { system::error_code operator()(int); }; - struct h3 { system::error_code operator()(Req&, Res&) const; }; - struct h4 { system::error_code operator()(Req&, Res&, system::error_code) const; }; - struct h5 { void operator()(Req&, Res&) {} }; - struct h6 { void operator()(Req&, Res&, system::error_code) {} }; - struct h7 { system::error_code operator()(Req&, Res&, int); }; - struct h8 { system::error_code operator()(Req, Res&, int); }; - struct h9 { system::error_code operator()(Req, Res&, system::error_code const&) const; }; - -#if 0 - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value == 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 1); - - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value == 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value != 2); - BOOST_CORE_STATIC_ASSERT(detail::handler_type::value == 2); - - BOOST_CORE_STATIC_ASSERT(detail::handler_type< - basic_router, Req, Res>::value == 4); - BOOST_CORE_STATIC_ASSERT(detail::handler_type< - basic_router, Req, Res>::value == 4); - BOOST_CORE_STATIC_ASSERT(detail::handler_type< - basic_router, Req, Res>::value == 4); - BOOST_CORE_STATIC_ASSERT(detail::handler_type< - basic_router, Req, Res>::value == 0); -#endif - } - - //-------------------------------------------- - - using Req = basic_request; - using Res = basic_response; - - /** A handler for testing - */ - struct handler - { - ~handler() - { - if(alive_) - BOOST_TEST_EQ(called_, want_ != 0); - } - - explicit handler( - int want, system::error_code ec = - http_proto::error::success) - : want_(want) - , ec_(ec) - { - } - - handler(handler&& other) - { - BOOST_ASSERT(other.alive_); - BOOST_ASSERT(! other.called_); - want_ = other.want_; - alive_ = true; - other.alive_ = false; - ec_ = other.ec_; - } - - route_result operator()(Req&, Res&) const - { - called_ = true; - switch(want_) - { - default: - case 0: return route::close; - case 1: return route::send; - case 2: return route::next; - case 3: return ec_; - case 4: return route::next_route; - } - } - - private: - // 0 = not called - // 1 = called - // 2 = next - // 3 = error - // 4 = next_route - int want_; - bool alive_ = true; - bool mutable called_ = false; - system::error_code ec_; - }; - - /** An error handler for testing - */ - struct err_handler - { - ~err_handler() - { - if(alive_) - BOOST_TEST_EQ(called_, want_ != 0); - } - - err_handler( - int want, - system::error_code ec) - : want_(want) - , ec_(ec) - { - } - - err_handler(err_handler&& other) - { - BOOST_ASSERT(other.alive_); - BOOST_ASSERT(! other.called_); - want_ = other.want_; - alive_ = true; - other.alive_ = false; - ec_ = other.ec_; - } - - route_result operator()( - Req&, Res&, system::error_code ec) const - { - called_ = true; - switch(want_) - { - default: - case 0: return route::close; - case 1: - BOOST_TEST(ec == ec_); - return route::send; - case 2: - BOOST_TEST(ec == ec_); - return route::next; - case 3: - BOOST_TEST(ec.failed()); - return ec_; - } - } - - private: - // 0 = not called - // 1 = called, expecting ec_ - // 2 = next, expecting ec_ - // 3 = change error - int want_; - bool alive_ = true; - bool mutable called_ = false; - system::error_code ec_; - }; - - // handler to check base_url and path - struct path - { - ~path() - { - if(alive_) - BOOST_TEST(called_); - } - path(path&& other) - { - BOOST_ASSERT(other.alive_); - BOOST_ASSERT(! other.called_); - alive_ = true; - other.alive_ = false; - base_path_ = other.base_path_; - path_ = other.path_; - } - path( - core::string_view path = "/") - : path_(path) - { - } - path( - core::string_view base_path, - core::string_view path) - : base_path_(base_path) - , path_(path) - { - } - route_result operator()(Req& req, Res&) const - { - called_ = true; - BOOST_TEST_EQ(req.base_path, base_path_); - BOOST_TEST_EQ(req.path, path_); - return route::next; - } - private: - bool alive_ = true; - bool mutable called_ = false; - core::string_view base_path_; - core::string_view path_; - }; - - //------------------------------------------- - - // must NOT be called - static handler skip() - { - return handler(0); - } - - // must be called - static handler send() - { - return handler(1); - } - - // must be called, returns route::next - static handler next() - { - return handler(2); - } - - // must be called, returns ec - static handler fail( - system::error_code ec) - { - return handler(3, ec); - } - - // must NOT be called - static err_handler err_skip() - { - return err_handler(0, - http_proto::error::success); - } - - // must be called, expects ec, returns route::send - static err_handler err_send( - system::error_code ec) - { - return err_handler(1, ec); - } - - // must be called with `ec`, returns route::next - static err_handler err_next( - system::error_code ec) - { - return err_handler(2, ec); - } - - // must be called, returns a new error `ec` - static err_handler err_return( - system::error_code ec) - { - return err_handler(3, ec); - } - - using test_router = basic_router; - - void check( - test_router& r, - core::string_view url, - route_result rv0 = route::send) - { - Req req; - Res res; - auto rv = r.dispatch( - http_proto::method::get, - urls::url_view(url), req, res); - if(BOOST_TEST_EQ(rv.message(), rv0.message())) - BOOST_TEST(rv == rv0); - } - - void check( - test_router& r, - http_proto::method verb, - core::string_view url, - route_result rv0 = route::send) - { - Req req; - Res res; - auto rv = r.dispatch(verb, - urls::url_view(url), req, res); - if(BOOST_TEST_EQ(rv.message(), rv0.message())) - BOOST_TEST(rv == rv0); - } - - void check( - test_router& r, - core::string_view verb, - core::string_view url, - route_result rv0 = route::send) - { - Req req; - Res res; - auto rv = r.dispatch(verb, - urls::url_view(url), req, res); - if(BOOST_TEST_EQ(rv.message(), rv0.message())) - BOOST_TEST(rv == rv0); - } - - //-------------------------------------------- - - // special members - void testSpecial() - { - // default construction - { - test_router r; - check(r, "/", route::next); - } - - // copy construction - { - test_router r0; - r0.use(send()); - check(r0, "/"); - test_router r1(r0); - check(r1, "/"); - check(r0, "/"); - } - - // move assignment - { - test_router r0; - r0.use(send()); - check(r0, "/"); - test_router r1; - check(r1, "/", route::next); - r1 = std::move(r0); - check(r1, "/"); - } - - // copy assignment - { - test_router r0; - r0.use(send()); - check(r0, "/"); - test_router r1; - check(r1, "/", route::next); - r1 = r0; - check(r1, "/"); - check(r0, "/"); - } - - // options - { - // make sure this compiles - test_router r(router_options() - .case_sensitive(true) - .merge_params(true) - .strict(false)); - } - } - - void testUse() - { - system::error_code const er = - http_proto::error::bad_connection; - system::error_code const er2 = - http_proto::error::bad_expect; - - // pathless - { - test_router r; - r.use(send()); - check(r,"/"); - } - { - test_router r; - r.use( - path(), - send()); - check(r,"/"); - } - { - test_router r; - r.use( - send(), - skip()); - check(r,"/"); - } - { - test_router r; - r.use(send()); - r.use(skip()); - check(r,"/"); - } - { - test_router r; - r.use( - next(), - send()); - check(r,"/"); - } - { - test_router r; - r.use(next()); - r.use(send()); - check(r,"/"); - } - { - test_router r; - r.use(next()); - r.use(send()); - r.use(skip()); - check(r,"/"); - } - { - test_router r; - r.use( - next(), - send(), - skip()); - check(r,"/"); - } - - // pathless with errors - { - test_router r; - r.use(fail(er)); - check(r, "/", er); - } - { - test_router r; - r.use(next()); - r.use(err_skip()); - r.use(fail(er)); - r.use(skip()); - r.use(err_send(er)); - r.use(skip()); - r.use(err_skip()); - check(r,"/"); - } - { - test_router r; - r.use( - next(), - err_skip(), - fail(er), - skip(), - err_send(er), - skip(), - err_skip()); - check(r,"/"); - } - { - test_router r; - r.use(next()); - r.use(err_skip()); - r.use(fail(er)); - r.use(skip()); - r.use(err_return(er2)); - r.use(skip()); - r.use(err_next(er2)); - r.use(err_send(er2)); - check(r,"/"); - } - { - test_router r; - r.use( - next(), - err_skip(), - fail(er), - skip(), - err_return(er2), - skip(), - err_next(er2), - err_send(er2)); - check(r,"/"); - } - { - // cannot return success - test_router r; - r.use(fail(system::error_code())); - BOOST_TEST_THROWS(check(r,"/"), - std::invalid_argument); - } - { - // can't change failure to success - test_router r; - r.use( - fail(er), - err_return(system::error_code{})); - BOOST_TEST_THROWS(check(r,"/"), - std::invalid_argument); - } - - // pathless, returning route enums - { - test_router r; - r.use(fail(route::close)); - check(r,"/", route::close); - } - { - test_router r; - r.use(fail(route::complete)); - check(r,"/", route::complete); - } - { - test_router r; - r.use(fail(route::detach)); - check(r,"/", route::detach); - } - { - test_router r; - r.use(fail(route::next)); - check(r,"/", route::next); - } - { - // middleware can't return route::next_route - test_router r; - r.use(fail(route::next_route)); - BOOST_TEST_THROWS(check(r,"/", route::next), - std::invalid_argument); - } - { - test_router r; - r.use(fail(route::send)); - check(r,"/", route::send); - } - - // empty path - { - test_router r; - r.use("", send()); - check(r,"/"); - check(r,"/api"); - } - { - test_router r; - r.use("", - path("/api"), - send()); - check(r,"/api"); - } - - // prefix matching - { - test_router r; - r.use("/api", skip()); - check(r,"/", route::next); - } - { - test_router r; - r.use("/api", skip()); - check(r,"/", route::next); - check(r,"/a", route::next); - check(r,"/ap", route::next); - } - { - test_router r; - r.use("/api", send()); - check(r,"/api"); - check(r,"/api/"); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api", - path("/api", "/more"), - send()); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api/more", - path("/api/more", "/"), - send()); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api", next()); - r.use("/api", send()); - check(r,"/api"); - check(r,"/api/"); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api", - next(), - send()); - check(r,"/api"); - check(r,"/api/"); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api", - next(), - send(), - err_skip(), - skip()); - check(r,"/api"); - check(r,"/api/"); - check(r,"/api/more"); - } - { - test_router r; - r.use("/api", skip()); - r.use("/", send()); - check(r,"/"); - } - { - test_router r; - r.use("/", next()); - r.use("/api", skip()); - r.use("/", send()); - check(r,"/"); - } - { - test_router r; - r.use("/x", next()); - r.use("/api", skip()); - r.use("/y", skip()); - r.use("/x", send()); - check(r,"/x"); - } - { - // no match - test_router r; - r.use("/x", skip()); - r.use("/api", skip()); - r.use("/y", skip()); - r.use("/x", skip()); - check(r,"/", route::next); - } - - // errors and matching - { - test_router r; - r.use("/x", skip()); - r.use("/api", skip()); - r.use("/y", fail(er)); - r.use("/y", skip()); - r.use("/x", err_skip()); - r.use("/y", err_return(er2)); - r.use("/z/", err_skip()); - r.use("/y", err_next(er2)); - r.use("/y", err_send(er2)); - r.use("/y", err_skip()); - r.use("/y", skip()); - check(r,"/y"); - } - { - test_router r; - r.use("/x", skip()); - r.use("/api", skip()); - r.use("/y", - fail(er), - skip()); - r.use("/x", err_skip()); - r.use("/y", err_return(er2)); - r.use("/z/", err_skip()); - r.use("/y", - err_next(er2), - err_send(er2), - err_skip(), - skip()); - check(r,"/y"); - } - - // case sensitivity - { - test_router r(router_options() - .case_sensitive(true)); - r.use("/x", skip()); - check(r, "/X", route::next); - } - { - test_router r(router_options() - .case_sensitive(false)); - r.use("/x", send()); - check(r, "/X"); - } - { - test_router r; - r.use("/x", send()); - check(r, "/X"); - } - } - - void testDispatch() - { - // dispatch - { - test_router r; - r.use(skip()); - BOOST_TEST_THROWS( - check(r, http_proto::method::unknown, "/", route::next), - std::invalid_argument); - } - } - - void testRoute() - { - static auto const GET = http_proto::method::get; - static auto const POST = http_proto::method::post; - static system::error_code const er = - http_proto::error::bad_connection; - static system::error_code const er2 = - http_proto::error::bad_expect; - - // empty - { - test_router r; - check(r, "/", route::next); - check(r, GET, "/", route::next); - check(r, POST, "/", route::next); - check(r, "GET", "/", route::next); - check(r, "POST", "/", route::next); - BOOST_TEST_THROWS( - check(r, "", "/", route::next), - std::invalid_argument); - } - - // add - { - test_router r; - r.add(GET, "/", - path(), - send()); - check(r, GET, "/"); - check(r, "GET", "/"); - check(r, "get", "/", route::next); - check(r, POST, "/", route::next); - check(r, "POST", "/", route::next); - check(r, "post", "/", route::next); - } - { - test_router r; - r.add(POST, "/", send()); - check(r, POST, "/"); - check(r, "POST", "/"); - check(r, "Post", "/", route::next); - check(r, GET, "/", route::next); - check(r, "GET", "/", route::next); - check(r, "get", "/", route::next); - } - { - test_router r; - r.add(GET, "/x", skip()); - r.add(POST, "/y", skip()); - r.add(GET, "/y", - path("/y", "/"), - send()); - r.add(GET, "/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.add("HACK", "/", next()); - r.add("CRACK", "/", send()); - r.add(GET, "/", skip()); - check(r, "CRACK", "/"); - check(r, "crack", "/", route::next); - check(r, "HACK", "/", route::next); - } - - // route.add - { - test_router r; - r.route("/") - .add(GET, send()); - check(r, GET, "/"); - check(r, "GET", "/"); - check(r, "get", "/", route::next); - check(r, POST, "/", route::next); - check(r, "POST", "/", route::next); - check(r, "post", "/", route::next); - } - { - test_router r; - r.route("/") - .add(POST, send()); - check(r, POST, "/"); - check(r, "POST", "/"); - check(r, "Post", "/", route::next); - check(r, GET, "/", route::next); - check(r, "GET", "/", route::next); - check(r, "get", "/", route::next); - } - { - test_router r; - r.route("/x").add(GET, skip()); - r.route("/y") - .add(POST, skip()) - .add(GET, send()); - r.route("/z") - .add(GET, skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.route("/") - .add("HACK", next()) - .add("CRACK", send()) - .add(GET, skip()); - check(r, "CRACK", "/"); - check(r, "crack", "/", route::next); - check(r, "HACK", "/", route::next); - } - - // mix with use - { - test_router r; - r.use(next()); - r.add(POST, "/x", skip()); - r.add(POST, "/y", skip()); - r.use("/z", next()); - r.add(POST, "/y", skip()); - r.add(POST, "/z", send()); - r.add(POST, "/z", skip()); - r.use(skip()); - check(r, POST, "/z"); - } - - // verb matching - { - test_router r; - r.add(GET, "/", send()); - check(r, GET, "/"); - check(r, POST, "/", route::next); - check(r, "GET", "/"); - check(r, "POST", "/", route::next); - check(r, "get", "/", route::next); - check(r, "Get", "/", route::next); - check(r, "gEt", "/", route::next); - check(r, "geT", "/", route::next); - check(r, "post", "/", route::next); - } - { - test_router r; - r.route("/") - .add(POST, skip()) - .add(GET, send()) - .add(GET, skip()) - .add(POST, skip()); - check(r, GET, "/"); - } - { - test_router r; - r.route("/") - .add(GET, skip()) - .add(POST, send()) - .add(POST, skip()) - .add(GET, skip()); - check(r, POST, "/"); - } - - // all - { - test_router r; - r.all("/x", skip()); - r.all("/y", send()); - r.all("/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.all("/y", next()); - r.all("/y", send()); - r.all("/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.add(GET, "/y", next()); - r.all("/y", send()); - r.all("/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.add(POST, "/y", skip()); - r.all("/y", send()); - r.all("/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - r.add(GET, "/x", skip()); - r.all("/y", send()); - r.use("/z", skip()); - check(r, GET, "/y"); - } - { - test_router r; - BOOST_TEST_THROWS( - r.all("", skip()), - std::invalid_argument); - } - - // error handling - { - test_router r; - r.use(err_skip()); - r.route("/") - .add(GET, skip()) - .add(POST, skip()) - .add("FAIL", fail(er)) - .add("HEAD", skip()); - check(r, "FAIL", "/", er); - } - { - test_router r; - r.use(err_skip()); - r.route("/") - .add(GET, skip()) - .add(POST, skip()) - .add("FAIL", fail(er)) - .add("HEAD", skip()); - r.use( - err_send(er), - err_skip()); - check(r, "FAIL", "/"); - } - { - test_router r; - r.route("/") - .add(GET, skip()) - .add(POST, fail(er)) - .add(POST, skip()); - r.use( - err_return(er2), - err_next(er2)); - r.use( - err_send(er2)); - check(r, POST, "/"); - } - - // request with known method, custom route - { - test_router r; - r.route("/") - .add("BEAUCOMP", skip()); - check(r, GET, "/", route::next); - } - - // clean up empty routes - { - test_router r; - r.route("/empty"); - r.route("/not-empty") - .add(GET, send()); - check(r, "/empty", route::next); - check(r, "/not-empty"); - } - - // bad verb - { - test_router r; - BOOST_TEST_THROWS( - r.route("/").add(http_proto::method::unknown, skip()), - std::invalid_argument); - } - - // skip route on error - { - test_router r; - r.route("/") - .add(GET, next()) - .add(GET, fail(er)) - .add(GET, skip()) - .all(skip()) - .add(POST, skip()); - r.route("/") - .all(skip()); - r.use(err_send(er)); - check(r, GET, "/"); - } - - // skip route on next_route - { - test_router r; - r.route("/") - .add(GET, next()) - .add(GET, next()) - .add(GET, fail(route::next_route)) - .add(GET, skip()) - .add(GET, skip()) - .add(GET, skip()) - .add(GET, skip()) - .all(skip()); - r.route("/") - .add(GET, send()); - check(r, GET, "/"); - } - } - - void testSubRouter() - { - static auto const GET = http_proto::method::get; - static auto const POST = http_proto::method::post; - static system::error_code const er = - http_proto::error::bad_connection; - static system::error_code const er2 = - http_proto::error::bad_expect; - - // sub-middleware - { - test_router r; - r.use("/api", []{ - test_router r; - r.use("/v1", - skip()); - r.use("/v2", - path("/api/v2", "/"), - send()); - return r; }()); - check(r,"/api/v2"); - } - - // error handling - { - test_router r; - r.use("/api", []{ - test_router r; - r.use("/v1", - path("/api/v1", "/"), - fail(er)); // return er - return r; }()); - check(r,"/api/v1", er); - } - { - test_router r; - r.use("/api", []{ - test_router r; - r.use("/v1", - path("/api/v1", "/"), - fail(er)); - return r; }()); - r.use(err_next(er)); // next - check(r,"/api/v1", er); - } - { - test_router r; - r.use("/api", []{ - test_router r; - r.use("/v1", - path("/api/v1", "/"), - fail(er)); - return r; }()); - r.use( - skip()); - r.use([]{ - test_router r; - r.use( - skip(), - err_skip()); - return r; }()); - r.use("/api/v2", - err_skip()); - r.use( - err_next(er)); - check(r,"/api/v1", er); - } - { - test_router r; - r.use([]{ - test_router r; - r.use([]{ - test_router r; - r.use(fail(er)); - return r; }()); - r.use( - err_next(er), - skip()); - return r; }()); - r.use( - err_return(er2), - err_next(er2), - err_return(er)); - check(r, "/", er); - } - - // sub routes - { - test_router r; - r.use("/api", []{ - test_router r; - r.route("/user") - .add(POST, path("/api/user", "/")) - .add(GET, skip()) - .add(POST, send()); - return r; }()); - check(r, POST, "/api/user"); - } - - // nested options - { - test_router r(router_options() - .case_sensitive(true)); - r.use("/api", []{ - test_router r; - r.route("/USER") - .add(GET, skip()); - r.route("/user") - .add(GET, send()); - return r; }()); - check(r, "/api/user"); - } - { - test_router r(router_options() - .case_sensitive(true)); - r.use("/api", []{ - test_router r(router_options() - .case_sensitive(false)); - r.route("/USER") - .add(GET, send()); - r.route("/user") - .add(GET, skip()); - return r; }()); - check(r, "/api/user"); - } - { - test_router r; - r.use("/api", []{ - test_router r(router_options() - .case_sensitive(true)); - r.route("/USER") - .add(GET, send()); - r.route("/user") - .add(GET, skip()); - return r; }()); - check(r, "/api/USER"); - } - } - - void testErr() - { - static auto const GET = http_proto::method::get; - static system::error_code const er = - http_proto::error::bad_connection; - static system::error_code const er2 = - http_proto::error::bad_content_length; - { - test_router r; - r.use(err_skip()); - check(r,"/", route::next); - } - { - test_router r; - r.use("", err_skip()); - check(r,"/", route::next); - } - { - test_router r; - r.use(send()); - r.use(err_skip()); - check(r,"/"); - } - { - test_router r; - r.use(fail(er)); - r.use(err_send(er)); - r.use(err_skip()); - check(r,"/", route::send); - } - { - test_router r; - r.use(fail(er)); - r.use("", err_send(er)); - r.use(err_skip()); - check(r,"/", route::send); - } - { - test_router r; - r.use(fail(er2)); - r.use(err_send(er2)); - r.use(err_skip()); - check(r,"/", route::send); - } - - // mount points - { - test_router r; - r.use("/api", fail(er)); - r.use("/api", err_send(er)); - r.use("/x", err_skip()); - check(r, "/api"); - } - { - test_router r; - r.use("/x", fail(er)); - r.use("/api", err_skip()); - r.use("/x", err_send(er)); - check(r, "/x/data"); - } - - // replacing errors - { - test_router r; - r.use(fail(er)); - r.use(err_return(er2)); - r.use(err_send(er2)); - check(r, "/"); - } - - { - test_router r; - r.use(fail(er)); - r.use(skip()); - r.use(err_send(er)); - check(r, "/"); - } - - // route-level vs. router-level - { - test_router r; - r.route("/").add(GET, fail(er)); - r.use(err_send(er)); - check(r, "/"); - } - - // subrouters - { - test_router r; - r.use("/api", []{ - test_router r; - r.use( - fail(er), - err_send(er), - err_skip()); - return r; }()); - r.use(err_skip()); - check(r, "/api"); - } - { - test_router r; - r.use("/api", []{ - test_router r; - r.use( - fail(er), - err_next(er), - skip()); - return r; }()); - r.use(err_send(er)); - r.use(err_skip()); - check(r, "/api"); - } - } - - void testPath() - { - auto const path = []( - core::string_view pat, - core::string_view target, - core::string_view good) - { - test_router r; - r.use( pat, - [&](Req& req, Res&) - { - BOOST_TEST_EQ(req.path, good); - return route::send; - }); - Req req; - Res res; - r.dispatch( - http_proto::method::get, - urls::url_view(target), - req, res); - }; - - path("/", "/", "/"); - path("/", "/api", "/api"); - path("/api", "/api", "/"); - path("/api", "/api/", "/"); - path("/api", "/api/", "/"); - path("/api", "/api/v0", "/v0"); - path("/api/", "/api", "/"); - path("/api/", "/api", "/"); - path("/api/", "/api/", "/"); - path("/api/", "/api/v0", "/v0"); - } - - void testPctDecode() - { - static auto const GET = http_proto::method::get; - - // slash - { - test_router r; - r.add(GET, "/auth/login", skip()); - check(r, "/auth%2flogin", route::next); - } - - // backslash - { - test_router r; - r.add(GET, "/auth\\login", skip()); - check(r, "/auth%5clogin", route::next); - } - - // unreserved - { - test_router r; - r.add(GET, "/a", send()); - check(r, "/%61"); - } - { - test_router r; - r.add(GET, "/%61", send()); - check(r, "/%61"); - } - { - test_router r; - r.add(GET, "/%61", send()); - check(r, "/a"); - } - } - - void testDetach() - { - static auto const GET = http_proto::method::get; - { - test_router r; - r.use(next()); - r.use(fail(route::detach)); - check(r,"/", route::detach); - } - { - test_router r; - r.use(next()); - r.use(fail(route::detach)); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - } - { - test_router r; - r.use(next()); - r.use(fail(route::detach)); - r.use(next()); - r.use(fail(route::send)); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::next); - BOOST_TEST(rv2 == route::send); - } - { - test_router r; - r.use(next()); - r.use([]{ - test_router r; - r.use( - next(), - fail(route::detach), - path(), - next()); - return r; }()); - r.use(send()); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::next); - BOOST_TEST(rv2 == route::send); - } - - // return values - { - test_router r; - r.use(fail(route::detach)); - Req req; - Res res; - { - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::send); - BOOST_TEST(rv2 == route::send); - } - { - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::close); - BOOST_TEST(rv2 == route::close); - } - { - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::complete); - BOOST_TEST(rv2 == route::complete); - } - { - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - BOOST_TEST_THROWS(r.resume(req, res, system::error_code()), - std::invalid_argument); - } - } - - // path restoration - { - test_router r; - r.use(next()); - r.use("/api", []{ - test_router r; - r.use( - next(), - fail(route::detach), - path("/api", "/"), - next()); - return r; }()); - r.use("/api", send()); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/api"), req, res); - BOOST_TEST(rv1 == route::detach); - auto rv2 = r.resume(req, res, route::next); - BOOST_TEST(rv2 == route::send); - } - - // detach on resume - { - test_router r; - r.use(fail(route::detach)); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - BOOST_TEST_THROWS(r.resume(req, res, route::detach), - std::invalid_argument); - } - - // invalid detach - { - test_router r; - r.use(fail(route::detach)); - Req req; - Res res; - auto rv1 = r.dispatch(GET, urls::url_view("/"), req, res); - BOOST_TEST(rv1 == route::detach); - BOOST_TEST_THROWS(r.resume(req, res, system::error_code()), - std::invalid_argument); - } - } - - void run() - { - testSpecial(); - testUse(); - testDispatch(); - testRoute(); - testSubRouter(); - testErr(); - testPath(); - testPctDecode(); - testDetach(); - } -}; - -TEST_SUITE( - basic_router_test, - "boost.http_proto.server.basic_router"); - -} // beast2 -} // boost diff --git a/test/unit/server/body_source.cpp b/test/unit/server/body_source.cpp new file mode 100644 index 00000000..ea0f0f27 --- /dev/null +++ b/test/unit/server/body_source.cpp @@ -0,0 +1,266 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/beast2 +// + +// Test that header file is self-contained. +#include + +#include +#include + +#include "test_suite.hpp" + +namespace boost { +namespace beast2 { + +namespace { + +struct data_source +{ + core::string_view s_; + + data_source( + core::string_view s) + : s_(s) + { + } + + capy::const_buffer + data() const + { + return { s_.data(), s_.size() }; + } +}; + +struct read_source +{ + core::string_view s_; + std::size_t nread_ = 0; + system::error_code ec_; + + explicit read_source( + core::string_view s, + system::error_code ec = {}) + : s_(s) + , ec_(ec) + { + } + + void rewind() + { + nread_ = 0; + } + + template + std::size_t read( + MutableBufferSequence const& dest, + system::error_code& ec) + { + if( nread_ > 0 && + ec_.failed()) + { + // fail on second read + ec = ec_; + return 0; + } + auto n = capy::copy( + dest, + capy::const_buffer( + s_.data() + nread_, + s_.size() - nread_)); + nread_ += n; + if(nread_ >= s_.size()) + { + if(ec_.failed()) + ec = ec_; + else + ec = http::error::end_of_stream; + } + else + { + ec = {}; + } + return n; + } +}; + +} // (anon) + +#if 0 +BOOST_CORE_STATIC_ASSERT( + std::is_move_constructible::value); +BOOST_CORE_STATIC_ASSERT( + ! std::is_copy_constructible::value); +BOOST_CORE_STATIC_ASSERT( + ! std::is_constructible::value); +BOOST_CORE_STATIC_ASSERT( + std::is_constructible::value); +BOOST_CORE_STATIC_ASSERT( + std::is_constructible::value); +BOOST_CORE_STATIC_ASSERT( + std::is_move_assignable::value); +BOOST_CORE_STATIC_ASSERT( + ! std::is_copy_assignable::value); +BOOST_CORE_STATIC_ASSERT( + std::is_assignable::value); +BOOST_CORE_STATIC_ASSERT( + ! std::is_assignable::value); +BOOST_CORE_STATIC_ASSERT( + std::is_assignable::value); +BOOST_CORE_STATIC_ASSERT( + std::is_assignable::value); +#endif + +struct body_source_test +{ + void grind( + body_source& b, + core::string_view s0, + system::error_code fec = {}) + { + char buf[16]; + for(std::size_t n = 1; n <= sizeof(buf); ++n) + { + std::string s; + system::error_code ec; + b.rewind(); + for(;;) + { + auto nread = b.read(buf, n, ec); + s.append(buf, nread); + if(ec == http::error::end_of_stream) + { + BOOST_TEST(! fec.failed()); + BOOST_TEST_EQ(s, s0); + break; + } + if(ec.failed()) + { + BOOST_TEST_EQ(ec, fec); + break; + } + BOOST_TEST_GT(nread, 0); + } + } + } + + void testEmpty() + { + body_source b; + BOOST_TEST_EQ(b.has_size(), true); + BOOST_TEST_EQ(b.size(), 0); + BOOST_TEST_EQ(b.has_buffers(), true); + BOOST_TEST_EQ(capy::buffer_size(b.data()), 0); + BOOST_TEST_NO_THROW(b.rewind()); + grind(b, ""); + } + + void testBuffers() + { + core::string_view s1("Hello, world!"); + core::string_view s2("Boost"); + + body_source b1((data_source(s1))); + BOOST_TEST_EQ(b1.has_size(), true); + BOOST_TEST_EQ(b1.size(), s1.size()); + BOOST_TEST_EQ(b1.has_buffers(), true); + BOOST_TEST_EQ(capy::buffer_size(b1.data()), s1.size()); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b1, s1); + + body_source b2 = std::move(b1); + BOOST_TEST_EQ(b2.has_size(), true); + BOOST_TEST_EQ(b2.size(), s1.size()); + BOOST_TEST_EQ(b2.has_buffers(), true); + BOOST_TEST_EQ(capy::buffer_size(b2.data()), s1.size()); + BOOST_TEST_NO_THROW(b2.rewind()); + grind(b2, s1); + + b1 = data_source(s2); + BOOST_TEST_EQ(b1.has_size(), true); + BOOST_TEST_EQ(b1.size(), s2.size()); + BOOST_TEST_EQ(b1.has_buffers(), true); + BOOST_TEST_EQ(capy::buffer_size(b1.data()), s2.size()); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b1, s2); + } + + void testStream() + { + core::string_view s1("Hello, world!"); + core::string_view s2("Boost"); + + body_source b1((read_source(s1))); + BOOST_TEST_EQ(b1.has_size(), false); + BOOST_TEST_EQ(b1.has_buffers(), false); + BOOST_TEST_THROWS(b1.size(), std::invalid_argument); + BOOST_TEST_THROWS(b1.data(), std::invalid_argument); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b1, s1); + + body_source b2 = std::move(b1); + BOOST_TEST_EQ(b2.has_size(), false); + BOOST_TEST_EQ(b2.has_buffers(), false); + BOOST_TEST_THROWS(b2.size(), std::invalid_argument); + BOOST_TEST_THROWS(b2.data(), std::invalid_argument); + BOOST_TEST_NO_THROW(b2.rewind()); + BOOST_TEST_EQ(b1.has_size(), true); + BOOST_TEST_EQ(b1.size(), 0); + BOOST_TEST_EQ(b1.has_buffers(), true); + BOOST_TEST_EQ(capy::buffer_size(b1.data()), 0); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b2, s1); + + b1 = read_source(s2); + BOOST_TEST_EQ(b1.has_size(), false); + BOOST_TEST_EQ(b1.has_buffers(), false); + BOOST_TEST_THROWS(b1.size(), std::invalid_argument); + BOOST_TEST_THROWS(b1.data(), std::invalid_argument); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b1, s2); + + // sized source + b2 = body_source(s2.size(), read_source(s2)); + BOOST_TEST_EQ(b2.has_size(), true); + BOOST_TEST_EQ(b2.has_buffers(), false); + BOOST_TEST_EQ(b2.size(), s2.size()); + BOOST_TEST_THROWS(b1.data(), std::invalid_argument); + BOOST_TEST_NO_THROW(b2.rewind()); + grind(b2, s2); + } + + void testFail() + { + core::string_view s1("Hello, world!"); + system::error_code fec = + http::error::bad_connection; + + body_source b1((read_source(s1, fec))); + BOOST_TEST_EQ(b1.has_size(), false); + BOOST_TEST_EQ(b1.has_buffers(), false); + BOOST_TEST_THROWS(b1.size(), std::invalid_argument); + BOOST_TEST_THROWS(b1.data(), std::invalid_argument); + BOOST_TEST_NO_THROW(b1.rewind()); + grind(b1, s1, fec); + } + + void run() + { + testEmpty(); + testBuffers(); + testStream(); + testFail(); + } +}; + +TEST_SUITE( + body_source_test, + "boost.beast2.body_source"); + +} // beast2 +} // boost diff --git a/test/unit/server/call_mf.cpp b/test/unit/server/call_mf.cpp deleted file mode 100644 index ace88d5c..00000000 --- a/test/unit/server/call_mf.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include diff --git a/test/unit/server/http_stream.cpp b/test/unit/server/http_stream.cpp deleted file mode 100644 index eb558dc5..00000000 --- a/test/unit/server/http_stream.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct http_stream_test -{ - void run() - { - } -}; - -TEST_SUITE(http_stream_test, "boost.beast2.server.http_stream_test"); - -} // beast2 -} // boost diff --git a/test/unit/server/https_server.cpp b/test/unit/server/https_server.cpp deleted file mode 100644 index 9a58b2d1..00000000 --- a/test/unit/server/https_server.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct https_server_test -{ - void run() - { - } -}; - -TEST_SUITE(https_server_test, "boost.beast2.server.https_server"); - -} // beast2 -} // boost diff --git a/test/unit/server/route_handler.cpp b/test/unit/server/route_handler.cpp deleted file mode 100644 index 8ca97180..00000000 --- a/test/unit/server/route_handler.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct route_handler_test -{ - using test_router = router; - - void check( - test_router& r, - http_proto::method verb, - core::string_view url, - route_result rv0 = route::send) - { - Request req; - Response res; - auto rv = r.dispatch( - verb, urls::url_view(url), req, res); - if(BOOST_TEST_EQ(rv.message(), rv0.message())) - BOOST_TEST(rv == rv0); - } - - void testData() - { - static auto const POST = http_proto::method::post; - - struct auth_token - { - bool valid = false; - }; - - struct session_token - { - bool valid = false; - }; - - test_router r; - r.use( - [](Request&, Response& res) - { - // create session_token - auto& st = res.data.try_emplace(); - BOOST_TEST_EQ(st.valid, false); - return route::next; - }); - r.use("/user", - [](Request&, Response& res) - { - // make session token valid - auto* st = res.data.find(); - if(BOOST_TEST_NE(st, nullptr)) - st->valid = true; - return route::next; - }); - r.route("/user/auth") - .add(POST, - [](Request& req, Response& res) - { - auto& st = res.data.get(); - BOOST_TEST_EQ(st.valid, true); - // create auth_token each time - auto& at = req.data.emplace(); - at.valid = true; - return route::next; - }, - [](Request& req, Response& res) - { - auto& at = req.data.get(); - auto& st = res.data.get(); - BOOST_TEST_EQ(at.valid, true); - BOOST_TEST_EQ(st.valid, true); - return route::send; - }); - check(r, POST, urls::url_view("/user/auth")); - } - - void run() - { - testData(); - } -}; - -TEST_SUITE( - route_handler_test, - "boost.http_proto.server.route_handler"); - -} // beast2 -} // boost diff --git a/test/unit/server/route_handler_asio.cpp b/test/unit/server/route_handler_asio.cpp deleted file mode 100644 index 723e855a..00000000 --- a/test/unit/server/route_handler_asio.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct route_handler_asio_test -{ - struct stream - { - }; - - using test_router = basic_router< - Request, ResponseAsio>; - - void check( - test_router& r, - core::string_view url, - route_result rv0 = route::send) - { - Request req; - ResponseAsio res; - auto rv = r.dispatch( - http_proto::method::get, - urls::url_view(url), req, res); - if(BOOST_TEST_EQ(rv.message(), rv0.message())) - BOOST_TEST(rv == rv0); - } - - struct handler - { - template - route_result - operator()( - Request&, - ResponseAsio&) const - { - BOOST_TEST(true); - return route::send; - } - }; - - void run() - { - test_router r; - r.use(handler{}); - check(r,"/"); - } -}; - -TEST_SUITE( - route_handler_asio_test, - "boost.beast2.server.route_handler_asio"); - -} // beast2 -} // boost diff --git a/test/unit/server/router_asio.cpp b/test/unit/server/router_asio.cpp deleted file mode 100644 index 1ab4b4d8..00000000 --- a/test/unit/server/router_asio.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -namespace { - -struct Stream -{ -}; - -BOOST_CORE_STATIC_ASSERT( - std::is_constructible>::value); - -} // (anon) - -struct router_asio_test -{ - void - run() - { - } -}; - -TEST_SUITE(router_asio_test, "boost.beast2.server.router_asio"); - -} // beast2 -} // boost \ No newline at end of file diff --git a/test/unit/server/router_types.cpp b/test/unit/server/router_types.cpp deleted file mode 100644 index 7350991b..00000000 --- a/test/unit/server/router_types.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -struct router_types_test -{ - void run() - { - } -}; - -TEST_SUITE( - router_types_test, - "boost.http_proto.server.router_types"); - -} // beast2 -} // boost diff --git a/test/unit/ssl_stream.cpp b/test/unit/ssl_stream.cpp deleted file mode 100644 index 17af0fd0..00000000 --- a/test/unit/ssl_stream.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include -#include -#include -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -class ssl_stream_test -{ -public: - void - run() - { - asio::io_context ioc; - asio::ssl::context ctx(asio::ssl::context::tlsv12); - ssl_stream ss(ioc.get_executor(), ctx); - // ss.async_read_some(asio::mutable_buffer{}); - } -}; - -TEST_SUITE(ssl_stream_test, "boost.beast2.ssl_stream"); - -} // beast2 -} // boost diff --git a/test/unit/test_helpers.hpp b/test/unit/test_helpers.hpp deleted file mode 100644 index 4dc1c05a..00000000 --- a/test/unit/test_helpers.hpp +++ /dev/null @@ -1,199 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -#ifndef BOOST_BEAST2_TEST_TEST_HELPERS_HPP -#define BOOST_BEAST2_TEST_TEST_HELPERS_HPP - -#include -#include -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { -namespace test { - -/** A CompletionHandler used for testing. - - This completion handler is used by tests to ensure correctness - of behavior. It is designed as a single type to reduce template - instantiations, with configurable settings through constructor - arguments. Typically this type will be used in type lists and - not instantiated directly; instances of this class are returned - by the helper functions listed below. - - @see success_handler, @ref fail_handler, @ref any_handler -*/ -class handler -{ - boost::optional ec_; - bool pass_ = false; - boost::source_location loc_{BOOST_CURRENT_LOCATION}; - - public: - handler( - boost::source_location loc = BOOST_CURRENT_LOCATION) - : loc_(loc) - { - } - - explicit - handler( - system::error_code ec, - boost::source_location loc = BOOST_CURRENT_LOCATION) - : ec_(ec) - , loc_(loc) - { - } - - explicit - handler( - boost::none_t, - boost::source_location loc = BOOST_CURRENT_LOCATION) - : loc_(loc) - { - } - - handler(handler&& other) - : ec_(other.ec_) - , pass_(boost::exchange(other.pass_, true)) - , loc_(other.loc_) - - { - } - - ~handler() - { - test_suite::any_runner::instance().test( - pass_, - "handler never invoked", - "", - loc_.file_name(), - loc_.line()); - } - - template - void - operator()(system::error_code ec, Args&&...) - { - test_suite::any_runner::instance().test( - !pass_, - "handler invoked multiple times", - "", - loc_.file_name(), - loc_.line()); - - test_suite::any_runner::instance().test( - !ec_.has_value() || ec == *ec_, - ec.message().c_str(), - "", - loc_.file_name(), - loc_.line()); - pass_ = true; - } -}; - -/** Return a test CompletionHandler which requires success. - - The returned handler can be invoked with any signature whose - first parameter is an `system::error_code`. The handler fails the test - if: - - @li The handler is destroyed without being invoked, or - - @li The handler is invoked with a non-successful error code. -*/ -inline -handler -success_handler(boost::source_location loc = BOOST_CURRENT_LOCATION) noexcept -{ - return handler(system::error_code{}, loc); -} - -/** Return a test CompletionHandler which requires invocation. - - The returned handler can be invoked with any signature. - The handler fails the test if: - - @li The handler is destroyed without being invoked. -*/ -inline -handler -any_handler(boost::source_location loc = BOOST_CURRENT_LOCATION) noexcept -{ - return handler(boost::none, loc); -} - -/** Return a test CompletionHandler which requires a specific error code. - - This handler can be invoked with any signature whose first - parameter is an `system::error_code`. The handler fails the test if: - - @li The handler is destroyed without being invoked. - - @li The handler is invoked with an error code different from - what is specified. - - @param ec The error code to specify. -*/ -inline -handler -fail_handler(system::error_code ec,boost::source_location loc = BOOST_CURRENT_LOCATION) noexcept -{ - return handler(ec, loc); -} - -/** Run an I/O context. - - This function runs and dispatches handlers on the specified - I/O context, until one of the following conditions is true: - - @li The I/O context runs out of work. - - @param ioc The I/O context to run -*/ -inline -std::size_t -run(asio::io_context& ioc) -{ - std::size_t n = ioc.run(); - ioc.restart(); - return n; -} - -/** Run an I/O context for a certain amount of time. - - This function runs and dispatches handlers on the specified - I/O context, until one of the following conditions is true: - - @li The I/O context runs out of work. - - @li No completions occur and the specified amount of time has elapsed. - - @param ioc The I/O context to run - - @param elapsed The maximum amount of time to run for. -*/ -template -std::size_t -run_for( - asio::io_context& ioc, - std::chrono::duration elapsed) -{ - std::size_t n = ioc.run_for(elapsed); - ioc.restart(); - return n; -} - -} // test -} // beast2 -} // boost - -#endif diff --git a/test/unit/write.cpp b/test/unit/write.cpp deleted file mode 100644 index 90c5da40..00000000 --- a/test/unit/write.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/beast2 -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace beast2 { - -class any_async_read_stream -{ -}; - -class write_test -{ -public: - void - testWrite() - { - } - - void - run() - { - testWrite(); - } -}; - -TEST_SUITE( - write_test, - "boost.beast2.write"); - -} // beast2 -} // boost