[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  Google\nAccessModifierOffset: -2\nAlignAfterOpenBracket: true\nAlignEscapedNewlinesLeft: true\nAlignOperands: true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Empty\nAlwaysBreakAfterDefinitionReturnType: false\nAlwaysBreakTemplateDeclarations: true\nAlwaysBreakBeforeMultilineStrings: true\nBreakBeforeBinaryOperators: None\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBinPackParameters: true\nBinPackArguments: true\nColumnLimit: 500\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nConstructorInitializerIndentWidth: 8\nDerivePointerAlignment: false\nExperimentalAutoDetectBinPacking: false\nIndentCaseLabels: true\nIndentWrappedFunctionNames: false\nIndentFunctionDeclarationAfterType: false\nMaxEmptyLinesToKeep: 2\nKeepEmptyLinesAtTheStartOfBlocks: false\nNamespaceIndentation: None\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakString: 1000\nPenaltyBreakFirstLessLess: 120\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 200\nPointerAlignment: Left\nSpacesBeforeTrailingComments: 1\nCpp11BracedListStyle: true\nStandard:        Auto\nIndentWidth:     4\nTabWidth:        8\nUseTab:          Never\nBreakBeforeBraces: Attach\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpacesInAngles:  false\nSpaceInEmptyParentheses: false\nSpacesInCStyleCastParentheses: false\nSpaceAfterCStyleCast: true\nSpacesInContainerLiterals: true\nSpaceBeforeAssignmentOperators: true\nContinuationIndentWidth: 8\nCommentPragmas:  '^ IWYU pragma:'\nSpaceBeforeParens: ControlStatements\n...\n"
  },
  {
    "path": ".clang-tidy",
    "content": "---\nChecks: '*,\n-cppcoreguidelines-pro-type-static-cast-downcast,\n-fuchsia-default-arguments-calls,\n-fuchsia-default-arguments,\n-fuchsia-default-arguments-declarations,\n-fuchsia-overloaded-operator,\n-fuchsia-statically-constructed-objects,\n-hicpp-use-auto,\n-modernize-use-auto,\n-modernize-use-trailing-return-type,\n-readability-implicit-bool-conversion,\n-readability-const-return-type,\n-google-runtime-references,\n-misc-non-private-member-variables-in-classes,\n-llvm-include-order,\n-cppcoreguidelines-non-private-member-variables-in-classes,\n-cppcoreguidelines-pro-type-vararg,\n-hicpp-vararg,\n-cppcoreguidelines-owning-memory,\n-llvmlibc-callee-namespace,\n-cppcoreguidelines-pro-bounds-array-to-pointer-decay,\n-hicpp-no-array-decay,\n-modernize-pass-by-value,\n-cppcoreguidelines-pro-bounds-constant-array-index,\n-hicpp-signed-bitwise,\n-llvmlibc-implementation-in-namespace,\n-llvmlibc-restrict-system-libc-headers,\n-readability-function-cognitive-complexity,\n-readability-identifier-length,\n-altera-unroll-loops,\n-altera-id-dependent-backward-branch,\n-bugprone-easily-swappable-parameters,\n-modernize-return-braced-init-list,\n-abseil-string-find-str-contains,\n-cppcoreguidelines-avoid-magic-numbers,\n-readability-magic-numbers,\n-cppcoreguidelines-avoid-do-while,\n-llvmlibc-inline-function-decl,\n-altera-struct-pack-align,\n-boost-use-ranges,\n-cppcoreguidelines-special-member-functions,\n-hicpp-special-member-functions,\n-misc-header-include-cycle,\n-cppcoreguidelines-avoid-const-or-ref-data-members,\n-google-explicit-constructor,\n-hicpp-explicit-conversions\n'\nWarningsAsErrors: '*'\nHeaderFilterRegex: 'include\\/cpr\\/.*\\.h(pp)?'\nFormatStyle: file\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: \"🐛 Bug report\"\ndescription: Something in cpr is not working as expected? Create a report to help us improve.\nlabels: [\"Needs Investigation :mag:\", \"Bug :bug:\"]\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Provide a general summary of the issue in the Title above.\n      Use Markdown to highlight and format your code!\n      [https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf](https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf)\n      [https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue)\n      ⚠️⚠️ If you do not use this template, we will simply close your issue. There are no exceptions for this! These steps, especially the part at the end, are very important to solve your problem quickly and efficiently. Please remember that we are not paid to solve or even answer your issues, so we do all this work in OUR free time. ⚠️⚠️\n- type: textarea\n  attributes:\n    label: Description\n    description: A clear and concise description of what the bug is.\n    placeholder: What happened? Also tell us, what did you expect to happen?\n  validations:\n      required: true\n- type: textarea\n  attributes:\n    label: Example/How to Reproduce\n    description: \"Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code to reproduce, if relevant.\"\n    value: |\n      1. Create a `cpr::Session`\n      2. Set option ...\n      3. Perform the request\n      4. See error\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Possible Fix\n    description: A possible fix for your issue.\n    placeholder: Not obligatory, but suggest a fix or reason for the bug.\n  validations:\n    required: false\n- type: dropdown\n  attributes:\n    label: Where did you get it from?\n    multiple: true\n    options:\n      - GitHub (branch e.g. master)\n      - vcpkg\n      - conan\n      - NuGet\n      - Other (specify in \"Additional Context/Your Environment\")\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Additional Context/Your Environment\n    description: Provide some additional context for your issue and your environment your are trying to use cpr in.\n    value: |\n        - OS:\n        - Version:\n  validations:\n    required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "content": "name: \"✨ Feature request\"\ndescription: Suggest an idea for this project.\nlabels: [\"Needs Investigation :mag:\", \"Feature :sparkles:\"]\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Provide a general summary of the feature in the Title above.\n      Use Markdown to highlight and format your code!\n      [https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf](https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf)\n      [https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue)\n      ⚠️⚠️ If you do not use this template, we will simply close your feature request. There are no exceptions for this! These steps are very important to solve your problem quickly and efficiently. Please remember that we are not paid to solve or even answer your feature requests, so we do all this work in OUR free time. ⚠️⚠️\n- type: markdown\n  attributes:\n    value: |\n      Thanks for suggesting new features or pointing our missing functionality.  \n      Please describe your request in detail so we can understand your ideas. Feel free to upload additional material such as mockups, diagrams, or sketches.\n- type: textarea\n  attributes:\n    label: Is your feature request related to a problem?\n    description: Please describe. A clear and concise description of what the problem is.\n    placeholder: Ex. I'm always frustrated when ...\n- type: textarea\n  attributes:\n    label: Possible Solution\n    description: Describe the solution you'd like.\n  validations:\n      required: true\n- type: textarea\n  attributes:\n    label: Alternatives\n    description: A clear and concise description of any alternative solutions or features you've considered.\n- type: textarea\n  attributes:\n    label: Additional Context\n    description: Add any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/build-deb.yml",
    "content": "name: Build Debian Package\non:\n  push:\n    tags: [ '[0-9]+.[0-9]+.[0-9]+' ]\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'The optional semantic version number. If not supplied the version from the main cpr CMake project will be used.'\n        type: string\n\njobs:\n  package-ubuntu-latest-amd64:\n    runs-on: ubuntu-latest\n    steps:\n    - name: \"Checkout\"\n      uses: actions/checkout@v6\n      # Install packages necessary for building libcpr and package\n    - name: \"Update package list\"\n      run: sudo apt update\n    - name: \"Install cpr dependencies\"\n      run: sudo apt install -y libssl-dev libcurl4-openssl-dev libpsl-dev\n    - name: \"Install building tools\"\n      run: sudo apt install -y cmake debmake devscripts debhelper\n      # Set version number\n    - name: Set version based on input\n      if: ${{ inputs.version }}\n      run: echo \"RELEASE_VERSION=${{ inputs.version }}\" >> \"$GITHUB_ENV\"\n    - name: Set version based on ref\n      if: ${{ !inputs.version }}\n      run: |\n        mkdir -p build\n        pushd build\n        cmake .. -DCPR_BUILD_VERSION_OUTPUT_ONLY=ON -DCPR_USE_SYSTEM_LIB_PSL=ON -DCPR_USE_SYSTEM_CURL=ON\n        echo \"RELEASE_VERSION=$(cat version.txt)\" >> $GITHUB_ENV\n        popd\n        rm -rf build\n    - name: Print Version\n      run: echo \"deb version will be '${{ env.RELEASE_VERSION }}'\"\n      # Build package of runtime library\n    - name: \"Package build of runtime library\"\n      env: \n        VERSION: ${{ env.RELEASE_VERSION }}\n      run: bash package-build/build-package.sh $(pwd)\n\n    - name: \"Upload deb-packages\"\n      uses: actions/upload-artifact@v6\n      with:\n        name: artifact-deb\n        path: ./*.deb\n"
  },
  {
    "path": ".github/workflows/build-nuget.yml",
    "content": "name: Build NuGet Package\non:\n  push:\n    tags: [ '[0-9]+.[0-9]+.[0-9]+' ]\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'The optional semantic version number. If not supplied the branch/tag will be used.'\n        type: string\n      no_publish:\n          description: 'Prevent publishing the NuGet package. Just build it and then upload it as an artifact.'\n          type: boolean\n          default: false\n\njobs:\n  package-nuget:\n    runs-on: windows-2022\n\n    # Use PowerShell everywhere unless a step overrides it.\n    defaults:\n      run:\n        shell: pwsh\n\n    steps:\n    # ────────────────────────────── Version handling ─────────────────────────────\n    - name: Set version from manual input\n      if: ${{ github.event_name == 'workflow_dispatch' && inputs.version != '' }}\n      run: |\n        \"RELEASE_VERSION=${{ inputs.version }}\" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append\n\n    - name: Set version from Git ref\n      if: ${{ env.RELEASE_VERSION == '' }}\n      run: |\n        $ref = \"${{ github.ref }}\".Split('/')[-1]\n        \"RELEASE_VERSION=$ref\" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append\n\n    - name: Print version\n      run: Write-Host \"NuGet package version will be '$env:RELEASE_VERSION'.\"\n\n    # ───────────────────────────── Repository & tools ────────────────────────────\n    - uses: actions/checkout@v6\n\n    - uses: actions/setup-python@v6\n  \n    - name: Install meson\n      run: pip install meson\n\n    # Prepare output folder paths\n    - name: Prepare NuGet output layout\n      shell: bash\n      run: |\n        set -eux\n        mkdir -p \"$GITHUB_WORKSPACE/nuget/build/native/{x86,x64}/{Debug,Release}\"\n        cp README.md \"$GITHUB_WORKSPACE/nuget\"\n\n    # ─────────────────────────────── Build x86 ─────────────────────────────────\n    - name: Enable MSVC x86 toolchain\n      uses: ilammy/msvc-dev-cmd@v1\n      with:\n        arch: x86\n\n    - name: Configure & build Release x86\n      run: |\n        cmake -S . -B build-release-x86  -G \"Visual Studio 17 2022\" -A Win32 -DCMAKE_INSTALL_PREFIX=\"$env:GITHUB_WORKSPACE/nuget/build/native/x86/Release\" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Release\n        cmake --build build-release-x86 --config Release --target install\n\n    - name: Configure & build Debug x86\n      run: |\n        cmake -S . -B build-debug-x86 -G \"Visual Studio 17 2022\" -A Win32 -DCMAKE_INSTALL_PREFIX=\"$env:GITHUB_WORKSPACE/nuget/build/native/x86/Debug\" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Debug\n        cmake --build build-debug-x86 --config Debug --target install\n\n    # ─────────────────────────────── Build x64 ─────────────────────────────────\n    - name: Enable MSVC x64 toolchain\n      uses: ilammy/msvc-dev-cmd@v1\n      with:\n        arch: x64\n\n    - name: Configure & build Release x64\n      run: |\n        cmake -S . -B build-release-x64 -G \"Visual Studio 17 2022\" -A x64 -DCMAKE_INSTALL_PREFIX=\"$env:GITHUB_WORKSPACE/nuget/build/native/x64/Release\" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Release\n        cmake --build build-release-x64 --config Release --target install\n\n    - name: Configure & build Debug x64\n      run: |\n        cmake -S . -B build-debug-x64 -G \"Visual Studio 17 2022\" -A x64 -DCMAKE_INSTALL_PREFIX=\"$env:GITHUB_WORKSPACE/nuget/build/native/x64/Debug\" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Debug\n        cmake --build build-debug-x64 --config Debug --target install\n\n    # ────────────────────────── Pack, push, artefact ─────────────────────────────\n    - name: Create NuGet package\n      env:\n        COMMIT_HASH: ${{ github.sha }}\n      run: nuget pack \"$env:GITHUB_WORKSPACE/nuget/libcpr.nuspec\" -OutputDirectory \"$env:GITHUB_WORKSPACE\" -Properties \"VERSION=$env:RELEASE_VERSION;COMMIT_HASH=$env:COMMIT_HASH\"\n\n    - name: Publish package to NuGet.org\n      if: ${{ !inputs.no_publish }}\n      env:\n        NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}\n      run: nuget push \"$env:GITHUB_WORKSPACE\\*.nupkg\" $env:NUGET_API_KEY -Source https://api.nuget.org/v3/index.json\n\n    - name: Upload built .nupkg as workflow artefact\n      uses: actions/upload-artifact@v6\n      with:\n        name: artifact-nuget\n        path: '*.nupkg'\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non: [push, workflow_dispatch, pull_request] # Trigger for every push as well as for every pull request. Yes, this will run stuff twice in case we create a PR from inside this repo. I'm open for better solutions, where I do not have to specify each brach individually for the 'push' trigger.\n\nenv:\n  # Enable verbose output.\n  # Repeat up to 5 times to deal with flaky tests.\n  CTEST_OPTIONS: \"--repeat until-pass:5 --output-on-failure\"\n  # The OpenSSL path for CI runs. Found via 'brew info openssl'.\n  MACOS_OPENSSL_ROOT_DIR: \"/opt/homebrew/Cellar/openssl@3/3.3.0\"\n\njobs:\n  ubuntu-clang-openssl:\n    strategy:\n      matrix:\n        container: [\"ubuntu:latest\", \"ubuntu:rolling\"]\n        systemCurl: [ON, OFF]\n        buildType: [Debug, Release]\n    runs-on: ubuntu-latest\n    container: ${{ matrix.container }}\n    steps:\n    - name: Update package list\n      run: apt update\n    - name: Install Dependencies\n      run: apt install -y git libssl-dev cmake build-essential clang libcurl4-openssl-dev libpsl-dev meson libunistring-dev\n      env:\n        DEBIAN_FRONTEND: noninteractive\n    - name: Setup cmake\n      uses: jwlawson/actions-setup-cmake@v2.1\n      with:\n        cmake-version: '3.22.x'\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_CURL: ${{ matrix.systemCurl }}\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  ubuntu-gcc-openssl:\n    strategy:\n      matrix:\n        container: [\"ubuntu:latest\", \"ubuntu:rolling\"]\n        systemCurl: [ON, OFF]\n        buildType: [Debug, Release]\n    runs-on: ubuntu-latest\n    container: ${{ matrix.container }}\n    steps:\n    - name: Update package list\n      run: apt update\n    - name: Install Dependencies\n      run: apt install -y git libssl-dev cmake build-essential libcurl4-openssl-dev libpsl-dev meson libunistring-dev\n      env:\n        DEBIAN_FRONTEND: noninteractive\n    - name: Setup cmake\n      uses: jwlawson/actions-setup-cmake@v2.1\n      with:\n        cmake-version: '3.22.x'\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_CURL: ${{ matrix.systemCurl }}\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: gcc\n        cxx: g++\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  ubuntu-gcc-mbedtls:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Update package list\n      run: sudo apt update\n    - name: Install Dependencies\n      run: sudo apt install -y git libssl-dev libmbedtls-dev cmake build-essential libpsl-dev meson libunistring-dev\n      env:\n        DEBIAN_FRONTEND: noninteractive\n    - name: Setup cmake\n      uses: jwlawson/actions-setup-cmake@v2.1\n      with:\n        cmake-version: '3.22.x'\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_MBEDTLS_BACKEND: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: gcc\n        cxx: g++\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  fedora-clang-openssl:\n    runs-on: ubuntu-latest\n    container: \"fedora:latest\"\n    steps:\n    - name: Update package list\n      run: dnf update -y\n    - name: Install Dependencies\n      run: dnf install -y gcc g++ clang git make openssl-devel libcurl-devel cmake libpsl-devel libunistring-devel meson\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_CURL: OFF\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  fedora-gcc-openssl:\n    strategy:\n      matrix:\n        systemCurl: [ON, OFF]\n        buildType: [Debug, Release]\n    runs-on: ubuntu-latest\n    container: \"fedora:latest\"\n    steps:\n    - name: Update package list\n      run: dnf update -y\n    - name: Install Dependencies\n      run: dnf install -y gcc g++ clang git make openssl-devel libcurl-devel cmake libpsl-devel libunistring-devel meson\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_CURL: ${{ matrix.systemCurl }}\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: gcc\n        cxx: g++\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  fedora-gcc-openssl-no-psl:\n    runs-on: ubuntu-latest\n    container: \"fedora:latest\"\n    steps:\n    - name: Update package list\n      run: dnf update -y\n    - name: Install Dependencies\n      run: dnf install -y git gcc g++ make openssl-devel cmake libunistring-devel\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_CURL: OFF\n        CPR_CURL_USE_LIBPSL: OFF\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: gcc\n        cxx: g++\n        build-type: release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  fedora-gcc-ssl-sanitizer:\n    strategy:\n      matrix:\n        buildType: [UdefSan, LeakSan, AddrSan] # ThreadSan is disabled for now until all problems are resolved: https://github.com/libcpr/cpr/issues/451\n    runs-on: ubuntu-latest\n    container: \"fedora:latest\" # Use fedora for an up to date version of all sanitizers\n    steps:\n    - name: Update package list\n      run: dnf update -y\n    - name: Install Dependencies\n      run: dnf install -y gcc g++ clang git make openssl-devel libasan libubsan liblsan libtsan cmake libpsl-devel libunistring-devel meson\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: gcc\n        cxx: g++\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  windows-msvc-ssl:\n    strategy:\n      matrix:\n        buildType: [Debug, Release]\n    runs-on: windows-latest\n    steps:\n    - uses: actions/setup-python@v6\n    - name: Install meson\n      run: pip install meson\n    - name: Setup MSVC environment\n      uses: ilammy/msvc-dev-cmd@v1\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CMAKE_GENERATOR: \"Visual Studio 17 2022\"\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: OFF\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  windows-msvc-openssl:\n    runs-on: windows-latest\n    steps:\n    - uses: actions/setup-python@v6\n    - name: Install meson\n      run: pip install meson\n    - name: Setup MSVC environment\n      uses: ilammy/msvc-dev-cmd@v1\n    - name: Install OpenSSL\n      run: choco install openssl -y\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CMAKE_GENERATOR: \"Visual Studio 17 2022\"\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n  \n  macos-clang-ssl:\n    strategy:\n      matrix:\n        buildType: [Debug, Release]\n    runs-on: macos-latest\n    steps:\n    - name: Install libpsl\n      run: brew install libpsl\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: OFF\n        CPR_USE_SYSTEM_LIB_PSL: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: ${{ matrix.buildType }}\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  macos-clang-darwinssl:\n    runs-on: macos-latest\n    steps:\n    - name: Install libpsl\n      run: brew install libpsl\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: OFF\n        CPR_FORCE_DARWINSSL_BACKEND: ON\n        CPR_USE_SYSTEM_LIB_PSL: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n\n  macos-clang-openssl:\n    runs-on: macos-latest\n    steps:\n    - name: Install OpenSSL\n      run: brew install openssl\n    - name: Install libpsl\n      run: brew install libpsl\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_SYSTEM_LIB_PSL: ON\n        OPENSSL_ROOT_DIR: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}\"\n        OPENSSL_LIBRARIES: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib\"\n        LDFLAGS: \"-L${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib\"\n        CPPFLAGS: \"-I${{ env.MACOS_OPENSSL_ROOT_DIR }}/include\"\n        PKG_CONFIG_PATH: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib/pkgconfig\"\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n  \n  macos-clang-openssl-boost:\n    runs-on: macos-latest\n    steps:\n    - name: Install Boost\n      run: brew install boost\n    - name: Install OpenSSL\n      run: brew install openssl\n    - name: Install libpsl\n      run: brew install libpsl\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n        CPR_BUILD_TESTS_SSL: ON\n        CPR_FORCE_OPENSSL_BACKEND: ON\n        CPR_USE_BOOST_FILESYSTEM: ON\n        CPR_USE_SYSTEM_LIB_PSL: ON\n        OPENSSL_ROOT_DIR: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}\"\n        OPENSSL_LIBRARIES: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib\"\n        LDFLAGS: \"-L${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib\"\n        CPPFLAGS: \"-I${{ env.MACOS_OPENSSL_ROOT_DIR }}/include\"\n        PKG_CONFIG_PATH: \"${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib/pkgconfig\"\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{ github.workspace }}/build\n        source-dir: ${{ github.workspace }}\n        cc: clang\n        cxx: clang++\n        build-type: Release\n        run-test: true\n        ctest-options: ${{ env.CTEST_OPTIONS }}\n"
  },
  {
    "path": ".github/workflows/clang-format.yml",
    "content": "name: \"Test Clang Format\"\n\non: [push, workflow_dispatch, pull_request] # Trigger for every push as well as for every pull request. Yes, this will run stuff twice in case we create a PR from inside this repo. I'm open for better solutions, where I do not have to specify each brach individually for the 'push' trigger.\n\njobs:\n  clang-format:\n    runs-on: ubuntu-latest\n    container: fedora:latest\n    steps:\n    - name: Update package list\n      run: sudo dnf update -y\n    - name: Install clang-format\n      run: sudo dnf install -y clang-tools-extra\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: Check format\n      run: bash scripts/check_clang_format.sh"
  },
  {
    "path": ".github/workflows/clang-tidy.yml",
    "content": "name: \"Test Clang Tidy\"\n\non: [push, workflow_dispatch, pull_request] # Trigger for every push as well as for every pull request. Yes, this will run stuff twice in case we create a PR from inside this repo. I'm open for better solutions, where I do not have to specify each brach individually for the 'push' trigger.\n\njobs:\n  clang-tidy:\n    runs-on: ubuntu-latest\n    container: fedora:latest\n    steps:\n    - name: Update package list\n      run: sudo dnf update -y\n    - name: Install dependencies\n      run: sudo dnf install -y openssl-devel cmake git gcc clang ninja-build libpsl-devel meson\n    - name: Install clang-tidy\n      run: sudo dnf install -y clang-tools-extra\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: \"[Release g++] Build & Test\"\n      env:\n        CPR_BUILD_TESTS: ON\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{github.workspace}}/build\n        source-dir: ${{github.workspace}}\n        cc: clang\n        cxx: clang++\n        build-type: Release\n        run-test: false\n        configure-options: -DCPR_ENABLE_LINTING=ON"
  },
  {
    "path": ".github/workflows/cppcheck.yml",
    "content": "name: \"Test cppcheck\"\n\non: [push, workflow_dispatch, pull_request] # Trigger for every push as well as for every pull request. Yes, this will run stuff twice in case we create a PR from inside this repo. I'm open for better solutions, where I do not have to specify each brach individually for the 'push' trigger.\n\njobs:\n  cppcheck:\n    runs-on: ubuntu-latest\n    container: \"fedora:latest\" # Use fedora for an up to date version of cppcheck\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: Update package list\n      run: dnf update -y\n    - name: Install Dependencies\n      run: dnf install -y gcc clang git gcc gdb make openssl-devel cmake libpsl-devel cppcheck meson\n    - name: \"[Release g++] Build\"\n      env:\n        CPR_ENABLE_CPPCHECK: ON\n        # Avoid parallel runs so only the resulting error file is not being written by multiple processes at the same time.\n        CMAKE_BUILD_PARALLEL_LEVEL: 1\n      uses: ashutoshvarma/action-cmake-build@master\n      with:\n        build-dir: ${{github.workspace}}/build\n        source-dir: ${{github.workspace}}\n        cc: gcc\n        cxx: g++\n        build-type: Release\n        run-test: false"
  },
  {
    "path": ".github/workflows/readme-updater.yml",
    "content": "name: Update GIT_TAG In Readme\non:\n  release:\n    types: [published]\n\n# Workflow configuration\nenv:\n  OUTPUT_BRANCH: \"master\"\n  COMMIT_MESSAGE: \"Update GIT_TAG on new release\"\n\njobs:\n  update-git-tag:\n    permissions:\n      contents: write\n    runs-on: ubuntu-latest\n    steps:\n    - name: \"Checkout\"\n      uses: actions/checkout@v6\n      with:\n        ref: ${{github.sha}}\n    - name: \"Replace GIT_TAG\"\n      run: sed -i -re 's/(GIT_TAG) [0-9a-f]+/\\1 ${{github.sha}}/' README.md\n    - name: \"Commit changes\"\n      uses: stefanzweifel/git-auto-commit-action@v7\n      with:\n        commit_message: ${{env.COMMIT_MESSAGE}}\n        branch: ${{env.OUTPUT_BRANCH}}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n# CMake\nCMakeCache.txt\nCMakeFiles\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\n\n# Custom\nbuild/\n!nuget/build\n\n# Jekyll stuff\n_includes/\n_site/\n\n# Vim\n.ycm_extra_conf.py*\n*.swp\n\n# VSCode\n.vscode/*\n!.vscode/tasks.json\n.vs/\n!.vs/tasks.json\n\n# clangd\n.cache/\n\n# compilation database\n# used in various editor configurations, such as vim & YcM\ncompile_commands.json\n\n# macOS\n.DS_Store\n\n# CLion\n.idea/\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n    // See https://go.microsoft.com/fwlink/?LinkId=733558\n    // for the documentation about the tasks.json format\n    \"version\": \"2.0.0\",\n    \"tasks\": [\n        {\n            \"label\": \"🗑️ Delete build dir\",\n            \"type\": \"shell\",\n            \"command\": \"${workspaceFolder}/scripts/delete_build_dir.sh\",\n            \"problemMatcher\": [],\n            \"group\": {\n                \"kind\": \"build\"\n            },\n            \"presentation\": {\n                \"clear\": true\n            },\n            \"options\": {\n                \"cwd\": \"${workspaceFolder}\"\n            }\n        },\n        {\n            \"label\": \"📝 Run clang-format\",\n            \"type\": \"shell\",\n            \"command\": \"${workspaceFolder}/scripts/run_clang_format.sh\",\n            \"args\": [\n                \"cpr\",\n                \"include\",\n                \"test\"\n            ],\n            \"problemMatcher\": [],\n            \"group\": {\n                \"kind\": \"build\"\n            },\n            \"presentation\": {\n                \"clear\": true\n            },\n            \"options\": {\n                \"cwd\": \"${workspaceFolder}\"\n            }\n        },\n        {\n            \"label\": \"📑 Check clang-format\",\n            \"type\": \"shell\",\n            \"command\": \"${workspaceFolder}/scripts/check_clang_format.sh\",\n            \"args\": [\n                \"cpr\",\n                \"include\",\n                \"test\"\n            ],\n            \"problemMatcher\": [],\n            \"group\": {\n                \"kind\": \"build\"\n            },\n            \"presentation\": {\n                \"clear\": true\n            },\n            \"options\": {\n                \"cwd\": \"${workspaceFolder}\"\n            }\n        }\n    ]\n}"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.18)\nproject(cpr VERSION 1.15.0 LANGUAGES CXX)\n\nmath(EXPR cpr_VERSION_NUM \"${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}\" OUTPUT_FORMAT HEXADECIMAL)\nconfigure_file(\"${cpr_SOURCE_DIR}/cmake/cprver.h.in\" \"${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h\")\n\n# Only change the folder behavior if cpr is not a subproject\nif(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})\n    set_property(GLOBAL PROPERTY USE_FOLDERS ON)\n    set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER \"CMake\")\n    set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)\n    set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)\n    set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json to be used by other tools (e.g. vs code)\nelse()\n    # Check required c++ standard of parent project\n    if(CMAKE_CXX_STANDARD)\n        set(PARENT_CXX_STANDARD ${CMAKE_CXX_STANDARD})\n        message(STATUS \"CXX standard of parent project: ${PARENT_CXX_STANDARD}\")\n    endif()\nendif()\n\n# Avoid the dll boilerplate code for windows\nset(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)\n\nif (PARENT_CXX_STANDARD)\n    # Don't set CMAKE_CXX_STANDARD if it is already set by parent project\n    if (PARENT_CXX_STANDARD LESS 17)\n        message(FATAL_ERROR \"cpr ${cpr_VERSION} does not support ${PARENT_CXX_STANDARD}. Please use cpr <= 1.9.x\")\n    endif()\nelse()\n    # Set standard version if not already set by potential parent project\n    set(CMAKE_CXX_STANDARD 17)\nendif()\n\nmessage(STATUS \"CXX standard: ${CMAKE_CXX_STANDARD}\")\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\nset(CPR_LIBRARIES cpr CACHE INTERNAL \"\")\n\nmacro(cpr_option OPTION_NAME OPTION_TEXT OPTION_DEFAULT)\n    option(${OPTION_NAME} ${OPTION_TEXT} ${OPTION_DEFAULT})\n    if(DEFINED ENV{${OPTION_NAME}})\n        # Allow overriding the option through an environment variable\n        set(${OPTION_NAME} $ENV{${OPTION_NAME}})\n    endif()\n    if(${OPTION_NAME})\n        add_definitions(-D${OPTION_NAME})\n    endif()\n    message(STATUS \"  ${OPTION_NAME}: ${${OPTION_NAME}}\")\nendmacro()\n\nmessage(STATUS \"C++ Requests CMake Options\")\nmessage(STATUS \"=======================================================\")\ncpr_option(CPR_GENERATE_COVERAGE \"Set to ON to generate coverage reports.\" OFF)\ncpr_option(CPR_CURL_NOSIGNAL \"Set to ON to disable use of signals in libcurl.\" OFF)\ncpr_option(CURL_VERBOSE_LOGGING \"Curl verbose logging during building curl\" OFF)\ncpr_option(CPR_USE_SYSTEM_GTEST \"If ON, this project will look in the system paths for an installed gtest library. If none is found it will use the built-in one.\" OFF)\ncpr_option(CPR_USE_SYSTEM_CURL \"If enabled we will use the curl lib already installed on this system.\" OFF)\ncpr_option(CPR_USE_EXISTING_CURL_TARGET \"Use an existing libcurl cmake target instead of having the cpr project source the dependency itself.\" OFF)\ncpr_option(CPR_CURL_USE_LIBPSL \"Since curl 8.13 curl depends on libpsl (https://everything.curl.dev/build/deps.html#libpsl). By default cpr keeps this as a secure default enabled wich in turn requires meson as build dependency. If set to OFF, psl support inside curl will be disabled.\" ON)\ncpr_option(CPR_USE_SYSTEM_LIB_PSL \"If enabled we will use the psl lib already installed on this system. Else meson is required as build dependency. Only relevant in case 'CPR_CURL_USE_LIBPSL' is set to ON.\" ${CPR_USE_SYSTEM_CURL})\ncpr_option(CPR_ENABLE_CURL_HTTP_ONLY \"If enabled we will only use the HTTP/HTTPS protocols from CURL. If disabled, all the CURL protocols are enabled. This is useful if your project uses libcurl and you need support for other CURL features e.g. sending emails.\" ON)\ncpr_option(CPR_ENABLE_SSL \"Enables or disables the SSL backend. Required to perform HTTPS requests.\" ON)\ncpr_option(CPR_FORCE_OPENSSL_BACKEND \"Force to use the OpenSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...).\" OFF)\ncpr_option(CPR_FORCE_WINSSL_BACKEND \"Force to use the WinSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...).\" OFF)\ncpr_option(CPR_FORCE_DARWINSSL_BACKEND \"Force to use the DarwinSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...).\" OFF)\ncpr_option(CPR_FORCE_MBEDTLS_BACKEND \"Force to use the Mbed TLS backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...).\" OFF)\ncpr_option(CPR_ENABLE_LINTING \"Set to ON to enable clang linting.\" OFF)\ncpr_option(CPR_ENABLE_CPPCHECK \"Set to ON to enable Cppcheck static analysis. Requires CPR_BUILD_TESTS and CPR_BUILD_TESTS_SSL to be OFF to prevent checking google tests source code.\" OFF)\ncpr_option(CPR_BUILD_TESTS \"Set to ON to build cpr tests.\" OFF)\ncpr_option(CPR_BUILD_TESTS_SSL \"Set to ON to build cpr ssl tests\" ${CPR_BUILD_TESTS})\ncpr_option(CPR_BUILD_TESTS_PROXY \"Set to ON to build proxy tests. They fail in case there is no valid proxy server available in proxy_tests.cpp\" OFF)\ncpr_option(CPR_BUILD_VERSION_OUTPUT_ONLY \"Set to ON to only export the version into 'build/version.txt' and exit\" OFF)\ncpr_option(CPR_SKIP_CA_BUNDLE_SEARCH \"Skip searching for Certificate Authority certs. Turn ON for systems like iOS where file access is restricted and prevents https from working.\" OFF)\ncpr_option(CPR_USE_BOOST_FILESYSTEM \"Set to ON to use the Boost.Filesystem library. This is useful, on, e.g., Apple platforms, where std::filesystem may not always be available when targeting older OS versions.\" OFF)\ncpr_option(CPR_DEBUG_SANITIZER_FLAG_THREAD \"Enables the ThreadSanitizer for debug builds.\" OFF)\ncpr_option(CPR_DEBUG_SANITIZER_FLAG_ADDR \"Enables the AddressSanitizer for debug builds.\" OFF)\ncpr_option(CPR_DEBUG_SANITIZER_FLAG_LEAK \"Enables the LeakSanitizer for debug builds.\" OFF)\ncpr_option(CPR_DEBUG_SANITIZER_FLAG_UB \"Enables the UndefinedBehaviorSanitizer for debug builds.\" OFF)\ncpr_option(CPR_DEBUG_SANITIZER_FLAG_ALL \"Enables all sanitizers for debug builds except the ThreadSanitizer since it is incompatible with the other sanitizers.\" OFF)\nmessage(STATUS \"=======================================================\")\n\nif (MSVC)\n    if (BUILD_SHARED_LIBS)\n        message(STATUS \"Build windows dynamic libs.\")\n    else()\n        # Add this to build windows pure static library.\n        message(STATUS \"Build windows static libs.\")\n    endif()\nendif()\n\n# Save the project version as txt file for deb and NuGet builds\nif(CPR_BUILD_VERSION_OUTPUT_ONLY)\n    message(STATUS \"Printing version and exiting...\")\n    file(WRITE \"${CMAKE_BINARY_DIR}/version.txt\" \"${PROJECT_VERSION}\")\n    return()\nendif()\n\nif (CPR_FORCE_USE_SYSTEM_CURL)\n    message(WARNING \"The variable CPR_FORCE_USE_SYSTEM_CURL is deprecated, please use CPR_USE_SYSTEM_CURL instead\")\n    set(CPR_USE_SYSTEM_CURL ${CPR_FORCE_USE_SYSTEM_CURL})\nendif()\n\ninclude(GNUInstallDirs)\ninclude(FetchContent)\ninclude(cmake/code_coverage.cmake)\ninclude(cmake/sanitizer.cmake)\ninclude(cmake/clear_variable.cmake)\n\n# So CMake can find FindMbedTLS.cmake\nset(CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}\")\n\n# Linting\nif(CPR_ENABLE_LINTING)\n    include(cmake/clang-tidy.cmake)\nendif()\n\n# Cppcheck\nif(CPR_ENABLE_CPPCHECK)\n    if(CPR_BUILD_TESTS OR CPR_BUILD_TESTS_SSL)\n        message(FATAL_ERROR \"Cppcheck is incompatible with building tests. Make sure to disable CPR_ENABLE_CPPCHECK or disable tests by setting CPR_BUILD_TESTS and CPR_BUILD_TESTS_SSL to OFF. This is because Cppcheck would try to check the google tests source code and then fail. \")\n    endif()\n    include(cmake/cppcheck.cmake)\nendif()\n\n# SSL\nif(CPR_ENABLE_SSL)\n    if(CPR_FORCE_OPENSSL_BACKEND OR CPR_FORCE_WINSSL_BACKEND OR CPR_FORCE_DARWINSSL_BACKEND OR CPR_FORCE_MBEDTLS_BACKEND)\n        message(STATUS \"Disabled SSL backend auto detect since either CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, or CPR_FORCE_WINSSL_BACKEND is enabled.\")\n        set(DETECT_SSL_BACKEND OFF CACHE INTERNAL \"\" FORCE)\n    else()\n        message(STATUS \"Automatically detecting SSL backend.\")\n        set(DETECT_SSL_BACKEND ON CACHE INTERNAL \"\" FORCE)\n    endif()\n\n    if(CPR_FORCE_WINSSL_BACKEND AND (NOT WIN32))\n        message(FATAL_ERROR \"WinSSL is only available on Windows! Use either OpenSSL (CPR_FORCE_OPENSSL_BACKEND) or DarwinSSL (CPR_FORCE_DARWINSSL_BACKEND) instead.\")\n    endif()\n\n    if(DETECT_SSL_BACKEND)\n        message(STATUS \"Detecting SSL backend...\")\n        if(WIN32)\n            message(STATUS \"SSL auto detect: Using WinSSL.\")\n            set(SSL_BACKEND_USED \"WinSSL\")\n        elseif(APPLE)\n            message(STATUS \"SSL auto detect: Using DarwinSSL.\")\n            set(CPR_BUILD_TESTS_SSL OFF)\n            set(SSL_BACKEND_USED \"DarwinSSL\")\n        else()\n            find_package(OpenSSL)\n            if(OPENSSL_FOUND)\n                message(STATUS \"SSL auto detect: Using OpenSSL.\")\n                set(SSL_BACKEND_USED \"OpenSSL\")\n            else()\n                find_package(MbedTLS)\n                if(MBEDTLS_FOUND)\n                    set(SSL_BACKEND_USED \"MbedTLS\")\n                else()\n                    message(FATAL_ERROR \"No valid SSL backend found! Please install OpenSSL, Mbed TLS or disable SSL by setting CPR_ENABLE_SSL to OFF.\")\n                endif()\n            endif()\n        endif()\n    else()\n        if(CPR_FORCE_OPENSSL_BACKEND)\n            find_package(OpenSSL)\n            if(OPENSSL_FOUND)\n                message(STATUS \"Using OpenSSL.\")\n                set(SSL_BACKEND_USED \"OpenSSL\")\n            else()\n                message(FATAL_ERROR \"CPR_FORCE_OPENSSL_BACKEND enabled but we were not able to find OpenSSL!\")\n            endif()\n        elseif(CPR_FORCE_WINSSL_BACKEND)\n            message(STATUS \"Using WinSSL.\")\n            set(SSL_BACKEND_USED \"WinSSL\")\n        elseif(CPR_FORCE_DARWINSSL_BACKEND)\n            message(STATUS \"Using DarwinSSL.\")\n            set(CPR_BUILD_TESTS_SSL OFF)\n            set(SSL_BACKEND_USED \"DarwinSSL\")\n        elseif(CPR_FORCE_MBEDTLS_BACKEND)\n            message(STATUS \"Using Mbed TLS.\")\n            set(CPR_BUILD_TESTS_SSL OFF)\n            set(SSL_BACKEND_USED \"MbedTLS\")\n        endif()\n    endif()\nendif()\n\nif(SSL_BACKEND_USED STREQUAL \"OpenSSL\")\n# Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly\nfind_package(OpenSSL REQUIRED)\n    add_compile_definitions(OPENSSL_BACKEND_USED)\nendif()\n\n# Curl configuration\nif(CPR_USE_EXISTING_CURL_TARGET)\n    message(STATUS \"cpr skipping management of curl dependency (CPR_USE_EXISTING_CURL_TARGET is set to ON).\")\nelseif(CPR_USE_SYSTEM_CURL)\n    if(CPR_ENABLE_SSL)\n        find_package(CURL COMPONENTS HTTP HTTPS)\n        if(CURL_FOUND)\n            message(STATUS \"Curl ${CURL_VERSION_STRING} found on this system.\")\n\n            # To be able to load certificates under Windows when using OpenSSL:\n            if(CMAKE_USE_OPENSSL AND WIN32 AND (NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL \"7.71.0\")))\n                message(FATAL_ERROR \"Your system curl version (${CURL_VERSION_STRING}) is too old to support OpenSSL on Windows which requires curl >= 7.71.0. Update your curl version, use WinSSL, disable SSL or use the built-in version of curl.\")\n            endif()\n        else()\n            find_package(CURL COMPONENTS HTTP)\n            if(CURL_FOUND)\n                message(FATAL_ERROR \"Curl found on this system but WITHOUT HTTPS/SSL support. Either disable SSL by setting CPR_ENABLE_SSL to OFF or use the built-in version of curl by setting CPR_USE_SYSTEM_CURL to OFF.\")\n            else()\n                message(FATAL_ERROR \"Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.\")\n            endif()\n        endif()\n    else()\n        find_package(CURL COMPONENTS HTTP)\n        if(CURL_FOUND)\n            message(STATUS \"Curl found on this system.\")\n        else()\n            message(FATAL_ERROR \"Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.\")\n        endif()\n    endif()\n\n    # Check for the minimum supported curl version \n    if(NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL \"7.64.0\"))\n        message(FATAL_ERROR \"Your system curl version (${CURL_VERSION_STRING}) is too old! curl >= 7.64.0 is required. Update your curl version, or use the build in curl version e.g. via `cmake .. -DCPR_USE_SYSTEM_CURL=OFF` during CMake configure.\")\n    endif()\nelse()\n    message(STATUS \"Configuring built-in curl...\")\n\n    # ZLIB is optional for curl\n    # to disable it:\n    # * from command line:\n    #     -DCURL_ZLIB=OFF\n    # * from CMake script:\n    if (CURL_ZLIB OR CURL_ZLIB STREQUAL AUTO OR NOT DEFINED CACHE{CURL_ZLIB})\n        include(cmake/zlib_external.cmake)\n    endif()\n\n    if (CPR_ENABLE_CURL_HTTP_ONLY)\n        # We only need HTTP (and HTTPS) support:\n        set(HTTP_ONLY ON CACHE INTERNAL \"\" FORCE)\n    endif()\n    set(BUILD_CURL_EXE OFF CACHE INTERNAL \"\" FORCE)\n    set(BUILD_TESTING OFF)\n\n    if (CURL_VERBOSE_LOGGING)\n        message(STATUS \"Enabled curl debug features\")\n        set(ENABLE_DEBUG ON CACHE INTERNAL \"\" FORCE)\n    endif()\n\n    if (CPR_ENABLE_SSL)\n        set(CURL_ENABLE_SSL ON CACHE INTERNAL \"\" FORCE)\n        if(ANDROID)\n            set(CURL_CA_PATH \"/system/etc/security/cacerts\" CACHE INTERNAL \"\")\n        elseif(CPR_SKIP_CA_BUNDLE_SEARCH)\n            set(CURL_CA_PATH \"none\" CACHE INTERNAL \"\")\n        else()\n            set(CURL_CA_PATH \"auto\" CACHE INTERNAL \"\")\n        endif()\n\n        if(CPR_SKIP_CA_BUNDLE_SEARCH)\n            set(CURL_CA_BUNDLE \"none\" CACHE INTERNAL \"\")\n        elseif(NOT DEFINED CURL_CA_BUNDLE)\n            set(CURL_CA_BUNDLE \"auto\" CACHE INTERNAL \"\")\n        endif()\n\n        if(SSL_BACKEND_USED STREQUAL \"WinSSL\")\n            set(CURL_USE_SCHANNEL ON CACHE INTERNAL \"\" FORCE)\n            set(CURL_WINDOWS_SSPI ON CACHE INTERNAL \"\" FORCE)\n        endif()\n\n        if(SSL_BACKEND_USED STREQUAL \"OpenSSL\")\n            set(CURL_USE_OPENSSL ON CACHE INTERNAL \"\" FORCE)\n        endif()\n\n        if(SSL_BACKEND_USED STREQUAL \"DarwinSSL\")\n            set(CURL_USE_SECTRANSP ON CACHE INTERNAL \"\" FORCE)\n        endif()\n\n        if(SSL_BACKEND_USED STREQUAL \"MbedTLS\")\n            set(CURL_USE_MBEDTLS ON CACHE INTERNAL \"\" FORCE)\n        endif()\n\n        message(STATUS \"Enabled curl SSL\")\n    else()\n        set(CURL_ENABLE_SSL OFF CACHE INTERNAL \"\" FORCE)\n\n        set(CURL_CA_PATH \"none\" CACHE INTERNAL \"\" FORCE)\n        set(CURL_USE_SCHANNEL OFF CACHE INTERNAL \"\" FORCE)\n        set(CURL_WINDOWS_SSPI OFF CACHE INTERNAL \"\" FORCE)\n        set(CURL_USE_OPENSSL OFF CACHE INTERNAL \"\" FORCE)\n        set(CURL_USE_SECTRANSP OFF CACHE INTERNAL \"\" FORCE)\n        set(CURL_USE_MBEDTLS OFF CACHE INTERNAL \"\" FORCE)\n        message(STATUS \"Disabled curl SSL\")\n    endif()\n    # Disable linting for curl\n    clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\n\n    if (CMAKE_VERSION VERSION_GREATER_EQUAL \"3.24.0\")\n        cmake_policy(SET CMP0135 NEW)\n    endif()\n\n    # Since curl 8.13, curl depends on lib psl\n    set(CURL_USE_LIBPSL ${CPR_CURL_USE_LIBPSL} CACHE INTERNAL \"\" FORCE)\n    if(CPR_CURL_USE_LIBPSL AND NOT CPR_USE_SYSTEM_LIB_PSL)\n        include(libpsl)\n    endif()\n\n    FetchContent_Declare(curl URL https://github.com/curl/curl/releases/download/curl-8_13_0/curl-8.13.0.tar.xz\n                              URL_HASH SHA256=4a093979a3c2d02de2fbc00549a32771007f2e78032c6faa5ecd2f7a9e152025) # the file hash for curl-8.13.0.tar.xz\n    FetchContent_MakeAvailable(curl)\n\n    restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\nendif()\n\n# Depending on which version of libcurl we are using the CMake target is called differently\nif(TARGET libcurl)\n    # Old curl CMake target name\n    set(CURL_LIB libcurl)\nelse()\n    # New curl CMake target name\n    set(CURL_LIB CURL::libcurl)\nendif()\n\n# GTest configuration\nif(CPR_BUILD_TESTS)\n    if(CPR_USE_SYSTEM_GTEST)\n        find_package(GTest)\n    endif()\n    if(NOT CPR_USE_SYSTEM_GTEST OR NOT GTEST_FOUND)\n        message(STATUS \"Not using system gtest, using built-in googletest project instead.\")\n        if(MSVC)\n            # By default, GTest compiles on Windows in CRT static linkage mode. We use this\n            # variable to force it into using the CRT in dynamic linkage (DLL), just as CPR\n            # does.\n            set(gtest_force_shared_crt ON CACHE BOOL \"Force gtest to use the shared c runtime\")\n        endif()\n\n        # Disable linting for google test\n        clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\n\n        FetchContent_Declare(googletest\n                             URL                    https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz\n                             URL_HASH               SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 # the file hash for release-1.14.0.tar.gz\n                             USES_TERMINAL_DOWNLOAD TRUE)   # <---- This is needed only for Ninja to show download progress\n        FetchContent_MakeAvailable(googletest)\n\n        restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\n        \n        add_library(gtest_int INTERFACE)\n        target_link_libraries(gtest_int INTERFACE gtest)\n        target_include_directories(gtest_int INTERFACE ${googletest_SOURCE_DIR}/include)\n\n        add_library(GTest::GTest ALIAS gtest_int)\n       \n        # Group under the \"tests/gtest\" project folder in IDEs such as Visual Studio.\n    set_property(TARGET gtest PROPERTY FOLDER \"tests/gtest\")\n    set_property(TARGET gtest_main PROPERTY FOLDER \"tests/gtest\")\n    endif()\nendif()\n\n\n# Mongoose configuration\nif(CPR_BUILD_TESTS)\n    message(STATUS \"Building mongoose project for test support.\")\n\n    if(CPR_BUILD_TESTS_SSL)\n        if(NOT CPR_ENABLE_SSL)\n            message(FATAL_ERROR \"OpenSSL is required to build SSL test but CPR_ENABLE_SSL is disabled. Either set CPR_ENABLE_SSL to ON or disable CPR_BUILD_TESTS_SSL.\")\n        endif()\n\n        if(NOT(SSL_BACKEND_USED STREQUAL \"OpenSSL\"))\n            message(FATAL_ERROR \"OpenSSL is required for SSL test, but it seams like OpenSSL is not being used as SSL backend. Either set CPR_BUILD_TESTS_SSL to OFF or set CPR_FORCE_OPENSSL_BACKEND to ON and try again.\")\n        endif()\n\n        set(ENABLE_SSL_TESTS ON CACHE INTERNAL \"\")\n    else()\n        set(ENABLE_SSL_TESTS OFF CACHE INTERNAL \"\")\n    endif()\n\n    # Disable linting for mongoose\n    clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\n\n    FetchContent_Declare(mongoose \n                         URL                    https://github.com/cesanta/mongoose/archive/7.7.tar.gz\n                         URL_HASH               SHA256=4e5733dae31c3a81156af63ca9aa3a6b9b736547f21f23c3ab2f8e3f1ecc16c0 # the hash for 7.7.tar.gz\n                         USES_TERMINAL_DOWNLOAD TRUE # This is needed only for Ninja to show download progress\n                         SOURCE_SUBDIR          \"?\") # Nonexistent directory to prevent FetchContent_MakeAvailable from calling add_subdirectory and duplicating the mongoose target\n    if (NOT mongoose_POPULATED)\n        FetchContent_MakeAvailable(mongoose)\n\n        file(INSTALL cmake/mongoose.CMakeLists.txt DESTINATION ${mongoose_SOURCE_DIR})\n        file(RENAME ${mongoose_SOURCE_DIR}/mongoose.CMakeLists.txt ${mongoose_SOURCE_DIR}/CMakeLists.txt)\n        add_subdirectory(${mongoose_SOURCE_DIR} ${mongoose_BINARY_DIR})\n\n    endif()\n    # Group under the \"external\" project folder in IDEs such as Visual Studio.\n    set_property(TARGET mongoose PROPERTY FOLDER \"external\")\n    restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\nendif()\n\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\nelse()\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror\")\n    if (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n        # Disable C++98 compatibility support in clang: https://github.com/libcpr/cpr/issues/927\n        set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-nonportable-system-include-path -Wno-exit-time-destructors -Wno-undef -Wno-global-constructors -Wno-switch-enum -Wno-old-style-cast -Wno-covered-switch-default -Wno-undefined-func-template\")\n    endif()\nendif()\n\nadd_subdirectory(cpr)\nadd_subdirectory(include)\n\nif(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND CPR_BUILD_TESTS)\n    # Disable linting for tests since they are currently not up to the standard\n    clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\n    enable_testing()\n    add_subdirectory(test)\n    restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)\nendif()\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\ncc@libcpr.org.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to C++ Requests\n\nPlease fork this repository and contribute back using [pull requests](https://github.com/libcpr/cpr/pulls). Features can be requested using [issues](https://github.com/libcpr/cpr/issues). All code, comments, and critiques are greatly appreciated.\n\n## Formatting\n\nTo avoid unproductive debates on formatting, this project uses `clang-format` to ensure a consistent style across all source files. Currently, `clang-format` 3.8 is the version of `clang-format` we use. The format file can be found [here](https://github.com/libcpr/cpr/blob/master/.clang-format). To install `clang-format` on Ubuntu, run this:\n\n```\napt-get install clang-format-3.8\n```\n\nTo install `clang-format` on OS X, run this:\n\n```\nbrew install clang-format\n```\n\nNote that `brew` might install a later version of `clang-format`, but it should be mostly compatible with what's run on the Travis servers.\n\nTo run `clang-format` on every source file, run this in the root directory:\n\n```\n./scripts/run_clang_format.sh cpr include/cpr\n```\n\nThis should indicate which files need formatting and also show a diff of the requested changes. More specific usage instructions can be found on the official [LLVM website](http://releases.llvm.org/3.8.0/tools/clang/docs/ClangFormat.html).\n"
  },
  {
    "path": "LICENSE",
    "content": "This license applies to everything except the contents of the \"test\"\ndirectory and its subdirectories.\n\nMIT License\n\nCopyright (c) 2017-2021 Huu Nguyen\nCopyright (c) 2022 libcpr and many other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# C++ Requests: Curl for People <img align=\"right\" height=\"40\" src=\"http://i.imgur.com/d9Xtyts.png\">\n\n[![Documentation](https://img.shields.io/badge/docs-online-informational?style=flat&link=https://docs.libcpr.dev/)](https://docs.libcpr.dev/)\n![CI](https://github.com/libcpr/cpr/workflows/CI/badge.svg)\n[![Gitter](https://badges.gitter.im/libcpr/community.svg)](https://gitter.im/libcpr/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)\n\n## Announcements\n\n* This project is being maintained by [Fabian Sauter](https://github.com/com8) and [Kilian Traub](https://github.com/KingKili).\n* For quick help, and discussion libcpr also offers a [gitter](https://gitter.im/libcpr/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) chat.\n\n## Supported Releases\n| Release                   | Min. C++ Standard | Status                   | Notes |\n|---------------------------|-------------------|--------------------------|-------|\n| master                    | `cpp17`           | ![alt text][preview]     |       |\n| 1.14.x                    | `cpp17`           | ![alt text][supported]   |       |\n| 1.10.x - 1.13.x           | `cpp17`           | ![alt text][unsupported] |       |\n| <= 1.9.x                  | `cpp11`           | ![alt text][unsupported] |       |\n\n[unsupported]: https://img.shields.io/badge/-unsupported-red \"unsupported\"\n[supported]: https://img.shields.io/badge/-supported-green \"supported\"\n[preview]: https://img.shields.io/badge/-preview-orange \"preview\"\n\n## TLDR\n\nC++ Requests is a simple wrapper around [libcurl](http://curl.haxx.se/libcurl) inspired by the excellent [Python Requests](https://github.com/kennethreitz/requests) project.\n\nDespite its name, libcurl's easy interface is far from simple, and errors and frustration often arise from mistakes or misuse. By leveraging the more expressive features of `C++17` (or `C++11` if using cpr <`= 1.9.x), this library distills the process of making network calls into a few clear and concise idioms.\n\nHere's a quick GET request:\n\n```c++\n#include <cpr/cpr.h>\n\nint main(int argc, char** argv) {\n    cpr::Response r = cpr::Get(cpr::Url{\"https://api.github.com/repos/whoshuu/cpr/contributors\"},\n                      cpr::Authentication{\"user\", \"pass\", cpr::AuthMode::BASIC},\n                      cpr::Parameters{{\"anon\", \"true\"}, {\"key\", \"value\"}});\n    r.status_code;                  // 200\n    r.header[\"content-type\"];       // application/json; charset=utf-8\n    r.text;                         // JSON text string\n    return 0;\n}\n```\n\nAnd here's [less functional, more complicated code, without cpr](https://gist.github.com/whoshuu/2dc858b8730079602044).\n\n## Documentation\n\n[![Documentation](https://img.shields.io/badge/docs-online-informational?style=for-the-badge&link=https://docs.libcpr.dev/)](https://docs.libcpr.dev/)\nYou can find the latest documentation [here](https://docs.libcpr.dev/). It's a work in progress, but it should give you a better idea of how to use the library than the [tests](https://github.com/libcpr/cpr/tree/master/test) currently do.\n\n## Features\n\nC++ Requests currently supports:\n\n* Custom headers\n* URL-encoded parameters\n* URL-encoded POST values\n* Multipart form POST upload\n* File POST upload\n* Basic authentication\n* Bearer authentication\n* Digest authentication\n* NTLM authentication\n* Connection and request timeout specification\n* Timeout for low speed connection\n* Asynchronous requests\n* :cookie: support!\n* Proxy support\n* Callback interfaces\n* PUT methods\n* DELETE methods\n* HEAD methods\n* OPTIONS methods\n* PATCH methods\n* Thread Safe access to [libCurl](https://curl.haxx.se/libcurl/c/threadsafe.html)\n* OpenSSL and WinSSL support for HTTPS requests\n* Server Sent Events (SSE) handling\n\n## Planned\n\nFor a quick overview about the planned features, have a look at the next [Milestones](https://github.com/libcpr/cpr/milestones).\n\n## Usage\n\n### CMake\n\n#### fetch_content:\nIf you already have a CMake project you need to integrate C++ Requests with, the primary way is to use `fetch_content`.\nAdd the following to your `CMakeLists.txt`.\n\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(cpr GIT_REPOSITORY https://github.com/libcpr/cpr.git\n                         GIT_TAG f091b2c061b307ee89b164c39976fc9202a1c79d.12.0) # Replace with your desired git commit from: https://github.com/libcpr/cpr/releases\nFetchContent_MakeAvailable(cpr)\n```\n\nThis will produce the target `cpr::cpr` which you can link against the typical way:\n\n```cmake\ntarget_link_libraries(your_target_name PRIVATE cpr::cpr)\n```\n\nThat should do it!\nThere's no need to handle `libcurl` yourself. All dependencies are taken care of for you.\nAll of this can be found in an example [**here**](https://github.com/libcpr/example-cmake-fetch-content).\n\n#### find_package():\nIf you prefer not to use `fetch_content`, you can download, build, and install the library and then use CMake `find_package()` function to integrate it into a project.\n\n**Note:** this feature is feasible only if CPR_USE_SYSTEM_CURL is set. (see [#645](https://github.com/libcpr/cpr/pull/645))\n```Bash\ngit clone https://github.com/libcpr/cpr.git\ncd cpr && mkdir build && cd build\ncmake .. -DCPR_USE_SYSTEM_CURL=ON\ncmake --build . --parallel\nsudo cmake --install .\n```\n#### Build Static Library\nAs an alternative if you want to switch between a static or shared version of cpr use ['-DBUILD_SHARED_LIBS=ON/OFF'](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html).\n```Bash\ngit clone https://github.com/libcpr/cpr.git\ncd cpr && mkdir build && cd build\ncmake .. -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF\ncmake --build . --parallel\nsudo cmake --install .\n```\n\nIn your `CMakeLists.txt`:\n```cmake\nfind_package(cpr REQUIRED)\nadd_executable(your_target_name your_target_name.cpp)\ntarget_link_libraries(your_target_name PRIVATE cpr::cpr)\n```\n\n#### Tests\n`cpr` provides a bunch of tests that can be executed via the following commands.\n```Bash\ngit clone https://github.com/libcpr/cpr.git\ncd cpr && mkdir build && cd build\ncmake .. -DCPR_BUILD_TESTS=ON # There are other test related options like 'CPR_BUILD_TESTS_SSL' and 'CPR_BUILD_TESTS_PROXY'\ncmake --build . --parallel\nctest -VV # -VV is optional since it enables verbose output\n```\n\n### Bazel\n`cpr` can be added as an extension by adding the following lines to your bazel MODULE file (tested with Bazel 8). Edit the versions as needed.\n```starlark\nbazel_dep(name = \"curl\", version = \"8.8.0.bcr.3\")\ngit_repository = use_repo_rule(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"git_repository\")\ngit_repository(\n    name = \"cpr\",\n    build_file = \"//path/to/build:cpr.BUILD\",\n    commit = \"516cb3e5f4e38bede088f69fcf122c6089e38f00\",\n    remote = \"https://github.com/libcpr/cpr.git\",\n    patches = [\"//path/to/patch:cpr.PATCH\"]\n)\n```\n\n```starlark\n// cpr.BUILD\ncc_library(\n    name = \"cpr\",\n    hdrs = glob([\"include/**/*.h\"]),\n    includes = [\"include\"],\n    visibility = [\"//visibility:public\"],\n\n    srcs = glob([\"cpr/**/*.cpp\"]),\n    deps = [\n        \"@curl//:curl\"\n    ],\n)\n```\n\n```starlark\n// Remove this line: cpr.PATCH\n--- include/cpr/cpr.h\n+++ include/cpr/cpr.h\n@@ -10,7 +10,6 @@\n #include \"cpr/connection_pool.h\"\n #include \"cpr/cookies.h\"\n #include \"cpr/cprtypes.h\"\n-#include \"cpr/cprver.h\"\n #include \"cpr/curl_container.h\"\n #include \"cpr/curlholder.h\"\n #include \"cpr/error.h\"\n```\n\n### Packages for Linux Distributions\n\nAlternatively, you may install a package specific to your Linux distribution. Since so few distributions currently have a package for cpr, most users will not be able to run your program with this approach.\n\nCurrently, we are aware of packages for the following distributions:\n\n* [Arch Linux (AUR)](https://aur.archlinux.org/packages/cpr)\n* [Fedora Linux](https://src.fedoraproject.org/rpms/cpr)\n\nIf there's no package for your distribution, try making one! If you do, and it is added to your distribution's repositories, please submit a pull request to add it to the list above. However, please only do this if you plan to actively maintain the package.\n\n### NuGet Package\n\nFor Windows, there is also a libcpr NuGet package available. Currently, x86 and x64 builds are supported with release and debug configuration.\n\nThe package can be found here: [NuGet.org](https://www.nuget.org/packages/libcpr/)\n\n### Port for macOS\n\nOn macOS you may install cpr via [MacPorts.org](https://ports.macports.org/port/cpr) (arm64, x86_64, powerpc)\n\n### FreeBSD Port\n\nOn FreeBSD, you can issue `pkg install cpr` or use the Ports tree to install it.\n\n## Requirements\n\nThe only explicit requirements are:\n\n* A `C++17` compatible compiler such as Clang or GCC. The minimum required version of GCC is unknown, so if anyone has trouble building this library with a specific version of GCC, do let us know.\n* In case you only have a `C++11` compatible compiler available, all versions below cpr 1.9.x are for you. The 1.10.0 release of cpr switches to `C++17` as a requirement.\n* If you would like to perform https requests `OpenSSL` and its development libraries are required.\n* If you do not use the built-in version of [curl](https://github.com/curl/curl) but instead use your systems version, make sure you use a version `>= 7.71.0`. Lower versions are not supported. This means you need Debian `>= 11` or Ubuntu `>= 22.04 LTS`.\n* [`The Meson Build System`](https://mesonbuild.com/) is required build PSL from source ([PSL support for curl](https://everything.curl.dev/build/deps.html#libpsl)). For more information take a look at the `CPR_CURL_USE_LIBPSL` and `CPR_USE_SYSTEM_LIB_PSL` CMake options.\n\n## Building cpr - Using vcpkg\n\nYou can download and install cpr using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:\n```Bash\ngit clone https://github.com/Microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.sh\n./vcpkg integrate install\n./vcpkg install cpr\n```\nThe `cpr` port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.\n\n## Building cpr - Using Conan\n\nYou can download and install `cpr` using the [Conan](https://conan.io/) package manager. Setup your CMakeLists.txt (see [Conan documentation](https://docs.conan.io/en/latest/integrations/build_system.html) on how to use MSBuild, Meson and others).\nAn example can be found [**here**](https://github.com/libcpr/example-cmake-conan).\n\nThe `cpr` package in Conan is kept up to date by Conan contributors. If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the `conan-center-index` repository.\n"
  },
  {
    "path": "cmake/FindMbedTLS.cmake",
    "content": "# Source: https://github.com/curl/curl/blob/curl-7_82_0/CMake/FindMbedTLS.cmake\nfind_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h)\n\nfind_library(MBEDTLS_LIBRARY mbedtls)\nfind_library(MBEDX509_LIBRARY mbedx509)\nfind_library(MBEDCRYPTO_LIBRARY mbedcrypto)\n\nset(MBEDTLS_LIBRARIES \"${MBEDTLS_LIBRARY}\" \"${MBEDX509_LIBRARY}\" \"${MBEDCRYPTO_LIBRARY}\")\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(MbedTLS DEFAULT_MSG\n    MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)\n\nmark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)\n"
  },
  {
    "path": "cmake/clang-tidy.cmake",
    "content": "# Include this file if and only if you want to use clang-tidy linter.\n\nif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL \"Windows\")\n    if (NOT ${CPR_LINTER_PATH} STREQUAL \"\")\n        get_filename_component(CLANG_TIDY_HINT_FILENAME ${CPR_LINTER_PATH} NAME)\n        get_filename_component(CLANG_TIDY_HINT_PATH ${CPR_LINTER_PATH} DIRECTORY)\n    endif ()\n\n    find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy ${CLANG_TIDY_HINT_FILENAME} HINTS ${CLANG_TIDY_HINT_PATH} REQUIRED)\n    mark_as_advanced(CLANG_TIDY_EXECUTABLE)\n\n    message(STATUS \"Enabling clang-tidy: ${CLANG_TIDY_EXECUTABLE}\")\n    set(CMAKE_CXX_CLANG_TIDY \"${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*\")\nelse ()\n    message(FATAL_ERROR \"Clang-tidy is not supported when building for windows\")\nendif ()\n"
  },
  {
    "path": "cmake/clear_variable.cmake",
    "content": "macro(clear_variable)\n    cmake_parse_arguments(CLEAR_VAR \"\" \"DESTINATION;BACKUP;REPLACE\" \"\" ${ARGN})\n    set(${CLEAR_VAR_BACKUP} ${${CLEAR_VAR_DESTINATION}})\n    set(${CLEAR_VAR_DESTINATION} ${CLEAR_VAR_REPLACE})\nendmacro()\n\nmacro(restore_variable)\n    cmake_parse_arguments(CLEAR_VAR \"\" \"DESTINATION;BACKUP\" \"\" ${ARGN})\n    set(${CLEAR_VAR_DESTINATION} ${${CLEAR_VAR_BACKUP}})\n    unset(${CLEAR_VAR_BACKUP})\nendmacro()\n"
  },
  {
    "path": "cmake/code_coverage.cmake",
    "content": "# Code coverage\nif(CPR_BUILD_TESTS AND CPR_GENERATE_COVERAGE)\n    set(CMAKE_BUILD_TYPE COVERAGE CACHE INTERNAL \"Coverage enabled build\")\n    message(STATUS \"Enabling gcov support\")\n    if(NOT \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n        set(COVERAGE_FLAG \"--coverage\")\n    endif()\n    set(CMAKE_CXX_FLAGS_COVERAGE\n        \"-g -O0 ${COVERAGE_FLAG} -fprofile-arcs -ftest-coverage\"\n        CACHE STRING \"Flags used by the C++ compiler during coverage builds.\"\n        FORCE)\n    set(CMAKE_C_FLAGS_COVERAGE\n        \"-g -O0 ${COVERAGE_FLAG} -fprofile-arcs -ftest-coverage\"\n        CACHE STRING \"Flags used by the C compiler during coverage builds.\"\n        FORCE)\n    set(CMAKE_EXE_LINKER_FLAGS_COVERAGE\n        \"\"\n        CACHE STRING \"Flags used for linking binaries during coverage builds.\"\n        FORCE)\n    set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE\n        \"\"\n        CACHE STRING \"Flags used by the shared libraries linker during coverage builds.\"\n        FORCE)\n    mark_as_advanced(\n        CMAKE_CXX_FLAGS_COVERAGE\n        CMAKE_C_FLAGS_COVERAGE\n        CMAKE_EXE_LINKER_FLAGS_COVERAGE\n        CMAKE_SHARED_LINKER_FLAGS_COVERAGE)\nendif()\n"
  },
  {
    "path": "cmake/cppcheck.cmake",
    "content": "# Include this file if and only if you want to use cppcheck.\n\nif (NOT ${CPR_CPPCHECK_PATH} STREQUAL \"\")\n    get_filename_component(CPPCHECK_HINT_FILENAME ${CPR_CPPCHECK_PATH} NAME)\n    get_filename_component(CPPCHECK_HINT_PATH ${CPR_CPPCHECK_PATH} DIRECTORY)\nendif ()\n\nfind_program(CMAKE_CXX_CPPCHECK NAMES cppcheck ${CPPCHECK_HINT_FILENAME} HINTS ${CPPCHECK_HINT_PATH} REQUIRED)\nmessage(STATUS \"Found cppcheck: ${CMAKE_CXX_CPPCHECK}\")\n\nlist(APPEND CMAKE_CXX_CPPCHECK\n        \"--xml\"\n        \"--error-exitcode=1\"\n        \"--enable=warning,style\"\n        \"--force\"\n        \"--inline-suppr\"\n        \"--addon=y2038\"\n        \"--std=c++${CMAKE_CXX_STANDARD}\"\n        \"--cppcheck-build-dir=${PROJECT_BINARY_DIR}\"\n        \"--suppress-xml=${PROJECT_SOURCE_DIR}/cppcheck-suppressions.xml\"\n        \"--output-file=${PROJECT_BINARY_DIR}/cppcheck.xml\"\n        \"--check-level=normal\"\n)\n"
  },
  {
    "path": "cmake/cprConfig-ssl.cmake.in",
    "content": "include(CMakeFindDependencyMacro)\n@PACKAGE_INIT@\n\nfind_dependency(CURL REQUIRED)\nfind_dependency(OpenSSL REQUIRED)\n\ninclude(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)\n\ncheck_required_components(cpr)"
  },
  {
    "path": "cmake/cprConfig.cmake.in",
    "content": "include(CMakeFindDependencyMacro)\n@PACKAGE_INIT@\n\nfind_dependency(CURL REQUIRED)\n\ninclude(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)\n\ncheck_required_components(cpr)"
  },
  {
    "path": "cmake/cprver.h.in",
    "content": "#ifndef CPR_CPRVER_H\n#define CPR_CPRVER_H\n\n/**\n * CPR version as a string.\n **/\n#define CPR_VERSION \"${cpr_VERSION}\"\n\n/**\n * CPR version split up into parts.\n **/\n#define CPR_VERSION_MAJOR ${cpr_VERSION_MAJOR}\n#define CPR_VERSION_MINOR ${cpr_VERSION_MINOR}\n#define CPR_VERSION_PATCH ${cpr_VERSION_PATCH}\n\n/**\n * CPR version as a single hex digit.\n * it can be split up into three parts:\n * 0xAABBCC\n * AA: The current CPR major version number in a hex format.\n * BB: The current CPR minor version number in a hex format.\n * CC: The current CPR patch version number in a hex format.\n *\n * Examples:\n * '0x010702' -> 01.07.02 -> CPR_VERSION: 1.7.2\n * '0xA13722' -> A1.37.22 -> CPR_VERSION: 161.55.34\n **/\n#define CPR_VERSION_NUM ${cpr_VERSION_NUM}\n\n#endif\n"
  },
  {
    "path": "cmake/libpsl.cmake",
    "content": "# Builds libpsl which is especially necessary on Windows since there it is not available via e.g. a package manager.\n\ninclude(ExternalProject)\nfind_program(MESON_PATH meson)\n\nif(MESON_PATH STREQUAL \"MESON_PATH-NOTFOUND\")\n    message(FATAL_ERROR \"meson not found. Please make sure you have meson installed on your system (https://mesonbuild.com/Getting-meson.html). Meson is required for building libpsl for curl on Windows.\")\n    return()\nendif()\n\nFetchContent_Declare(libpsl_src GIT_REPOSITORY https://github.com/rockdaboot/libpsl.git\n                                GIT_TAG 0.21.5)\nFetchContent_MakeAvailable(libpsl_src) # sets libpsl_src_SOURCE_DIR / _BINARY_DIR\n\nset(LIBPSL_SOURCE_DIR \"${libpsl_src_SOURCE_DIR}\")\nset(LIBPSL_BUILD_DIR \"${libpsl_src_BINARY_DIR}\")\nset(LIBPSL_INSTALL_DIR \"${CMAKE_BINARY_DIR}/libpsl_src-install\")\nfile(MAKE_DIRECTORY \"${LIBPSL_BUILD_DIR}\")\n\nstring(TOLOWER \"${CMAKE_SYSTEM_NAME}\" MESON_TARGET_HOST_SYSTEM_NAME)\nstring(TOLOWER \"${CMAKE_SYSTEM_PROCESSOR}\" MESON_TARGET_SYSTEM_PROCESSOR_LOWER)\nif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES \"^(x86_64|amd64)$\")\n    set(MESON_TARGET_HOST_CPU_FAMILY \"x86_64\")\nelseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES \"^(i.86|x86)$\")\n    set(MESON_TARGET_HOST_CPU_FAMILY \"x86\")\nelseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES \"^(armv7|armv6|arm)$\")\n    set(MESON_TARGET_HOST_CPU_FAMILY \"arm\")\nelseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES \"^(aarch64|arm64)$\")\n    set(MESON_TARGET_HOST_CPU_FAMILY \"aarch64\")\nelse()\n    set(MESON_TARGET_HOST_CPU_FAMILY \"${MESON_TARGET_SYSTEM_PROCESSOR_LOWER}\")\nendif()\n\nif(CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n    set(MESON_BUILD_TYPE debug)\nelseif(CMAKE_BUILD_TYPE STREQUAL \"RelWithDebInfo\")\n    set(MESON_BUILD_TYPE debugoptimized)\nelse()\n     set(MESON_BUILD_TYPE release)\nendif()\n\ninclude (TestBigEndian)\nTEST_BIG_ENDIAN(IS_BIG_ENDIAN)\nif(IS_BIG_ENDIAN)\n    set(MESON_ENDIAN \"big\")\nelse()\n    set(MESON_ENDIAN \"little\")\nendif()\n\n# libpsl is plain C. Make sure CMake initializes a C tool-chain.\nif(NOT CMAKE_C_COMPILER)\n    enable_language(C) # initializes CMAKE_C_COMPILER, CMAKE_AR, …\nendif()\n\n# Write a meson cross compilation file to allow cross compiling\n# for example for building NuGet packages although usually it is not required.\nfile(WRITE \"${CMAKE_BINARY_DIR}/libpsl-meson-cross.txt\" \"[binaries]\nc = '${CMAKE_C_COMPILER}'\ncpp = '${CMAKE_CXX_COMPILER}'\nar = '${CMAKE_AR}'\nstrip = '${CMAKE_STRIP}'\n\n[host_machine]\nsystem = '${MESON_TARGET_HOST_SYSTEM_NAME}'\ncpu_family = '${MESON_TARGET_HOST_CPU_FAMILY}'\ncpu = '${MESON_TARGET_HOST_CPU_FAMILY}'\nendian = '${MESON_ENDIAN}'\n\")\n\n# Meson configure\n# We only care about static libraries of psl. In case you need a dynamic version, feel free to add support for it.\nmessage(STATUS \"Configuring libpsl...\")\nexecute_process(COMMAND \"${MESON_PATH}\" setup\n                        \"${LIBPSL_BUILD_DIR}\"\n                        \"${LIBPSL_SOURCE_DIR}\"\n                        -Dtests=false\n                        -Ddocs=false\n                        --cross-file \"${CMAKE_BINARY_DIR}/libpsl-meson-cross.txt\"\n                        --buildtype=${MESON_BUILD_TYPE}\n                        --prefix \"${LIBPSL_INSTALL_DIR}\"\n                        --default-library=static\n                RESULT_VARIABLE MESON_SETUP_RC)\nif(MESON_SETUP_RC)\n    message(FATAL_ERROR \"Meson setup for libpsl failed!\")\nendif()\n\n# Meson build\nmessage(STATUS \"Building libpsl...\")\nexecute_process(COMMAND \"${MESON_PATH}\" compile -C \"${LIBPSL_BUILD_DIR}\"\n                RESULT_VARIABLE MESON_COMPILE_RC\n)\nif(MESON_COMPILE_RC)\n    message(FATAL_ERROR \"Meson compile for libpsl failed!\")\nendif()\n\n# Meson install\nmessage(STATUS \"Installing libpsl...\")\nexecute_process(COMMAND \"${MESON_PATH}\" install -C \"${LIBPSL_BUILD_DIR}\"\n                RESULT_VARIABLE MESON_INSTALL_RC)\nif(MESON_INSTALL_RC)\n    message(FATAL_ERROR \"Meson install for libpsl failed!\")\nendif()\n\nlist(APPEND CMAKE_INCLUDE_PATH \"${LIBPSL_INSTALL_DIR}/include\")\n\nif(EXISTS \"${LIBPSL_INSTALL_DIR}/lib64\")\n    set(LIBPSL_LIBRARY \"${LIBPSL_INSTALL_DIR}/lib/libpsl.a\")\n    list(APPEND CMAKE_LIBRARY_PATH \"${LIBPSL_INSTALL_DIR}/lib64\")\nelse()\n    set(LIBPSL_LIBRARY \"${LIBPSL_INSTALL_DIR}/lib/libpsl.a\")\n    list(APPEND CMAKE_LIBRARY_PATH \"${LIBPSL_INSTALL_DIR}/lib\")\nendif()\n\nset(LIBPSL_INCLUDE_DIR  \"${LIBPSL_INSTALL_DIR}/include\")\n\n# Workaround for Windows compilation.\n# Ref: https://github.com/microsoft/vcpkg/pull/38847/files#diff-922fe829582a7e5acf5b0c35181daa63064fc12a2c889c5d89a19e5e02113f1bL44\nadd_compile_definitions(PSL_STATIC=1)\n"
  },
  {
    "path": "cmake/mongoose.CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\nproject(mongoose C)\n\n\nadd_library(mongoose STATIC mongoose.c)\ntarget_include_directories(mongoose PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n\nif(ENABLE_SSL_TESTS)\n    # Enable mongoose SSL\n    target_compile_definitions(mongoose PUBLIC MG_ENABLE_OPENSSL)\n    target_link_libraries(mongoose PUBLIC OpenSSL::SSL)\n\n    # Fix macOS and Windows invalid OpenSSL include path\n    target_include_directories(mongoose PUBLIC \"${OPENSSL_INCLUDE_DIR}\")\nendif()\n"
  },
  {
    "path": "cmake/sanitizer.cmake",
    "content": "include(CheckCXXSourceCompiles)\n\nset(ALL_ACTIVE_SAN_FLAGS \"\")\n\n # No sanitizers when cross compiling to prevent stuff like this: https://github.com/whoshuu/cpr/issues/582\nif(NOT CMAKE_CROSSCOMPILING)\n    function(cpr_check_sanitizer_compile flags result_var)\n        set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS})\n        set(CMAKE_REQUIRED_FLAGS \"${flags}\")\n        check_cxx_source_compiles(\"int main() { return 0; }\" ${result_var})\n        set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG})\n    endfunction()\n\n    # Thread sanitizer\n    set(THREAD_SAN_FLAGS \"-fsanitize=thread\")\n    if(CPR_DEBUG_SANITIZER_FLAG_THREAD)\n        cpr_check_sanitizer_compile(\"${THREAD_SAN_FLAGS}\" THREAD_SANITIZER_AVAILABLE_AND_ENABLED)\n        if(NOT THREAD_SANITIZER_AVAILABLE_AND_ENABLED)\n            message(FATAL_ERROR \"ThreadSanitizer requested but the test program failed to compile with ${THREAD_SAN_FLAGS}.\")\n        endif()\n    endif()\n\n    # Address sanitizer\n    set(ADDR_SAN_FLAGS \"-fsanitize=address\")\n    if(CPR_DEBUG_SANITIZER_FLAG_ADDR)\n        cpr_check_sanitizer_compile(\"${ADDR_SAN_FLAGS}\" ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)\n        if(NOT ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)\n            message(FATAL_ERROR \"AddressSanitizer requested but the test program failed to compile with ${ADDR_SAN_FLAGS}.\")\n        endif()\n    endif()\n\n    # Leak sanitizer\n    set(LEAK_SAN_FLAGS \"-fsanitize=leak\")\n    if(CPR_DEBUG_SANITIZER_FLAG_LEAK)\n        cpr_check_sanitizer_compile(\"${LEAK_SAN_FLAGS}\" LEAK_SANITIZER_AVAILABLE_AND_ENABLED)\n        if(NOT LEAK_SANITIZER_AVAILABLE_AND_ENABLED)\n            message(FATAL_ERROR \"LeakSanitizer requested but the test program failed to compile with ${LEAK_SAN_FLAGS}.\")\n        endif()\n    endif()\n\n    # Undefined behavior sanitizer\n    set(UDEF_SAN_FLAGS \"-fsanitize=undefined\")\n    if(CPR_DEBUG_SANITIZER_FLAG_UB)\n        cpr_check_sanitizer_compile(\"${UDEF_SAN_FLAGS}\" UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)\n        if(NOT UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)\n            message(FATAL_ERROR \"UndefinedBehaviorSanitizer requested but the test program failed to compile with ${UDEF_SAN_FLAGS}.\")\n        endif()\n    endif()\n\n    # All sanitizer (without thread sanitizer)\n    if(CPR_DEBUG_SANITIZER_FLAG_ALL)\n        cpr_check_sanitizer_compile(\"${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}\" ALL_SANITIZERS_AVAILABLE_AND_ENABLED)\n        if(NOT ALL_SANITIZERS_AVAILABLE_AND_ENABLED)\n            message(FATAL_ERROR \"All sanitizers requested but the test program failed to compile with ${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}.\")\n        endif()\n        set(ALL_ACTIVE_SAN_FLAGS \"${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}\")\n    endif()\n\n    if(THREAD_SANITIZER_AVAILABLE_AND_ENABLED)\n        set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}\" CACHE INTERNAL \"Flags used by the C compiler during thread sanitizer builds.\" FORCE)\n        set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}\" CACHE INTERNAL \"Flags used by the C++ compiler during thread sanitizer builds.\" FORCE)\n        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG \"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}\" CACHE INTERNAL \"Flags used for the linker during thread sanitizer builds\" FORCE)\n    elseif(ALL_SANITIZERS_AVAILABLE_AND_ENABLED)\n        set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${ALL_ACTIVE_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls\" CACHE INTERNAL \"Flags used by the C compiler during most possible sanitizer builds.\" FORCE)\n        set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${ALL_ACTIVE_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls\" CACHE INTERNAL \"Flags used by the C++ compiler during most possible sanitizer builds.\" FORCE)\n        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG \"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}\" CACHE INTERNAL \"Flags used for the linker during most possible sanitizer builds\" FORCE)\n    elseif(ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)\n        set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls\" CACHE INTERNAL \"Flags used by the C compiler during address sanitizer builds.\" FORCE)\n        set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls\" CACHE INTERNAL \"Flags used by the C++ compiler during address sanitizer builds.\" FORCE)\n        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG \"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}\" CACHE INTERNAL \"Flags used for the linker during address sanitizer builds\" FORCE)\n    elseif(LEAK_SANITIZER_AVAILABLE_AND_ENABLED)\n        set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer\" CACHE INTERNAL \"Flags used by the C compiler during leak sanitizer builds.\" FORCE)\n        set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer\" CACHE INTERNAL \"Flags used by the C++ compiler during leak sanitizer builds.\" FORCE)\n        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG \"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}\" CACHE INTERNAL \"Flags used for the linker during leak sanitizer builds\" FORCE)\n    elseif(UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)\n        set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}\" CACHE INTERNAL \"Flags used by the C compiler during undefined behavior sanitizer builds.\" FORCE)\n        set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}\" CACHE INTERNAL \"Flags used by the C++ compiler during undefined behavior sanitizer builds.\" FORCE)\n        set(CMAKE_SHARED_LINKER_FLAGS_DEBUG \"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}\" CACHE INTERNAL \"Flags used for the linker during undefined behavior sanitizer builds\" FORCE)\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/std_fs_support_test.cpp",
    "content": "#if __has_include(<filesystem>)\n#include <filesystem>\nnamespace fs = std::filesystem;\n#else\n#include <experimental/filesystem>\nnamespace fs = std::experimental::filesystem;\n#endif\n\nint main() {\n    auto cwd = fs::current_path();\n}\n"
  },
  {
    "path": "cmake/zlib_external.cmake",
    "content": "# ZLIB\n\n# Fix Windows missing \"zlib.dll\":\nif(WIN32 AND (${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}))\n    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/$<CONFIG> CACHE INTERNAL \"\" FORCE)\nendif()\n\nset(ZLIB_COMPAT ON CACHE INTERNAL \"\" FORCE)\nset(ZLIB_ENABLE_TESTS OFF CACHE INTERNAL \"\" FORCE)\n\nFetchContent_Declare(zlib\n                    GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng\n                    GIT_TAG 2.1.3\n                    USES_TERMINAL_DOWNLOAD TRUE)\nFetchContent_MakeAvailable(zlib)\n\n# Fix Windows zlib dll names from \"zlibd1.dll\" to \"zlib.dll\":\nif(WIN32 AND BUILD_SHARED_LIBS)\n    set_target_properties(zlib PROPERTIES OUTPUT_NAME \"zlib\")\n    set_target_properties(zlib PROPERTIES DEBUG_POSTFIX \"\")\n    set_target_properties(zlib PROPERTIES SUFFIX \".dll\")\nendif()\n"
  },
  {
    "path": "cppcheck-suppressions.xml",
    "content": "<?xml version=\"1.0\"?>\n<suppressions>\n    <!-- Exclude Paths -->\n    <suppress>\n        <id>*</id>\n        <fileName>*/build/*</fileName>\n    </suppress>\n    <suppress>\n        <id>CheckLevelMaxBranches</id>\n    </suppress>\n    <suppress>\n        <id>noExplicitConstructor</id>\n    </suppress>\n    <suppress>\n        <id>knownConditionTrueFalse</id>\n        <fileName>*/include/cpr/async_wrapper.h</fileName>\n    </suppress>\n    <suppress>\n        <id>y2038-unsafe-call</id>\n        <fileName>*/cpr/cookies.cpp</fileName>\n    </suppress>\n    <!-- Known Limitation/Bug: https://github.com/libcpr/cpr/issues/1174 -->\n    <suppress>\n        <id>y2038-unsafe-call</id>\n        <fileName>*/include/cpr/low_speed.h</fileName>\n    </suppress>\n    <!-- No reason to check third party includes -->\n    <suppress>\n        <id>y2038-unsafe-call</id>\n        <fileName>*/curl/curl.h</fileName>\n    </suppress>\n    <suppress>\n        <id>normalCheckLevelMaxBranches</id>\n    </suppress>\n    <suppress>\n        <id>constParameterPointer</id>\n        <fileName>*/cpr/util.cpp</fileName>\n    </suppress>\n    <suppress>\n        <id>postfixOperator</id>\n    </suppress>\n    <suppress>\n        <id>syntaxError</id>\n        <fileName>*/cpr/util.cpp</fileName>\n    </suppress>\n</suppressions>"
  },
  {
    "path": "cpr/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\nadd_library(cpr\n        accept_encoding.cpp\n        async.cpp\n        auth.cpp\n        callback.cpp\n        cert_info.cpp\n        connection_pool.cpp\n        cookies.cpp\n        cprtypes.cpp\n        curl_container.cpp\n        curlholder.cpp\n        error.cpp\n        file.cpp\n        multipart.cpp\n        parameters.cpp\n        payload.cpp\n        proxies.cpp\n        proxyauth.cpp\n        session.cpp\n        sse.cpp\n        threadpool.cpp\n        timeout.cpp\n        unix_socket.cpp\n        util.cpp\n        response.cpp\n        redirect.cpp\n        interceptor.cpp\n        ssl_ctx.cpp\n        curlmultiholder.cpp\n        multiperform.cpp)\n\nadd_library(cpr::cpr ALIAS cpr)\n\ntarget_link_libraries(cpr PUBLIC ${CURL_LIB}) # todo should be private, but first dependencies in ssl_options need to be removed\n\n# Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly\nif(SSL_BACKEND_USED STREQUAL \"OpenSSL\")\n        target_link_libraries(cpr PRIVATE OpenSSL::SSL)\n        target_include_directories(cpr PRIVATE ${OPENSSL_INCLUDE_DIR})\nendif()\n\n# Set version for shared libraries.\nset_target_properties(cpr\n        PROPERTIES\n        VERSION ${${PROJECT_NAME}_VERSION}\n        SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})\n\n# Import GNU common install directory variables\ninclude(GNUInstallDirs)\n\ninstall(TARGETS cpr\n        EXPORT cprTargets\n        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})\n\nif(CPR_USE_SYSTEM_CURL)\n        # Include CMake helpers for package config files\n        # Follow this installation guideline: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html\n        include(CMakePackageConfigHelpers)\n\n        write_basic_package_version_file(\n                \"${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake\"\n                VERSION ${${PROJECT_NAME}_VERSION}\n                COMPATIBILITY ExactVersion)\n\n        if(SSL_BACKEND_USED STREQUAL \"OpenSSL\")\n            configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig-ssl.cmake.in\n                \"${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake\"\n                INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)\n        else()\n            configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in\n                \"${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake\"\n                INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)\n        endif()\n\n        install(FILES ${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake\n                ${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)\nendif()\n\ninstall(EXPORT cprTargets\n        FILE cprTargets.cmake\n        NAMESPACE cpr::\n        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)\n"
  },
  {
    "path": "cpr/accept_encoding.cpp",
    "content": "#include \"cpr/accept_encoding.h\"\n\n#include <algorithm>\n#include <initializer_list>\n#include <iterator>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <utility>\n\nnamespace cpr {\n\nAcceptEncoding::AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods) {\n    methods_.clear();\n    std::transform(methods.begin(), methods.end(), std::inserter(methods_, methods_.begin()), [&](cpr::AcceptEncodingMethods method) { return cpr::AcceptEncodingMethodsStringMap.at(method); });\n}\n\nAcceptEncoding::AcceptEncoding(const std::initializer_list<std::string>& string_methods) : methods_{string_methods} {}\n\nbool AcceptEncoding::empty() const noexcept {\n    return methods_.empty();\n}\n\nconst std::string AcceptEncoding::getString() const {\n    return std::accumulate(std::next(methods_.begin()), methods_.end(), *methods_.begin(), [](std::string a, std::string b) { return std::move(a) + \", \" + std::move(b); });\n}\n\n[[nodiscard]] bool AcceptEncoding::disabled() const {\n    if (methods_.find(cpr::AcceptEncodingMethodsStringMap.at(AcceptEncodingMethods::disabled)) != methods_.end()) {\n        if (methods_.size() != 1) {\n            throw std::invalid_argument(\"AcceptEncoding does not accept any other values if 'disabled' is present. You set the following encodings: \" + getString());\n        }\n        return true;\n    }\n    return false;\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/async.cpp",
    "content": "#include \"cpr/async.h\"\n\nnamespace cpr {\n\n// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)\nCPR_SINGLETON_IMPL(GlobalThreadPool);\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/auth.cpp",
    "content": "#include \"cpr/auth.h\"\n\n#include <string_view>\n\nnamespace cpr {\n\nAuthentication::Authentication(std::string_view username, std::string_view password, AuthMode auth_mode) : auth_mode_{auth_mode} {\n    auth_string_.reserve(username.size() + 1 + password.size());\n    auth_string_ += username;\n    auth_string_ += ':';\n    auth_string_ += password;\n}\n\nconst char* Authentication::GetAuthString() const noexcept {\n    return auth_string_.c_str();\n}\n\nAuthMode Authentication::GetAuthMode() const noexcept {\n    return auth_mode_;\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/callback.cpp",
    "content": "#include \"cpr/callback.h\"\n#include \"cpr/cprtypes.h\"\n#include <functional>\n\nnamespace cpr {\nvoid CancellationCallback::SetProgressCallback(ProgressCallback& u_cb) {\n    user_cb.emplace(std::reference_wrapper{u_cb});\n}\n\nbool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const {\n    const bool const_operation = !(cancellation_state->load());\n    return user_cb ? (const_operation && (*user_cb)(dltotal, dlnow, ultotal, ulnow)) : const_operation;\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/cert_info.cpp",
    "content": "#include \"cpr/cert_info.h\"\n#include <cstddef>\n#include <string>\n\nnamespace cpr {\n\nstd::string& CertInfo::operator[](const size_t& pos) {\n    return cert_info_[pos];\n}\n\nCertInfo::iterator CertInfo::begin() {\n    return cert_info_.begin();\n}\nCertInfo::iterator CertInfo::end() {\n    return cert_info_.end();\n}\n\nCertInfo::const_iterator CertInfo::begin() const {\n    return cert_info_.begin();\n}\n\nCertInfo::const_iterator CertInfo::end() const {\n    return cert_info_.end();\n}\n\nCertInfo::const_iterator CertInfo::cbegin() const {\n    return cert_info_.cbegin();\n}\n\nCertInfo::const_iterator CertInfo::cend() const {\n    return cert_info_.cend();\n}\n\nvoid CertInfo::emplace_back(const std::string& str) {\n    cert_info_.emplace_back(str);\n}\n\nvoid CertInfo::push_back(const std::string& str) {\n    cert_info_.push_back(str);\n}\n\nvoid CertInfo::pop_back() {\n    cert_info_.pop_back();\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/connection_pool.cpp",
    "content": "#include \"cpr/connection_pool.h\"\n#include <curl/curl.h>\n#include <memory>\n#include <mutex>\n\nnamespace cpr {\nConnectionPool::ConnectionPool() {\n    CURLSH* curl_share = curl_share_init();\n    this->connection_mutex_ = std::make_shared<std::mutex>();\n\n    auto lock_f = +[](CURL* /*handle*/, curl_lock_data /*data*/, curl_lock_access /*access*/, void* userptr) {\n        std::mutex* lock = static_cast<std::mutex*>(userptr);\n        lock->lock(); // cppcheck-suppress localMutex  // False positive: mutex is used as callback for libcurl, not local scope\n    };\n\n    auto unlock_f = +[](CURL* /*handle*/, curl_lock_data /*data*/, void* userptr) {\n        std::mutex* lock = static_cast<std::mutex*>(userptr);\n        lock->unlock();\n    };\n\n    curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);\n    curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);\n    curl_share_setopt(curl_share, CURLSHOPT_USERDATA, this->connection_mutex_.get());\n    curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, lock_f);\n    curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, unlock_f);\n\n    this->curl_sh_ = std::shared_ptr<CURLSH>(curl_share, [](CURLSH* ptr) {\n        // Make sure to reset callbacks before cleanup to avoid deadlocks\n        curl_share_setopt(ptr, CURLSHOPT_LOCKFUNC, nullptr);\n        curl_share_setopt(ptr, CURLSHOPT_UNLOCKFUNC, nullptr);\n        curl_share_cleanup(ptr);\n    });\n}\n\nvoid ConnectionPool::SetupHandler(CURL* easy_handler) const {\n    curl_easy_setopt(easy_handler, CURLOPT_SHARE, this->curl_sh_.get());\n}\n\n} // namespace cpr"
  },
  {
    "path": "cpr/cookies.cpp",
    "content": "#include \"cpr/cookies.h\"\n#include \"cpr/curlholder.h\"\n#include <chrono>\n#include <ctime>\n#include <iomanip>\n#include <sstream>\n#include <string>\n#include <string_view>\n\nnamespace cpr {\nconst std::string& Cookie::GetDomain() const {\n    return domain_;\n}\n\nbool Cookie::IsIncludingSubdomains() const {\n    return includeSubdomains_;\n}\n\nconst std::string& Cookie::GetPath() const {\n    return path_;\n}\n\nbool Cookie::IsHttpsOnly() const {\n    return httpsOnly_;\n}\n\nstd::chrono::system_clock::time_point Cookie::GetExpires() const {\n    return expires_;\n}\n\nstd::string Cookie::GetExpiresString() const {\n    std::stringstream ss;\n    std::tm tm{};\n    const std::time_t tt = std::chrono::system_clock::to_time_t(expires_);\n#ifdef _WIN32\n    gmtime_s(&tm, &tt);\n#else\n    // NOLINTNEXTLINE(misc-include-cleaner,cert-err33-c) False positive since <ctime> is included. Also ignore the ret value here.\n    gmtime_r(&tt, &tm);\n#endif\n    ss << std::put_time(&tm, \"%a, %d %b %Y %H:%M:%S GMT\");\n    return ss.str();\n}\n\nconst std::string& Cookie::GetName() const {\n    return name_;\n}\n\nconst std::string& Cookie::GetValue() const {\n    return value_;\n}\n\nstd::string Cookies::GetEncoded(const CurlHolder& holder) const {\n    std::stringstream stream;\n    for (const cpr::Cookie& item : cookies_) {\n        // Depending on if encoding is set to \"true\", we will URL-encode cookies\n        stream << (encode ? std::string_view{holder.urlEncode(item.GetName())} : std::string_view{item.GetName()}) << \"=\";\n\n        // special case version 1 cookies, which can be distinguished by\n        // beginning and trailing quotes\n        if (!item.GetValue().empty() && item.GetValue().front() == '\"' && item.GetValue().back() == '\"') {\n            stream << item.GetValue();\n        } else {\n            // Depending on if encoding is set to \"true\", we will URL-encode cookies\n            stream << (encode ? std::string_view{holder.urlEncode(item.GetValue())} : std::string_view{item.GetValue()});\n        }\n        stream << \"; \";\n    }\n    return stream.str();\n}\n\ncpr::Cookie& Cookies::operator[](size_t pos) {\n    return cookies_[pos];\n}\n\nCookies::iterator Cookies::begin() {\n    return cookies_.begin();\n}\n\nCookies::iterator Cookies::end() {\n    return cookies_.end();\n}\n\nCookies::const_iterator Cookies::begin() const {\n    return cookies_.begin();\n}\n\nCookies::const_iterator Cookies::end() const {\n    return cookies_.end();\n}\n\nCookies::const_iterator Cookies::cbegin() const {\n    return cookies_.cbegin();\n}\n\nCookies::const_iterator Cookies::cend() const {\n    return cookies_.cend();\n}\n\nvoid Cookies::emplace_back(const Cookie& str) {\n    cookies_.emplace_back(str);\n}\n\nbool Cookies::empty() const {\n    return cookies_.empty();\n}\n\nvoid Cookies::push_back(const Cookie& str) {\n    cookies_.push_back(str);\n}\n\nvoid Cookies::pop_back() {\n    cookies_.pop_back();\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/cprtypes.cpp",
    "content": "#include \"cpr/cprtypes.h\"\n#include <algorithm>\n#include <cctype>\n#include <string>\n\n\nnamespace cpr {\nbool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const noexcept {\n    return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), [](unsigned char ac, unsigned char bc) { return std::tolower(ac) < std::tolower(bc); });\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/curl_container.cpp",
    "content": "#include \"cpr/curl_container.h\"\n#include \"cpr/curlholder.h\"\n#include <algorithm>\n#include <initializer_list>\n#include <iterator>\n#include <string>\n\nnamespace cpr {\ntemplate <class T>\nCurlContainer<T>::CurlContainer(const std::initializer_list<T>& containerList) : containerList_(containerList) {}\n\ntemplate <class T>\nvoid CurlContainer<T>::Add(const std::initializer_list<T>& containerList) {\n    std::transform(containerList.begin(), containerList.end(), std::back_inserter(containerList_), [](const T& elem) { return std::move(elem); });\n}\n\ntemplate <class T>\nvoid CurlContainer<T>::Add(const T& element) {\n    containerList_.push_back(std::move(element));\n}\n\ntemplate <>\nconst std::string CurlContainer<Parameter>::GetContent(const CurlHolder& holder) const {\n    std::string content{};\n    for (const Parameter& parameter : containerList_) {\n        if (!content.empty()) {\n            content += \"&\";\n        }\n\n        const std::string escapedKey = encode ? std::string{holder.urlEncode(parameter.key)} : parameter.key;\n        if (parameter.value.empty()) {\n            content += escapedKey;\n        } else {\n            const std::string escapedValue = encode ? std::string{holder.urlEncode(parameter.value)} : parameter.value;\n            content += escapedKey + \"=\";\n            content += escapedValue;\n        }\n    }\n\n    return content;\n}\n\ntemplate <>\nconst std::string CurlContainer<Parameter>::GetContent() const {\n    std::string content{};\n    for (const Parameter& parameter : containerList_) {\n        if (!content.empty()) {\n            content += \"&\";\n        }\n\n        if (parameter.value.empty()) {\n            content += parameter.key;\n        } else {\n            content += parameter.key + \"=\";\n            content += parameter.value;\n        }\n    }\n\n    return content;\n}\n\ntemplate <>\nconst std::string CurlContainer<Pair>::GetContent(const CurlHolder& holder) const {\n    std::string content{};\n    for (const cpr::Pair& element : containerList_) {\n        if (!content.empty()) {\n            content += \"&\";\n        }\n        const std::string escaped = encode ? std::string{holder.urlEncode(element.value)} : element.value;\n        content += element.key + \"=\" + escaped;\n    }\n\n    return content;\n}\n\ntemplate <>\nconst std::string CurlContainer<Pair>::GetContent() const {\n    std::string content{};\n    for (const cpr::Pair& element : containerList_) {\n        if (!content.empty()) {\n            content += \"&\";\n        }\n        content += element.key + \"=\" + element.value;\n    }\n\n    return content;\n}\n\ntemplate class CurlContainer<Pair>;\ntemplate class CurlContainer<Parameter>;\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/curlholder.cpp",
    "content": "#include \"cpr/curlholder.h\"\n#include \"cpr/secure_string.h\"\n#include <cassert>\n#include <curl/curl.h>\n#include <curl/easy.h>\n#include <string_view>\n\nnamespace cpr {\nCurlHolder::CurlHolder() {\n    /**\n     * Allow multithreaded access to CPR by locking curl_easy_init().\n     * curl_easy_init() is not thread safe.\n     * References:\n     * https://curl.haxx.se/libcurl/c/curl_easy_init.html\n     * https://curl.haxx.se/libcurl/c/threadsafe.html\n     **/\n    curl_easy_init_mutex_().lock();\n    // NOLINTNEXTLINE (cppcoreguidelines-prefer-member-initializer) since we need it to happen inside the lock\n    handle = curl_easy_init();\n    curl_easy_init_mutex_().unlock();\n\n    assert(handle);\n}\n\nCurlHolder::CurlHolder(CurlHolder&& old) noexcept : handle(old.handle), chunk(old.chunk), resolveCurlList(old.resolveCurlList), multipart(old.multipart), error(old.error) {\n    // Avoid double free\n    old.handle = nullptr;\n    old.chunk = nullptr;\n    old.resolveCurlList = nullptr;\n    old.multipart = nullptr;\n}\n\nCurlHolder::~CurlHolder() {\n    curl_slist_free_all(chunk);\n    curl_slist_free_all(resolveCurlList);\n    curl_mime_free(multipart);\n    curl_easy_cleanup(handle);\n}\n\nCurlHolder& CurlHolder::operator=(CurlHolder&& old) noexcept {\n    // Free the previous stuff\n    curl_slist_free_all(chunk);\n    curl_slist_free_all(resolveCurlList);\n    curl_mime_free(multipart);\n    curl_easy_cleanup(handle);\n\n    // Move\n    handle = old.handle;\n    chunk = old.chunk;\n    resolveCurlList = old.resolveCurlList;\n    multipart = old.multipart;\n    error = old.error;\n\n    // Avoid double free\n    old.handle = nullptr;\n    old.chunk = nullptr;\n    old.resolveCurlList = nullptr;\n    old.multipart = nullptr;\n    return *this;\n}\n\nutil::SecureString CurlHolder::urlEncode(std::string_view s) const {\n    assert(handle);\n    char* output = curl_easy_escape(handle, s.data(), static_cast<int>(s.length()));\n    if (output) {\n        util::SecureString result = output;\n        curl_free(output);\n        return result;\n    }\n    return \"\";\n}\n\nutil::SecureString CurlHolder::urlDecode(std::string_view s) const {\n    assert(handle);\n    char* output = curl_easy_unescape(handle, s.data(), static_cast<int>(s.length()), nullptr);\n    if (output) {\n        util::SecureString result = output;\n        curl_free(output);\n        return result;\n    }\n    return \"\";\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/curlmultiholder.cpp",
    "content": "#include \"cpr/curlmultiholder.h\"\n#include <cassert>\n#include <curl/multi.h>\n\nnamespace cpr {\n\nCurlMultiHolder::CurlMultiHolder() : handle{curl_multi_init()} {\n    assert(handle);\n}\n\nCurlMultiHolder::~CurlMultiHolder() {\n    curl_multi_cleanup(handle);\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/error.cpp",
    "content": "#include \"cpr/error.h\"\n#include <cstdint>\n#include <curl/curl.h>\n#include <curl/curlver.h>\n\nnamespace cpr {\n// NOLINTBEGIN(bugprone-branch-clone) Fine in this case\nErrorCode Error::getErrorCodeForCurlError(std::int32_t curl_code) {\n    switch (curl_code) {\n        case CURLE_OK:\n            return ErrorCode::OK;\n        case CURLE_UNSUPPORTED_PROTOCOL:\n            return ErrorCode::UNSUPPORTED_PROTOCOL;\n        case CURLE_FAILED_INIT:\n            return ErrorCode::FAILED_INIT;\n        case CURLE_URL_MALFORMAT:\n            return ErrorCode::URL_MALFORMAT;\n        case CURLE_NOT_BUILT_IN:\n            return ErrorCode::NOT_BUILT_IN;\n        case CURLE_COULDNT_RESOLVE_PROXY:\n            return ErrorCode::COULDNT_RESOLVE_PROXY;\n        case CURLE_COULDNT_RESOLVE_HOST:\n            return ErrorCode::COULDNT_RESOLVE_HOST;\n        case CURLE_COULDNT_CONNECT:\n            return ErrorCode::COULDNT_CONNECT;\n\n// Name changed in curl >= 7.51.0.\n#if LIBCURL_VERSION_NUM >= 0x073300\n        case CURLE_WEIRD_SERVER_REPLY:\n            return ErrorCode::WEIRD_SERVER_REPLY;\n#else\n        case CURLE_FTP_WEIRD_SERVER_REPLY:\n            return ErrorCode::WEIRD_SERVER_REPLY;\n#endif\n\n        case CURLE_REMOTE_ACCESS_DENIED:\n            return ErrorCode::REMOTE_ACCESS_DENIED;\n        case CURLE_HTTP2:\n            return ErrorCode::HTTP2;\n        case CURLE_QUOTE_ERROR:\n            return ErrorCode::QUOTE_ERROR;\n        case CURLE_HTTP_RETURNED_ERROR:\n            return ErrorCode::HTTP_RETURNED_ERROR;\n        case CURLE_WRITE_ERROR:\n            return ErrorCode::WRITE_ERROR;\n        case CURLE_UPLOAD_FAILED:\n            return ErrorCode::UPLOAD_FAILED;\n        case CURLE_READ_ERROR:\n            return ErrorCode::READ_ERROR;\n        case CURLE_OUT_OF_MEMORY:\n            return ErrorCode::OUT_OF_MEMORY;\n        case CURLE_OPERATION_TIMEDOUT:\n            return ErrorCode::OPERATION_TIMEDOUT;\n        case CURLE_RANGE_ERROR:\n            return ErrorCode::RANGE_ERROR;\n        case CURLE_HTTP_POST_ERROR:\n            return ErrorCode::HTTP_POST_ERROR;\n        case CURLE_SSL_CONNECT_ERROR:\n            return ErrorCode::SSL_CONNECT_ERROR;\n        case CURLE_BAD_DOWNLOAD_RESUME:\n            return ErrorCode::BAD_DOWNLOAD_RESUME;\n        case CURLE_FILE_COULDNT_READ_FILE:\n            return ErrorCode::FILE_COULDNT_READ_FILE;\n        case CURLE_FUNCTION_NOT_FOUND:\n            return ErrorCode::FUNCTION_NOT_FOUND;\n        case CURLE_ABORTED_BY_CALLBACK:\n            return ErrorCode::ABORTED_BY_CALLBACK;\n        case CURLE_BAD_FUNCTION_ARGUMENT:\n            return ErrorCode::BAD_FUNCTION_ARGUMENT;\n        case CURLE_INTERFACE_FAILED:\n            return ErrorCode::INTERFACE_FAILED;\n        case CURLE_TOO_MANY_REDIRECTS:\n            return ErrorCode::TOO_MANY_REDIRECTS;\n        case CURLE_UNKNOWN_OPTION:\n            return ErrorCode::UNKNOWN_OPTION;\n\n// Added in curl 7.78.0.\n#if LIBCURL_VERSION_NUM >= 0x074E00\n        case CURLE_SETOPT_OPTION_SYNTAX:\n            return ErrorCode::SETOPT_OPTION_SYNTAX;\n#endif\n\n        case CURLE_GOT_NOTHING:\n            return ErrorCode::GOT_NOTHING;\n        case CURLE_SSL_ENGINE_NOTFOUND:\n            return ErrorCode::SSL_ENGINE_NOTFOUND;\n        case CURLE_SSL_ENGINE_SETFAILED:\n            return ErrorCode::SSL_ENGINE_SETFAILED;\n        case CURLE_SEND_ERROR:\n            return ErrorCode::SEND_ERROR;\n        case CURLE_RECV_ERROR:\n            return ErrorCode::RECV_ERROR;\n        case CURLE_SSL_CERTPROBLEM:\n            return ErrorCode::SSL_CERTPROBLEM;\n        case CURLE_SSL_CIPHER:\n            return ErrorCode::SSL_CIPHER;\n        case CURLE_PEER_FAILED_VERIFICATION:\n            return ErrorCode::PEER_FAILED_VERIFICATION;\n        case CURLE_BAD_CONTENT_ENCODING:\n            return ErrorCode::BAD_CONTENT_ENCODING;\n        case CURLE_FILESIZE_EXCEEDED:\n            return ErrorCode::FILESIZE_EXCEEDED;\n        case CURLE_USE_SSL_FAILED:\n            return ErrorCode::USE_SSL_FAILED;\n        case CURLE_SEND_FAIL_REWIND:\n            return ErrorCode::SEND_FAIL_REWIND;\n        case CURLE_SSL_ENGINE_INITFAILED:\n            return ErrorCode::SSL_ENGINE_INITFAILED;\n\n// Added in curl 7.13.1.\n#if LIBCURL_VERSION_NUM >= 0x070D01\n        case CURLE_LOGIN_DENIED:\n            return ErrorCode::LOGIN_DENIED;\n#endif\n\n// Added in curl 7.16.0.\n#if LIBCURL_VERSION_NUM >= 0x071000\n        case CURLE_SSL_CACERT_BADFILE:\n            return ErrorCode::SSL_CACERT_BADFILE;\n#endif\n\n// Added in curl 7.16.1.\n#if LIBCURL_VERSION_NUM >= 0x071001\n        case CURLE_SSL_SHUTDOWN_FAILED:\n            return ErrorCode::SSL_SHUTDOWN_FAILED;\n#endif\n\n// Added in curl 7.18.2.\n#if LIBCURL_VERSION_NUM >= 0x071202\n        case CURLE_AGAIN:\n            return ErrorCode::AGAIN;\n#endif\n\n// Added in curl 7.19.0.\n#if LIBCURL_VERSION_NUM >= 0x071300\n        case CURLE_SSL_CRL_BADFILE:\n            return ErrorCode::SSL_CRL_BADFILE;\n        case CURLE_SSL_ISSUER_ERROR:\n            return ErrorCode::SSL_ISSUER_ERROR;\n#endif\n\n// Added in curl 7.21.0.\n#if LIBCURL_VERSION_NUM >= 0x071500\n        case CURLE_CHUNK_FAILED:\n            return ErrorCode::CHUNK_FAILED;\n#endif\n\n// Added in curl 7.30.0.\n#if LIBCURL_VERSION_NUM >= 0x071E00\n        case CURLE_NO_CONNECTION_AVAILABLE:\n            return ErrorCode::NO_CONNECTION_AVAILABLE;\n#endif\n\n// Added in curl 7.39.0.\n#if LIBCURL_VERSION_NUM >= 0x072700\n        case CURLE_SSL_PINNEDPUBKEYNOTMATCH:\n            return ErrorCode::SSL_PINNEDPUBKEYNOTMATCH;\n#endif\n\n// Added in curl 7.41.0.\n#if LIBCURL_VERSION_NUM >= 0x072900\n        case CURLE_SSL_INVALIDCERTSTATUS:\n            return ErrorCode::SSL_INVALIDCERTSTATUS;\n#endif\n\n// Added in curl 7.49.0.\n#if LIBCURL_VERSION_NUM >= 0x073100\n        case CURLE_HTTP2_STREAM:\n            return ErrorCode::HTTP2_STREAM;\n#endif\n        case CURLE_PARTIAL_FILE:\n            return ErrorCode::PARTIAL_FILE;\n\n// Added in curl 7.59.0.\n#if LIBCURL_VERSION_NUM >= 0x073B00\n        case CURLE_RECURSIVE_API_CALL:\n            return ErrorCode::RECURSIVE_API_CALL;\n#endif\n\n// Added in curl 7.66.0.\n#if LIBCURL_VERSION_NUM >= 0x074200\n        case CURLE_AUTH_ERROR:\n            return ErrorCode::AUTH_ERROR;\n#endif\n\n// Added in curl 7.68.0.\n#if LIBCURL_VERSION_NUM >= 0x074400\n        case CURLE_HTTP3:\n            return ErrorCode::HTTP3;\n#endif\n\n// Added in curl 7.69.0.\n#if LIBCURL_VERSION_NUM >= 0x074500\n        case CURLE_QUIC_CONNECT_ERROR:\n            return ErrorCode::QUIC_CONNECT_ERROR;\n#endif\n\n// Added in curl 7.73.0.\n#if LIBCURL_VERSION_NUM >= 0x074900\n        case CURLE_PROXY:\n            return ErrorCode::PROXY;\n#endif\n\n// Added in curl 7.77.0.\n#if LIBCURL_VERSION_NUM >= 0x074D00\n        case CURLE_SSL_CLIENTCERT:\n            return ErrorCode::SSL_CLIENTCERT;\n#endif\n\n// Added in curl 7.84.0.\n#if LIBCURL_VERSION_NUM >= 0x075400\n        case CURLE_UNRECOVERABLE_POLL:\n            return ErrorCode::UNRECOVERABLE_POLL;\n#endif\n\n// Added in curl 7.6.0.\n#if LIBCURL_VERSION_NUM >= 0x080600\n        case CURLE_TOO_LARGE:\n            return ErrorCode::TOO_LARGE;\n#endif\n        default:\n            return ErrorCode::UNKNOWN_ERROR;\n    }\n}\n// NOLINTEND(bugprone-branch-clone)\n} // namespace cpr"
  },
  {
    "path": "cpr/file.cpp",
    "content": "#include \"cpr/file.h\"\n#include <initializer_list>\n#include <string>\n#include <utility>\n\nnamespace cpr {\n\nFiles::Files(const std::initializer_list<std::string>& p_filepaths) : files(p_filepaths.begin(), p_filepaths.end()) {}\n\nFiles::iterator Files::begin() {\n    return files.begin();\n}\n\nFiles::iterator Files::end() {\n    return files.end();\n}\n\nFiles::const_iterator Files::begin() const {\n    return files.begin();\n}\n\nFiles::const_iterator Files::end() const {\n    return files.end();\n}\n\nFiles::const_iterator Files::cbegin() const {\n    return files.cbegin();\n}\n\nFiles::const_iterator Files::cend() const {\n    return files.cend();\n}\n\nvoid Files::emplace_back(const File& file) {\n    files.emplace_back(file);\n}\n\nvoid Files::push_back(const File& file) {\n    files.push_back(file);\n}\n\nvoid Files::pop_back() {\n    files.pop_back();\n}\n\nFiles& Files::operator=(const Files& other) {\n    if (&other != this) {\n        files = other.files;\n    }\n    return *this;\n}\n\nFiles& Files::operator=(Files&& old) noexcept {\n    if (&old != this) {\n        files = std::move(old.files);\n    }\n    return *this;\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/interceptor.cpp",
    "content": "#include \"cpr/interceptor.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/multiperform.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include <cstddef>\n#include <iosfwd>\n#include <stdexcept>\n#include <vector>\n\nnamespace cpr {\n\nResponse Interceptor::proceed(Session& session) {\n    return session.proceed();\n}\n\nResponse Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod) {\n    switch (httpMethod) {\n        case ProceedHttpMethod::DELETE_REQUEST:\n            return session.Delete();\n        case ProceedHttpMethod::GET_REQUEST:\n            return session.Get();\n        case ProceedHttpMethod::HEAD_REQUEST:\n            return session.Head();\n        case ProceedHttpMethod::OPTIONS_REQUEST:\n            return session.Options();\n        case ProceedHttpMethod::PATCH_REQUEST:\n            return session.Patch();\n        case ProceedHttpMethod::POST_REQUEST:\n            return session.Post();\n        case ProceedHttpMethod::PUT_REQUEST:\n            return session.Put();\n        default:\n            throw std::invalid_argument{\"Can't proceed the session with the provided http method!\"};\n    }\n}\n\nResponse Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file) {\n    if (httpMethod == ProceedHttpMethod::DOWNLOAD_FILE_REQUEST) {\n        return session.Download(file);\n    }\n    throw std::invalid_argument{\"std::ofstream argument is only valid for ProceedHttpMethod::DOWNLOAD_FILE!\"};\n}\n\nResponse Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write) {\n    if (httpMethod == ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST) {\n        return session.Download(write);\n    }\n    throw std::invalid_argument{\"WriteCallback argument is only valid for ProceedHttpMethod::DOWNLOAD_CALLBACK!\"};\n}\n\nstd::vector<Response> InterceptorMulti::proceed(MultiPerform& multi) {\n    return multi.proceed();\n}\n\nvoid InterceptorMulti::PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write) {\n    multi.PrepareDownloadSessions(sessions_index, write);\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/multipart.cpp",
    "content": "#include \"cpr/multipart.h\"\n#include <initializer_list>\n#include <vector>\n\nnamespace cpr {\nMultipart::Multipart(const std::initializer_list<Part>& p_parts) : parts{p_parts} {}\nMultipart::Multipart(const std::vector<Part>& p_parts) : parts{p_parts} {}\nMultipart::Multipart(const std::vector<Part>&& p_parts) : parts{p_parts} {}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/multiperform.cpp",
    "content": "#include \"cpr/multiperform.h\"\n\n#include \"cpr/callback.h\"\n#include \"cpr/curlmultiholder.h\"\n#include \"cpr/interceptor.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include <algorithm>\n#include <cassert>\n#include <cstddef>\n#include <curl/curl.h>\n#include <curl/curlver.h>\n#include <curl/multi.h>\n#include <functional>\n#include <iosfwd>\n#include <iostream>\n#include <memory>\n#include <optional>\n#include <stdexcept>\n#include <utility>\n#include <vector>\n\nnamespace cpr {\n\nMultiPerform::MultiPerform() : multicurl_(new CurlMultiHolder()) {\n    current_interceptor_ = interceptors_.end();\n    first_interceptor_ = interceptors_.end();\n}\n\nMultiPerform::MultiPerform(MultiPerform&& old) noexcept {\n    *this = std::move(old);\n}\n\nMultiPerform& MultiPerform::operator=(MultiPerform&& old) noexcept {\n    sessions_ = std::move(old.sessions_);\n    multicurl_ = std::move(old.multicurl_);\n    interceptors_ = std::move(old.interceptors_);\n    current_interceptor_ = interceptors_.end();\n    first_interceptor_ = interceptors_.end();\n    return *this;\n}\n\nMultiPerform::~MultiPerform() {\n    // Unlock all sessions\n    for (const auto& [session, method] : sessions_) {\n        session->isUsedInMultiPerform = false;\n\n        // Remove easy handle from multi handle\n        const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle);\n        if (error_code) {\n            std::cerr << \"curl_multi_remove_handle() failed, code \" << static_cast<int>(error_code) << '\\n';\n        }\n    }\n}\n\nvoid MultiPerform::AddSession(std::shared_ptr<Session>& session, HttpMethod method) {\n    // Check if this multiperform is download only\n    if (((method != HttpMethod::DOWNLOAD_REQUEST && is_download_multi_perform) && method != HttpMethod::UNDEFINED) || (method == HttpMethod::DOWNLOAD_REQUEST && !is_download_multi_perform && !sessions_.empty())) {\n        // Currently it is not possible to mix download and non-download methods, as download needs additional parameters\n        throw std::invalid_argument(\"Failed to add session: Cannot mix download and non-download methods!\");\n    }\n\n    // Set download only if neccessary\n    if (method == HttpMethod::DOWNLOAD_REQUEST) {\n        is_download_multi_perform = true;\n    }\n\n    // Lock session to the multihandle\n    session->isUsedInMultiPerform = true;\n\n    // Add session to sessions_\n    sessions_.emplace_back(session, method);\n}\n\nvoid MultiPerform::RemoveSession(const std::shared_ptr<Session>& session) {\n    if (sessions_.empty()) {\n        throw std::invalid_argument(\"Failed to find session!\");\n    }\n\n    // Unlock session\n    session->isUsedInMultiPerform = false;\n\n    // Remove session from sessions_\n    auto it = std::find_if(sessions_.begin(), sessions_.end(), [&session](const std::pair<std::shared_ptr<Session>, HttpMethod>& pair) { return session->curl_->handle == pair.first->curl_->handle; });\n    if (it == sessions_.end()) {\n        throw std::invalid_argument(\"Failed to find session!\");\n    }\n    sessions_.erase(it);\n\n    // Reset download only if empty\n    if (sessions_.empty()) {\n        is_download_multi_perform = false;\n    }\n}\n\nstd::vector<std::pair<std::shared_ptr<Session>, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() {\n    return sessions_;\n}\n\nconst std::vector<std::pair<std::shared_ptr<Session>, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() const {\n    return sessions_;\n}\n\nvoid MultiPerform::DoMultiPerform() {\n    // Do multi perform until every handle has finished\n    int still_running{0};\n    for (const auto& [session, _] : sessions_) {\n        const CURLMcode error_code = curl_multi_add_handle(multicurl_->handle, session->curl_->handle);\n        if (error_code && error_code != CURLM_ADDED_ALREADY) {\n            std::cerr << \"curl_multi_add_handle() failed, code \" << static_cast<int>(error_code) << '\\n';\n        }\n    }\n    do {\n        CURLMcode error_code = curl_multi_perform(multicurl_->handle, &still_running);\n        if (error_code) {\n            std::cerr << \"curl_multi_perform() failed, code \" << static_cast<int>(error_code) << '\\n';\n            break;\n        }\n\n        if (still_running) {\n            const int timeout_ms{250};\n#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0\n            error_code = curl_multi_poll(multicurl_->handle, nullptr, 0, timeout_ms, nullptr);\n            if (error_code) {\n                std::cerr << \"curl_multi_poll() failed, code \" << static_cast<int>(error_code) << '\\n';\n#else\n            error_code = curl_multi_wait(multicurl_->handle, nullptr, 0, timeout_ms, nullptr);\n            if (error_code) {\n                std::cerr << \"curl_multi_wait() failed, code \" << static_cast<int>(error_code) << '\\n';\n\n#endif\n                break;\n            }\n        }\n    } while (still_running);\n}\n\nstd::vector<Response> MultiPerform::ReadMultiInfo(const std::function<Response(Session&, CURLcode)>& complete_function) {\n    // Get infos and create Response objects\n    std::vector<Response> responses;\n    struct CURLMsg* info{nullptr};\n    do {\n        int msgq = 0;\n\n        // Read info from multihandle\n        info = curl_multi_info_read(multicurl_->handle, &msgq);\n\n        if (info) {\n            // Find current session\n            auto it = std::find_if(sessions_.begin(), sessions_.end(), [&info](const std::pair<std::shared_ptr<Session>, HttpMethod>& pair) { return pair.first->curl_->handle == info->easy_handle; });\n            if (it == sessions_.end()) {\n                std::cerr << \"Failed to find current session!\" << '\\n';\n                break;\n            }\n            const std::shared_ptr<Session> current_session = (*it).first;\n\n            // Add response object\n            // NOLINTNEXTLINE (cppcoreguidelines-pro-type-union-access)\n            responses.push_back(complete_function(*current_session, info->data.result));\n        }\n    } while (info);\n\n    for (const auto& [session, _] : sessions_) {\n        const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle);\n        if (error_code) {\n            std::cerr << \"curl_multi_remove_handle() failed, code \" << static_cast<int>(error_code) << '\\n';\n        }\n    }\n\n    // Sort response objects to match order of added sessions\n    std::vector<Response> sorted_responses;\n    for (const auto& [session, _] : sessions_) {\n        Session& current_session = *session;\n        auto it = std::find_if(responses.begin(), responses.end(), [&current_session](const Response& response) { return current_session.curl_->handle == response.curl_->handle; });\n        const Response current_response = *it; // NOLINT (performance-unnecessary-copy-initialization) False positive\n        // Erase response from original vector to increase future search speed\n        responses.erase(it);\n        sorted_responses.push_back(current_response);\n    }\n    return sorted_responses;\n}\n\nstd::vector<Response> MultiPerform::MakeRequest() {\n    const std::optional<std::vector<Response>> r = intercept();\n    if (r.has_value()) {\n        return r.value();\n    }\n\n    DoMultiPerform();\n    return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.Complete(curl_error); });\n}\n\nstd::vector<Response> MultiPerform::MakeDownloadRequest() {\n    const std::optional<std::vector<Response>> r = intercept();\n    if (r.has_value()) {\n        return r.value();\n    }\n\n    DoMultiPerform();\n    return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.CompleteDownload(curl_error); });\n}\n\nvoid MultiPerform::PrepareSessions() {\n    for (const auto& [session, method] : sessions_) {\n        switch (method) {\n            case HttpMethod::GET_REQUEST:\n                session->PrepareGet();\n                break;\n            case HttpMethod::POST_REQUEST:\n                session->PreparePost();\n                break;\n            case HttpMethod::PUT_REQUEST:\n                session->PreparePut();\n                break;\n            case HttpMethod::DELETE_REQUEST:\n                session->PrepareDelete();\n                break;\n            case HttpMethod::PATCH_REQUEST:\n                session->PreparePatch();\n                break;\n            case HttpMethod::HEAD_REQUEST:\n                session->PrepareHead();\n                break;\n            case HttpMethod::OPTIONS_REQUEST:\n                session->PrepareOptions();\n                break;\n            default:\n                std::cerr << \"PrepareSessions failed: Undefined HttpMethod or download without arguments!\" << '\\n';\n                return;\n        }\n    }\n}\n\nvoid MultiPerform::PrepareDownloadSession(size_t sessions_index, const WriteCallback& write) {\n    const auto& [session, method] = sessions_[sessions_index];\n    switch (method) {\n        case HttpMethod::DOWNLOAD_REQUEST:\n            session->PrepareDownload(write);\n            break;\n        default:\n            std::cerr << \"PrepareSessions failed: Undefined HttpMethod or non download method with arguments!\" << '\\n';\n            return;\n    }\n}\n\nvoid MultiPerform::PrepareDownloadSession(size_t sessions_index, std::ofstream& file) {\n    const auto& [session, method] = sessions_[sessions_index];\n    switch (method) {\n        case HttpMethod::DOWNLOAD_REQUEST:\n            session->PrepareDownload(file);\n            break;\n        default:\n            std::cerr << \"PrepareSessions failed: Undefined HttpMethod or non download method with arguments!\" << '\\n';\n            return;\n    }\n}\n\nvoid MultiPerform::SetHttpMethod(HttpMethod method) {\n    for (auto& [_, session_method] : sessions_) {\n        session_method = method;\n    }\n}\n\nvoid MultiPerform::PrepareGet() {\n    SetHttpMethod(HttpMethod::GET_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PrepareDelete() {\n    SetHttpMethod(HttpMethod::DELETE_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PreparePut() {\n    SetHttpMethod(HttpMethod::PUT_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PreparePatch() {\n    SetHttpMethod(HttpMethod::PATCH_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PrepareHead() {\n    SetHttpMethod(HttpMethod::HEAD_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PrepareOptions() {\n    SetHttpMethod(HttpMethod::OPTIONS_REQUEST);\n    PrepareSessions();\n}\n\nvoid MultiPerform::PreparePost() {\n    SetHttpMethod(HttpMethod::POST_REQUEST);\n    PrepareSessions();\n}\n\nstd::vector<Response> MultiPerform::Get() {\n    PrepareGet();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Delete() {\n    PrepareDelete();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Put() {\n    PreparePut();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Head() {\n    PrepareHead();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Options() {\n    PrepareOptions();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Patch() {\n    PreparePatch();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Post() {\n    PreparePost();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::Perform() {\n    PrepareSessions();\n    return MakeRequest();\n}\n\nstd::vector<Response> MultiPerform::proceed() {\n    // Check if this multiperform mixes download and non download requests\n    if (!sessions_.empty()) {\n        const bool new_is_download_multi_perform = sessions_.front().second == HttpMethod::DOWNLOAD_REQUEST;\n\n        for (const auto& [_, method] : sessions_) {\n            if ((new_is_download_multi_perform && method != HttpMethod::DOWNLOAD_REQUEST) || (!new_is_download_multi_perform && method == HttpMethod::DOWNLOAD_REQUEST)) {\n                throw std::invalid_argument(\"Failed to proceed with session: Cannot mix download and non-download methods!\");\n            }\n        }\n        is_download_multi_perform = new_is_download_multi_perform;\n    }\n\n    PrepareSessions();\n    return MakeRequest();\n}\n\nconst std::optional<std::vector<Response>> MultiPerform::intercept() {\n    if (current_interceptor_ == interceptors_.end()) {\n        current_interceptor_ = first_interceptor_;\n    } else {\n        current_interceptor_++;\n    }\n\n    if (current_interceptor_ != interceptors_.end()) {\n        auto icpt = current_interceptor_;\n        // Nested makeRequest() start at first_interceptor_, thus excluding previous interceptors.\n        first_interceptor_ = current_interceptor_;\n        ++first_interceptor_;\n\n        const std::optional<std::vector<Response>> r = (*current_interceptor_)->intercept(*this);\n\n        first_interceptor_ = icpt;\n\n        return r;\n    }\n    return std::nullopt;\n}\n\nvoid MultiPerform::AddInterceptor(const std::shared_ptr<InterceptorMulti>& pinterceptor) {\n    // Shall only add before first interceptor run\n    assert(current_interceptor_ == interceptors_.end());\n    interceptors_.push_back(pinterceptor);\n    first_interceptor_ = interceptors_.begin();\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/parameters.cpp",
    "content": "// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future\n#include \"cpr/parameters.h\"\n\nnamespace cpr {} // namespace cpr\n"
  },
  {
    "path": "cpr/payload.cpp",
    "content": "// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future\n#include \"cpr/payload.h\"\n\nnamespace cpr {} // namespace cpr\n"
  },
  {
    "path": "cpr/proxies.cpp",
    "content": "#include \"cpr/proxies.h\"\n\n#include <initializer_list>\n#include <map>\n#include <string>\n#include <utility>\n\nnamespace cpr {\n\nProxies::Proxies(const std::initializer_list<std::pair<const std::string, std::string>>& hosts) : hosts_{hosts} {}\nProxies::Proxies(const std::map<std::string, std::string>& hosts) : hosts_{hosts} {}\n\nbool Proxies::has(const std::string& protocol) const {\n    return hosts_.count(protocol) > 0;\n}\n\nconst std::string& Proxies::operator[](const std::string& protocol) {\n    return hosts_[protocol];\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/proxyauth.cpp",
    "content": "#include \"cpr/proxyauth.h\"\n#include \"cpr/secure_string.h\"\n#include <string>\n#include <string_view>\n\nnamespace cpr {\n\nstd::string_view EncodedAuthentication::GetUsername() const {\n    return username;\n}\n\nstd::string_view EncodedAuthentication::GetPassword() const {\n    return password;\n}\n\nconst util::SecureString& EncodedAuthentication::GetUsernameUnderlying() const {\n    return username;\n}\n\nconst util::SecureString& EncodedAuthentication::GetPasswordUnderlying() const {\n    return password;\n}\n\nbool ProxyAuthentication::has(const std::string& protocol) const {\n    return proxyAuth_.count(protocol) > 0;\n}\n\nstd::string_view ProxyAuthentication::GetUsername(const std::string& protocol) {\n    return proxyAuth_[protocol].GetUsername();\n}\n\nstd::string_view ProxyAuthentication::GetPassword(const std::string& protocol) {\n    return proxyAuth_[protocol].GetPassword();\n}\n\nconst util::SecureString& ProxyAuthentication::GetUsernameUnderlying(const std::string& protocol) const {\n    return proxyAuth_.at(protocol).GetUsernameUnderlying();\n}\n\nconst util::SecureString& ProxyAuthentication::GetPasswordUnderlying(const std::string& protocol) const {\n    return proxyAuth_.at(protocol).GetPasswordUnderlying();\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/redirect.cpp",
    "content": "#include \"cpr/redirect.h\"\n#include <cstdint>\n\nnamespace cpr {\nPostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs) {\n    return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));\n}\n\nPostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs) {\n    return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));\n}\n\nPostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs) {\n    return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs));\n}\n\nPostRedirectFlags operator~(PostRedirectFlags flag) {\n    return static_cast<PostRedirectFlags>(~static_cast<uint8_t>(flag));\n}\n\nPostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {\n    lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));\n    const uint8_t tmp = static_cast<uint8_t>(lhs);\n    lhs = static_cast<PostRedirectFlags>(tmp);\n    return lhs;\n}\n\nPostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {\n    lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));\n    return lhs;\n}\n\nPostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {\n    lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs));\n    return lhs;\n}\n\nbool any(PostRedirectFlags flag) {\n    return flag != PostRedirectFlags::NONE;\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/response.cpp",
    "content": "#include \"cpr/response.h\"\n#include <cassert>\n#include <cpr/cert_info.h>\n#include <cpr/cookies.h>\n#include <cpr/cprtypes.h>\n#include <cpr/curlholder.h>\n#include <cpr/error.h>\n#include <cpr/util.h>\n#include <curl/curl.h>\n#include <curl/curlver.h>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\nnamespace cpr {\n\nResponse::Response(std::shared_ptr<CurlHolder> curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies = Cookies{}, Error&& p_error = Error{}) : curl_(std::move(curl)), text(std::move(p_text)), cookies(std::move(p_cookies)), error(std::move(p_error)), raw_header(std::move(p_header_string)) {\n    header = cpr::util::parseHeader(raw_header, &status_line, &reason);\n    assert(curl_);\n    assert(curl_->handle);\n    curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code);\n    curl_easy_getinfo(curl_->handle, CURLINFO_TOTAL_TIME, &elapsed);\n    const char* url_string{nullptr};\n    curl_easy_getinfo(curl_->handle, CURLINFO_EFFECTIVE_URL, &url_string);\n    url = Url(url_string);\n#if LIBCURL_VERSION_NUM >= 0x073700 // 7.55.0\n    curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD_T, &downloaded_bytes);\n    curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD_T, &uploaded_bytes);\n#else\n    double downloaded_bytes_double, uploaded_bytes_double;\n    curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD, &downloaded_bytes_double);\n    curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD, &uploaded_bytes_double);\n    downloaded_bytes = downloaded_bytes_double;\n    uploaded_bytes = uploaded_bytes_double;\n#endif\n    curl_easy_getinfo(curl_->handle, CURLINFO_REDIRECT_COUNT, &redirect_count);\n#if LIBCURL_VERSION_NUM >= 0x071300 // 7.19.0\n    const char* ip_ptr{nullptr};\n    if (curl_easy_getinfo(curl_->handle, CURLINFO_PRIMARY_IP, &ip_ptr) == CURLE_OK && ip_ptr) {\n        primary_ip = ip_ptr;\n    }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x071500 // 7.21.0\n    // Ignored here since libcurl uses a long for this.\n    // NOLINTNEXTLINE(google-runtime-int)\n    long port = 0;\n    if (curl_easy_getinfo(curl_->handle, CURLINFO_PRIMARY_PORT, &port) == CURLE_OK) {\n        primary_port = port;\n    }\n#endif\n}\n\nstd::vector<CertInfo> Response::GetCertInfos() const {\n    assert(curl_);\n    assert(curl_->handle);\n    const curl_certinfo* ci{nullptr};\n    curl_easy_getinfo(curl_->handle, CURLINFO_CERTINFO, &ci);\n\n    std::vector<CertInfo> cert_infos;\n    for (int i = 0; i < ci->num_of_certs; i++) {\n        CertInfo cert_info;\n        // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        for (curl_slist* slist = ci->certinfo[i]; slist; slist = slist->next) {\n            cert_info.emplace_back(std::string{slist->data});\n        }\n        cert_infos.emplace_back(cert_info);\n    }\n    return cert_infos;\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/session.cpp",
    "content": "#include \"cpr/session.h\"\n\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <fstream>\n#include <iostream>\n#include <memory>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include <curl/curl.h>\n#include <curl/curlver.h>\n#include <curl/easy.h>\n#include <curl/system.h>\n\n#include \"cpr/accept_encoding.h\"\n#include \"cpr/async.h\"\n#include \"cpr/auth.h\"\n#include \"cpr/bearer.h\"\n#include \"cpr/body.h\"\n#include \"cpr/body_view.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/connect_timeout.h\"\n#include \"cpr/connection_pool.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/curlholder.h\"\n#include \"cpr/error.h\"\n#include \"cpr/file.h\"\n#include \"cpr/filesystem.h\" // IWYU pragma: keep\n#include \"cpr/http_version.h\"\n#include \"cpr/interceptor.h\"\n#include \"cpr/interface.h\"\n#include \"cpr/limit_rate.h\"\n#include \"cpr/local_port.h\"\n#include \"cpr/local_port_range.h\"\n#include \"cpr/low_speed.h\"\n#include \"cpr/multipart.h\"\n#include \"cpr/parameters.h\"\n#include \"cpr/payload.h\"\n#include \"cpr/proxies.h\"\n#include \"cpr/proxyauth.h\"\n#include \"cpr/range.h\"\n#include \"cpr/redirect.h\"\n#include \"cpr/reserve_size.h\"\n#include \"cpr/resolve.h\"\n#include \"cpr/response.h\"\n#include \"cpr/ssl_options.h\"\n#include \"cpr/timeout.h\"\n#include \"cpr/unix_socket.h\"\n#include \"cpr/user_agent.h\"\n#include \"cpr/util.h\"\n#include \"cpr/verbose.h\"\n\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n#include \"cpr/ssl_ctx.h\"\n#endif\n\n\nnamespace cpr {\n// Ignored here since libcurl reqires a long:\n// NOLINTNEXTLINE(google-runtime-int)\nconstexpr long ON = 1L;\n// Ignored here since libcurl reqires a long:\n// NOLINTNEXTLINE(google-runtime-int)\nconstexpr long OFF = 0L;\n\nCURLcode Session::DoEasyPerform() {\n    if (isUsedInMultiPerform) {\n        std::cerr << \"curl_easy_perform cannot be executed if the CURL handle is used in a MultiPerform.\\n\";\n        return CURLcode::CURLE_FAILED_INIT;\n    }\n    return curl_easy_perform(curl_->handle);\n}\n\nvoid Session::prepareHeader() {\n    curl_slist* chunk = nullptr;\n    for (const std::pair<const std::string, std::string>& item : header_) {\n        std::string header_string = item.first;\n        if (item.second.empty()) {\n            header_string += \";\";\n        } else {\n            header_string += \": \" + item.second;\n        }\n\n        curl_slist* temp = curl_slist_append(chunk, header_string.c_str());\n        if (temp) {\n            chunk = temp;\n        }\n    }\n\n    // Set the chunked transfer encoding in case it does not already exist:\n    if (chunkedTransferEncoding_ && header_.find(\"Transfer-Encoding\") == header_.end()) {\n        curl_slist* temp = curl_slist_append(chunk, \"Transfer-Encoding:chunked\");\n        if (temp) {\n            chunk = temp;\n        }\n    }\n\n    // libcurl would prepare the header \"Expect: 100-continue\" by default when uploading files larger than 1 MB.\n    // Here we would like to disable this feature:\n    curl_slist* temp = curl_slist_append(chunk, \"Expect:\");\n    if (temp) {\n        chunk = temp;\n    }\n\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPHEADER, chunk);\n\n    curl_slist_free_all(curl_->chunk);\n    curl_->chunk = chunk;\n}\n\nvoid Session::prepareProxy() {\n    const std::string protocol = url_.str().substr(0, url_.str().find(':'));\n    if (proxies_.has(protocol)) {\n        curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str());\n        if (proxyAuth_.has(protocol)) {\n            curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERNAME, proxyAuth_.GetUsernameUnderlying(protocol).c_str());\n            curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPasswordUnderlying(protocol).c_str());\n        }\n    }\n}\n\n// Only supported with libcurl >= 7.61.0.\n// As an alternative use SetHeader and add the token manually.\n#if LIBCURL_VERSION_NUM >= 0x073D00\nvoid Session::SetBearer(const Bearer& token) {\n    // Ignore here since this has been defined by libcurl.\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);\n    curl_easy_setopt(curl_->handle, CURLOPT_XOAUTH2_BEARER, token.GetToken());\n}\n#endif\n\nSession::Session() : curl_(new CurlHolder()) {\n    // Set up some sensible defaults\n    const curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW);\n    const std::string version = \"curl/\" + std::string{version_info->version};\n    curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str());\n    SetRedirect(Redirect());\n    curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L);\n    curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data());\n    curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, \"\");\n#ifdef CPR_CURL_NOSIGNAL\n    curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L);\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x071900 // 7.25.0\n    curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L);\n#endif\n    current_interceptor_ = interceptors_.end();\n    first_interceptor_ = interceptors_.end();\n}\n\nResponse Session::makeDownloadRequest() {\n    const std::optional<Response> r = intercept();\n    if (r.has_value()) {\n        return r.value();\n    }\n\n    const CURLcode curl_error = DoEasyPerform();\n\n    return CompleteDownload(curl_error);\n}\n\nvoid Session::prepareCommonShared() {\n    assert(curl_->handle);\n\n    // Set Header:\n    prepareHeader();\n\n    // URL parameter:\n    const std::string parametersContent = parameters_.GetContent(*curl_);\n    if (!parametersContent.empty()) {\n        const Url new_url{url_ + \"?\" + parametersContent};\n        curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str());\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str());\n    }\n\n    // Proxy:\n    prepareProxy();\n\n    // handle NO_PROXY override passed through Proxies object\n    // Example: Proxies{\"no_proxy\": \"\"} will override environment variable definition with an empty list\n    const std::array<std::string, 2> no_proxy{\"no_proxy\", \"NO_PROXY\"};\n    for (const auto& item : no_proxy) { // cppcheck-suppress useStlAlgorithm\n        if (proxies_.has(item)) {       // cppcheck-suppress useStlAlgorithm\n            curl_easy_setopt(curl_->handle, CURLOPT_NOPROXY, proxies_[item].c_str());\n            break;\n        }\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x071506 // 7.21.6\n    if (acceptEncoding_.empty()) {\n        // Enable all supported built-in compressions\n        curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, \"\");\n    } else if (acceptEncoding_.disabled()) {\n        // Disable curl adding the 'Accept-Encoding' header\n        curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, nullptr);\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str());\n    }\n#endif\n\n    curl_->error[0] = '\\0';\n\n    // Clear the response\n    response_string_.clear();\n    if (response_string_reserve_size_ > 0) {\n        response_string_.reserve(response_string_reserve_size_);\n    }\n\n    // Enable so we are able to retrieve certificate information:\n    curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L);\n}\n\nvoid Session::prepareCommon() {\n    assert(curl_->handle);\n\n    // Everything else:\n    prepareCommonShared();\n\n    // Set Content:\n    prepareBodyPayloadOrMultipart();\n\n    if (!cbs_->writecb_.callback && !cbs_->ssecb_.callback) {\n        curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction);\n        curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_);\n    }\n\n    header_string_.clear();\n    if (!cbs_->headercb_.callback) {\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction);\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_);\n    }\n}\n\nvoid Session::prepareCommonDownload() {\n    assert(curl_->handle);\n\n    // Everything else:\n    prepareCommonShared();\n\n    header_string_.clear();\n    if (cbs_->headercb_.callback) {\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction);\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &cbs_->headercb_);\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction);\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_);\n    }\n}\n\nResponse Session::makeRequest() {\n    const std::optional<Response> r = intercept();\n    if (r.has_value()) {\n        return r.value();\n    }\n\n    const CURLcode curl_error = DoEasyPerform();\n\n    return Complete(curl_error);\n}\n\nvoid Session::SetLimitRate(const LimitRate& limit_rate) {\n    curl_easy_setopt(curl_->handle, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate.downrate);\n    curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate);\n}\n\nconst Content& Session::GetContent() const {\n    return content_;\n}\n\nvoid Session::RemoveContent() {\n    // inverse function to prepareBodyPayloadOrMultipart()\n    if (std::holds_alternative<cpr::Payload>(content_) || std::holds_alternative<cpr::Body>(content_)) {\n        // set default values, so curl does not send a body in subsequent requests\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, -1);\n        curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, nullptr);\n    } else if (std::holds_alternative<cpr::Multipart>(content_)) {\n        if (curl_->multipart) {\n            // remove multipart data\n            curl_mime_free(curl_->multipart);\n            curl_->multipart = nullptr;\n        }\n    } else if (std::holds_alternative<cpr::BodyView>(content_)) {\n        // set default values, so curl does not send a body in subsequent requests\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, -1);\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr);\n    }\n    content_ = std::monostate{};\n}\n\nvoid Session::SetReadCallback(const ReadCallback& read) {\n    cbs_->readcb_ = read;\n    curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size);\n    curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size);\n    curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction);\n    curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &cbs_->readcb_);\n    chunkedTransferEncoding_ = read.size == -1;\n}\n\nvoid Session::SetHeaderCallback(const HeaderCallback& header) {\n    curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction);\n    cbs_->headercb_ = header;\n    curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &cbs_->headercb_);\n}\n\nvoid Session::SetWriteCallback(const WriteCallback& write) {\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction);\n    cbs_->writecb_ = write;\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &cbs_->writecb_);\n}\n\nvoid Session::SetServerSentEventCallback(const ServerSentEventCallback& sse) {\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeSSEFunction);\n    cbs_->ssecb_ = sse;\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &cbs_->ssecb_);\n}\n\nvoid Session::SetProgressCallback(const ProgressCallback& progress) {\n    cbs_->progresscb_ = progress;\n    if (isCancellable) {\n        cbs_->cancellationcb_.SetProgressCallback(cbs_->progresscb_);\n        return;\n    }\n#if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0\n    curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<ProgressCallback>);\n    curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cbs_->progresscb_);\n#else\n    curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<ProgressCallback>);\n    curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cbs_->progresscb_);\n#endif\n    curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L);\n}\n\nvoid Session::SetDebugCallback(const DebugCallback& debug) {\n    curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction);\n    cbs_->debugcb_ = debug;\n    curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &cbs_->debugcb_);\n    curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L);\n}\n\nvoid Session::SetUrl(const Url& url) {\n    url_ = url;\n}\n\nvoid Session::SetResolve(const Resolve& resolve) {\n    SetResolves({resolve});\n}\n\nvoid Session::SetResolves(const std::vector<Resolve>& resolves) {\n    curl_slist_free_all(curl_->resolveCurlList);\n    curl_->resolveCurlList = nullptr;\n    for (const Resolve& resolve : resolves) {\n        for (const uint16_t port : resolve.ports) {\n            curl_->resolveCurlList = curl_slist_append(curl_->resolveCurlList, (resolve.host + \":\" + std::to_string(port) + \":\" + resolve.addr).c_str());\n        }\n    }\n    curl_easy_setopt(curl_->handle, CURLOPT_RESOLVE, curl_->resolveCurlList);\n}\n\nvoid Session::SetParameters(const Parameters& parameters) {\n    parameters_ = parameters;\n}\n\nvoid Session::SetParameters(Parameters&& parameters) {\n    parameters_ = std::move(parameters);\n}\n\nvoid Session::SetHeader(const Header& header) {\n    header_ = header;\n}\n\nvoid Session::UpdateHeader(const Header& header) {\n    for (const std::pair<const std::string, std::string>& item : header) {\n        header_[item.first] = item.second;\n    }\n}\n\nHeader& Session::GetHeader() {\n    return header_;\n}\n\nconst Header& Session::GetHeader() const {\n    return header_;\n}\n\nvoid Session::SetTimeout(const Timeout& timeout) {\n    curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds());\n}\n\nvoid Session::SetConnectTimeout(const ConnectTimeout& timeout) {\n    curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds());\n}\n\nvoid Session::SetConnectionPool(const ConnectionPool& pool) {\n    CURL* curl = curl_->handle;\n    pool.SetupHandler(curl);\n}\n\nvoid Session::SetAuth(const Authentication& auth) {\n    // Ignore here since this has been defined by libcurl.\n    switch (auth.GetAuthMode()) {\n        case AuthMode::BASIC:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n        case AuthMode::DIGEST:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n        case AuthMode::NTLM:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n        case AuthMode::NEGOTIATE:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n        case AuthMode::ANY:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n        case AuthMode::ANYSAFE:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);\n            curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());\n            break;\n    }\n}\n\nvoid Session::SetUserAgent(const UserAgent& ua) {\n    curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str());\n}\n\nvoid Session::SetPayload(const Payload& payload) {\n    content_ = payload;\n}\n\nvoid Session::SetPayload(Payload&& payload) {\n    content_ = std::move(payload);\n}\n\nvoid Session::SetProxies(const Proxies& proxies) {\n    proxies_ = proxies;\n}\n\nvoid Session::SetProxies(Proxies&& proxies) {\n    proxies_ = std::move(proxies);\n}\n\nvoid Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) {\n    proxyAuth_ = std::move(proxy_auth);\n}\n\nvoid Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) {\n    proxyAuth_ = proxy_auth;\n}\n\nvoid Session::SetMultipart(const Multipart& multipart) {\n    content_ = multipart;\n}\n\nvoid Session::SetMultipart(Multipart&& multipart) {\n    content_ = std::move(multipart);\n}\n\nvoid Session::SetRedirect(const Redirect& redirect) {\n    curl_easy_setopt(curl_->handle, CURLOPT_FOLLOWLOCATION, redirect.follow ? 1L : 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_MAXREDIRS, redirect.maximum);\n    curl_easy_setopt(curl_->handle, CURLOPT_UNRESTRICTED_AUTH, redirect.cont_send_cred ? 1L : 0L);\n\n    // NOLINTNEXTLINE (google-runtime-int)\n    long mask = 0;\n    if (any(redirect.post_flags & PostRedirectFlags::POST_301)) {\n        mask |= CURL_REDIR_POST_301;\n    }\n    if (any(redirect.post_flags & PostRedirectFlags::POST_302)) {\n        mask |= CURL_REDIR_POST_302;\n    }\n    if (any(redirect.post_flags & PostRedirectFlags::POST_303)) {\n        mask |= CURL_REDIR_POST_303;\n    }\n    curl_easy_setopt(curl_->handle, CURLOPT_POSTREDIR, mask);\n}\n\nvoid Session::SetCookies(const Cookies& cookies) {\n    curl_easy_setopt(curl_->handle, CURLOPT_COOKIELIST, \"ALL\");\n    curl_easy_setopt(curl_->handle, CURLOPT_COOKIE, cookies.GetEncoded(*curl_).c_str());\n}\n\nvoid Session::SetBody(const Body& body) {\n    content_ = body;\n}\n\nvoid Session::SetBody(Body&& body) {\n    content_ = std::move(body);\n}\n\n// cppcheck-suppress passedByValue\nvoid Session::SetBodyView(BodyView body) {\n    static_assert(std::is_trivially_copyable_v<BodyView>, \"BodyView expected to be trivially copyable otherwise will need some std::move across codebase\");\n    content_ = body;\n}\n\nvoid Session::SetLowSpeed(const LowSpeed& low_speed) {\n    curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, static_cast<long>(low_speed.limit));\n    curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, static_cast<long>(low_speed.time.count())); // cppcheck-suppress y2038-unsafe-call\n}\n\nvoid Session::SetVerifySsl(const VerifySsl& verify) {\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF);\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L);\n}\n\nvoid Session::SetUnixSocket(const UnixSocket& unix_socket) {\n    curl_easy_setopt(curl_->handle, CURLOPT_UNIX_SOCKET_PATH, unix_socket.GetUnixSocketString());\n}\n\nvoid Session::SetSslOptions(const SslOptions& options) {\n    if (!options.cert_file.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT, options.cert_file.c_str());\n        if (!options.cert_type.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_SSLCERTTYPE, options.cert_type.c_str());\n        }\n    }\n#if SUPPORT_CURLOPT_SSLCERT_BLOB\n    else if (!options.cert_blob.empty()) {\n        std::string cert_blob(options.cert_blob);\n        curl_blob blob{};\n        // NOLINTNEXTLINE (readability-container-data-pointer)\n        blob.data = &cert_blob[0];\n        blob.len = cert_blob.length();\n        blob.flags = CURL_BLOB_COPY;\n        curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT_BLOB, &blob);\n        if (!options.cert_type.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_SSLCERTTYPE, options.cert_type.c_str());\n        }\n    }\n#endif\n    if (!options.key_file.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY, options.key_file.c_str());\n        if (!options.key_type.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str());\n        }\n        if (!options.key_pass.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str());\n        }\n#if SUPPORT_CURLOPT_SSLKEY_BLOB\n    } else if (!options.key_blob.empty()) {\n        std::string key_blob(options.key_blob);\n        curl_blob blob{};\n        // NOLINTNEXTLINE (readability-container-data-pointer)\n        blob.data = &key_blob[0];\n        blob.len = key_blob.length();\n        blob.flags = CURL_BLOB_COPY;\n        curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY_BLOB, &blob);\n        if (!options.key_type.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str());\n        }\n        if (!options.key_pass.empty()) {\n            curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str());\n        }\n#endif\n    }\n    if (!options.pinned_public_key.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_PINNEDPUBLICKEY, options.pinned_public_key.c_str());\n    }\n#if SUPPORT_ALPN\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_ALPN, options.enable_alpn ? ON : OFF);\n#endif\n#if SUPPORT_NPN\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_NPN, options.enable_npn ? ON : OFF);\n#endif\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, options.verify_peer ? ON : OFF);\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, options.verify_host ? 2L : 0L);\n#if LIBCURL_VERSION_NUM >= 0x072900 // 7.41.0\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYSTATUS, options.verify_status ? ON : OFF);\n#endif\n\n    int maxTlsVersion = options.ssl_version;\n#if SUPPORT_MAX_TLS_VERSION\n    maxTlsVersion |= options.max_version;\n#endif\n\n    curl_easy_setopt(curl_->handle, CURLOPT_SSLVERSION,\n                     // Ignore here since this has been defined by libcurl.\n                     maxTlsVersion);\n\n    // NOLINTNEXTLINE (google-runtime-int)\n    long curlSslOptions = 0;\n#if SUPPORT_SSL_NO_REVOKE\n    sslNoRevoke_ = options.ssl_no_revoke;\n    if (options.ssl_no_revoke) {\n        curlSslOptions |= CURLSSLOPT_NO_REVOKE;\n    }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0\n    // Fix loading certs from Windows cert store when using OpenSSL:\n    curlSslOptions |= CURLSSLOPT_NATIVE_CA;\n#endif\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, curlSslOptions);\n\n    if (!options.ca_info.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_CAINFO, options.ca_info.c_str());\n    }\n#if SUPPORT_CURLOPT_CAINFO_BLOB\n    if (!options.ca_info_blob.empty()) {\n        std::string cainfo_blob(options.ca_info_blob);\n        curl_blob blob{};\n        blob.data = cainfo_blob.data();\n        blob.len = cainfo_blob.length();\n        blob.flags = CURL_BLOB_COPY;\n        curl_easy_setopt(curl_->handle, CURLOPT_CAINFO_BLOB, &blob);\n    }\n#endif\n    if (!options.ca_path.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_CAPATH, options.ca_path.c_str());\n    }\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n#ifdef OPENSSL_BACKEND_USED\n    if (!options.ca_buffer.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function_load_ca_cert_from_buffer);\n        curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_DATA, options.ca_buffer.c_str());\n    }\n#endif\n#endif\n    if (!options.crl_file.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_CRLFILE, options.crl_file.c_str());\n    }\n    if (!options.ciphers.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_SSL_CIPHER_LIST, options.ciphers.c_str());\n    }\n#if SUPPORT_TLSv13_CIPHERS\n    if (!options.tls13_ciphers.empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_TLS13_CIPHERS, options.tls13_ciphers.c_str());\n    }\n#endif\n#if SUPPORT_SESSIONID_CACHE\n    curl_easy_setopt(curl_->handle, CURLOPT_SSL_SESSIONID_CACHE, options.session_id_cache ? ON : OFF);\n#endif\n}\n\nvoid Session::SetVerbose(const Verbose& verbose) {\n    curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF);\n}\n\nvoid Session::SetInterface(const Interface& iface) {\n    if (iface.str().empty()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr);\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str());\n    }\n}\n\nvoid Session::SetLocalPort(const LocalPort& local_port) {\n    curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORT, static_cast<long>(static_cast<uint16_t>(local_port)));\n}\n\nvoid Session::SetLocalPortRange(const LocalPortRange& local_port_range) {\n    curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORTRANGE, static_cast<long>(static_cast<uint16_t>(local_port_range)));\n}\n\nvoid Session::SetHttpVersion(const HttpVersion& version) {\n    switch (version.code) {\n        case HttpVersionCode::VERSION_NONE:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);\n            break;\n\n        case HttpVersionCode::VERSION_1_0:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);\n            break;\n\n        case HttpVersionCode::VERSION_1_1:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);\n            break;\n\n#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0\n        case HttpVersionCode::VERSION_2_0:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);\n            break;\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0\n        case HttpVersionCode::VERSION_2_0_TLS:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);\n            break;\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0\n        case HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);\n            break;\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0\n        case HttpVersionCode::VERSION_3_0:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);\n            break;\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075701 // 7.87.1, but corresponds to 7.88.0 tag\n        case HttpVersionCode::VERSION_3_0_ONLY:\n            curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);\n            break;\n#endif\n\n        default: // Should not happen\n            throw std::invalid_argument(\"Invalid/Unknown HTTP version type.\");\n    }\n}\n\nvoid Session::SetRange(const Range& range) {\n    const std::string range_str = range.str();\n    curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str());\n}\n\nvoid Session::SetMultiRange(const MultiRange& multi_range) {\n    const std::string multi_range_str = multi_range.str();\n    curl_easy_setopt(curl_->handle, CURLOPT_RANGE, multi_range_str.c_str());\n}\n\nvoid Session::SetReserveSize(const ReserveSize& reserve_size) {\n    ResponseStringReserve(reserve_size.size);\n}\n\nvoid Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) {\n    acceptEncoding_ = accept_encoding;\n}\n\nvoid Session::SetAcceptEncoding(AcceptEncoding&& accept_encoding) {\n    acceptEncoding_ = std::move(accept_encoding);\n}\n\ncpr_off_t Session::GetDownloadFileLength() {\n    cpr_off_t downloadFileLength = -1;\n    curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str());\n\n    prepareProxy();\n\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1);\n    if (DoEasyPerform() == CURLE_OK) {\n        // NOLINTNEXTLINE (google-runtime-int)\n        long status_code{};\n        curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code);\n        if (200 == status_code) {\n            curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLength);\n        }\n    }\n    return downloadFileLength;\n}\n\nvoid Session::ResponseStringReserve(size_t size) {\n    response_string_reserve_size_ = size;\n}\n\nResponse Session::Delete() {\n    PrepareDelete();\n    return makeRequest();\n}\n\nResponse Session::Download(const WriteCallback& write) {\n    PrepareDownload(write);\n    return makeDownloadRequest();\n}\n\nResponse Session::Download(std::ofstream& file) {\n    PrepareDownload(file);\n    return makeDownloadRequest();\n}\n\nResponse Session::Get() {\n    PrepareGet();\n    return makeRequest();\n}\n\nResponse Session::Head() {\n    PrepareHead();\n    return makeRequest();\n}\n\nResponse Session::Options() {\n    PrepareOptions();\n    return makeRequest();\n}\n\nResponse Session::Patch() {\n    PreparePatch();\n    return makeRequest();\n}\n\nResponse Session::Post() {\n    PreparePost();\n    return makeRequest();\n}\n\nResponse Session::Put() {\n    PreparePut();\n    return makeRequest();\n}\n\nstd::shared_ptr<Session> Session::GetSharedPtrFromThis() {\n    try {\n        return shared_from_this();\n    } catch (std::bad_weak_ptr&) {\n        throw std::runtime_error(\"Failed to get a shared pointer from this. The reason is probably that the session object is not managed by a shared pointer, which is required to use this functionality.\");\n    }\n}\n\nAsyncResponse Session::GetAsync() {\n    auto shared_this = shared_from_this();\n    return async([shared_this]() { return shared_this->Get(); });\n}\n\nAsyncResponse Session::DeleteAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Delete(); });\n}\n\nAsyncResponse Session::DownloadAsync(const WriteCallback& write) {\n    return async([shared_this = GetSharedPtrFromThis(), write]() { return shared_this->Download(write); });\n}\n\nAsyncResponse Session::DownloadAsync(std::ofstream& file) {\n    return async([shared_this = GetSharedPtrFromThis(), &file]() { return shared_this->Download(file); });\n}\n\nAsyncResponse Session::HeadAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Head(); });\n}\n\nAsyncResponse Session::OptionsAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Options(); });\n}\n\nAsyncResponse Session::PatchAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Patch(); });\n}\n\nAsyncResponse Session::PostAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Post(); });\n}\n\nAsyncResponse Session::PutAsync() {\n    return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Put(); });\n}\n\nstd::shared_ptr<CurlHolder> Session::GetCurlHolder() {\n    return curl_;\n}\n\nstd::string Session::GetFullRequestUrl() {\n    const std::string parametersContent = parameters_.GetContent(*curl_);\n    return url_.str() + (parametersContent.empty() ? \"\" : \"?\") + parametersContent;\n}\n\nvoid Session::PrepareDelete() {\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"DELETE\");\n    prepareCommon();\n}\n\nvoid Session::PrepareGet() {\n    // In case there is a body or payload for this request, we create a custom GET-Request since a\n    // GET-Request with body is based on the HTTP RFC **not** a leagal request.\n    if (hasBodyOrPayload()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n        curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"GET\");\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n        curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);\n        curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1L);\n    }\n    prepareCommon();\n}\n\nvoid Session::PrepareHead() {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1L);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);\n    prepareCommon();\n}\n\nvoid Session::PrepareOptions() {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"OPTIONS\");\n    prepareCommon();\n}\n\nvoid Session::PreparePatch() {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"PATCH\");\n    prepareCommon();\n}\n\nvoid Session::PreparePost() {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n\n    // In case there is no body or payload set it to an empty post:\n    if (hasBodyOrPayload()) {\n        curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);\n    } else {\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, cbs_->readcb_.callback ? nullptr : \"\");\n        curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"POST\");\n    }\n    prepareCommon();\n}\n\nvoid Session::PreparePut() {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    if (!hasBodyOrPayload() && cbs_->readcb_.callback) {\n        /**\n         * Yes, this one has to be CURLOPT_POSTFIELDS even if we are performing a PUT request.\n         * In case we don't set this one, performing a POST-request with PUT won't work.\n         * It in theory this only enforces the usage of the readcallback for POST requests, but works here as well.\n         **/\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr);\n    }\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, \"PUT\");\n    curl_easy_setopt(curl_->handle, CURLOPT_RANGE, nullptr);\n    prepareCommon();\n}\n\nvoid Session::PrepareDownload(std::ofstream& file) {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction);\n    curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);\n\n    prepareCommonDownload();\n}\n\nvoid Session::PrepareDownload(const WriteCallback& write) {\n    curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);\n    curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);\n    curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);\n\n    SetWriteCallback(write);\n\n    prepareCommonDownload();\n}\n\nResponse Session::Complete(CURLcode curl_error) {\n    curl_slist* raw_cookies{nullptr};\n    curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies);\n    Cookies cookies = util::parseCookies(raw_cookies);\n    curl_slist_free_all(raw_cookies);\n\n    std::string errorMsg = curl_->error.data();\n    return Response(curl_, std::move(response_string_), std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg)));\n}\n\nResponse Session::CompleteDownload(CURLcode curl_error) {\n    if (!cbs_->headercb_.callback) {\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr);\n        curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0);\n    }\n\n    curl_slist* raw_cookies{nullptr};\n    curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies);\n    Cookies cookies = util::parseCookies(raw_cookies);\n    curl_slist_free_all(raw_cookies);\n    std::string errorMsg = curl_->error.data();\n\n    return Response(curl_, \"\", std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg)));\n}\n\nvoid Session::AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor) {\n    // Shall only add before first interceptor run\n    assert(current_interceptor_ == interceptors_.end());\n    interceptors_.push_back(pinterceptor);\n    first_interceptor_ = interceptors_.begin();\n}\n\nResponse Session::proceed() {\n    prepareCommon();\n    return makeRequest();\n}\n\nconst std::optional<Response> Session::intercept() {\n    if (current_interceptor_ == interceptors_.end()) {\n        current_interceptor_ = first_interceptor_;\n    } else {\n        ++current_interceptor_;\n    }\n\n    if (current_interceptor_ != interceptors_.end()) {\n        auto icpt = current_interceptor_;\n        // Nested makeRequest() start at first_interceptor_, thus excluding previous interceptors.\n        first_interceptor_ = current_interceptor_;\n        ++first_interceptor_;\n\n        const std::optional<Response> r = (*current_interceptor_)->intercept(*this);\n\n        first_interceptor_ = icpt;\n\n        return r;\n    }\n    return std::nullopt;\n}\n\nvoid Session::prepareBodyPayloadOrMultipart() const {\n    // Either a body, multipart or a payload is allowed. Inverse function to RemoveContent()\n\n    if (std::holds_alternative<cpr::Payload>(content_)) {\n        const std::string payload = std::get<cpr::Payload>(content_).GetContent(*curl_);\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(payload.length()));\n        curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, payload.c_str());\n    } else if (std::holds_alternative<cpr::Body>(content_)) {\n        const std::string& body = std::get<cpr::Body>(content_).str();\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.length()));\n        curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str());\n    } else if (std::holds_alternative<cpr::BodyView>(content_)) {\n        const std::string_view body = std::get<cpr::BodyView>(content_).str();\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.length()));\n        // NOLINTNEXTLINE (bugprone-suspicious-stringview-data-usage)\n        curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, body.data());\n    } else if (std::holds_alternative<cpr::Multipart>(content_)) {\n        // Make sure, we have a empty multipart to start with:\n        if (curl_->multipart) {\n            curl_mime_free(curl_->multipart);\n        }\n        curl_->multipart = curl_mime_init(curl_->handle);\n\n        // Add all multipart pieces:\n        const cpr::Multipart& multipart = std::get<cpr::Multipart>(content_);\n        for (const Part& part : multipart.parts) {\n            if (part.is_file) {\n                for (const File& file : part.files) {\n                    curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart);\n                    if (!part.content_type.empty()) {\n                        curl_mime_type(mimePart, part.content_type.c_str());\n                    }\n\n                    curl_mime_filedata(mimePart, file.filepath.c_str());\n                    curl_mime_name(mimePart, part.name.c_str());\n\n                    if (file.hasOverridenFilename()) {\n                        curl_mime_filename(mimePart, file.overriden_filename.c_str());\n                    } else {\n                        // NOLINTNEXTLINE (misc-include-cleaner)\n                        curl_mime_filename(mimePart, fs::path(file.filepath).filename().string().c_str());\n                    }\n                }\n            } else {\n                curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart);\n                if (!part.content_type.empty()) {\n                    curl_mime_type(mimePart, part.content_type.c_str());\n                }\n                if (part.is_buffer) {\n                    // Do not use formdata, to prevent having to use reinterpreter_cast:\n                    curl_mime_name(mimePart, part.name.c_str());\n                    curl_mime_data(mimePart, part.data, part.datalen);\n                    curl_mime_filename(mimePart, part.value.c_str());\n                } else {\n                    curl_mime_name(mimePart, part.name.c_str());\n                    curl_mime_data(mimePart, part.value.c_str(), CURL_ZERO_TERMINATED);\n                }\n            }\n        }\n\n        curl_easy_setopt(curl_->handle, CURLOPT_MIMEPOST, curl_->multipart);\n    }\n}\n\n[[nodiscard]] bool Session::hasBodyOrPayload() const {\n    return std::holds_alternative<cpr::Body>(content_) || std::holds_alternative<cpr::BodyView>(content_) || std::holds_alternative<cpr::Payload>(content_);\n}\n\n// clang-format off\nvoid Session::SetOption(const Resolve& resolve) { SetResolve(resolve); }\nvoid Session::SetOption(const std::vector<Resolve>& resolves) { SetResolves(resolves); }\nvoid Session::SetOption(const ReadCallback& read) { SetReadCallback(read); }\nvoid Session::SetOption(const HeaderCallback& header) { SetHeaderCallback(header); }\nvoid Session::SetOption(const WriteCallback& write) { SetWriteCallback(write); }\nvoid Session::SetOption(const ProgressCallback& progress) { SetProgressCallback(progress); }\nvoid Session::SetOption(const DebugCallback& debug) { SetDebugCallback(debug); }\nvoid Session::SetOption(const ServerSentEventCallback& sse) { SetServerSentEventCallback(sse); }\nvoid Session::SetOption(const Url& url) { SetUrl(url); }\nvoid Session::SetOption(const Parameters& parameters) { SetParameters(parameters); }\nvoid Session::SetOption(Parameters&& parameters) { SetParameters(std::move(parameters)); }\nvoid Session::SetOption(const Header& header) { SetHeader(header); }\nvoid Session::SetOption(const Timeout& timeout) { SetTimeout(timeout); }\nvoid Session::SetOption(const ConnectTimeout& timeout) { SetConnectTimeout(timeout); }\nvoid Session::SetOption(const Authentication& auth) { SetAuth(auth); }\nvoid Session::SetOption(const LimitRate& limit_rate) { SetLimitRate(limit_rate); }\n// Only supported with libcurl >= 7.61.0.\n// As an alternative use SetHeader and add the token manually.\n#if LIBCURL_VERSION_NUM >= 0x073D00\nvoid Session::SetOption(const Bearer& auth) { SetBearer(auth); }\n#endif\nvoid Session::SetOption(const UserAgent& ua) { SetUserAgent(ua); }\nvoid Session::SetOption(const Payload& payload) { SetPayload(payload); }\nvoid Session::SetOption(Payload&& payload) { SetPayload(std::move(payload)); }\nvoid Session::SetOption(const Proxies& proxies) { SetProxies(proxies); }\nvoid Session::SetOption(Proxies&& proxies) { SetProxies(std::move(proxies)); }\nvoid Session::SetOption(ProxyAuthentication&& proxy_auth) { SetProxyAuth(std::move(proxy_auth)); }\nvoid Session::SetOption(const ProxyAuthentication& proxy_auth) { SetProxyAuth(proxy_auth); }\nvoid Session::SetOption(const Multipart& multipart) { SetMultipart(multipart); }\nvoid Session::SetOption(Multipart&& multipart) { SetMultipart(std::move(multipart)); }\nvoid Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); }\nvoid Session::SetOption(const Cookies& cookies) { SetCookies(cookies); }\nvoid Session::SetOption(const Body& body) { SetBody(body); }\nvoid Session::SetOption(Body&& body) { SetBody(std::move(body)); }\n// cppcheck-suppress passedByValue\nvoid Session::SetOption(BodyView body) { SetBodyView(body); }\nvoid Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); }\nvoid Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); }\nvoid Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); }\nvoid Session::SetOption(const UnixSocket& unix_socket) { SetUnixSocket(unix_socket); }\nvoid Session::SetOption(const SslOptions& options) { SetSslOptions(options); }\nvoid Session::SetOption(const Interface& iface) { SetInterface(iface); }\nvoid Session::SetOption(const LocalPort& local_port) { SetLocalPort(local_port); }\nvoid Session::SetOption(const LocalPortRange& local_port_range) { SetLocalPortRange(local_port_range); }\nvoid Session::SetOption(const HttpVersion& version) { SetHttpVersion(version); }\nvoid Session::SetOption(const Range& range) { SetRange(range); }\nvoid Session::SetOption(const MultiRange& multi_range) { SetMultiRange(multi_range); }\nvoid Session::SetOption(const ReserveSize& reserve_size) { SetReserveSize(reserve_size); }\nvoid Session::SetOption(const AcceptEncoding& accept_encoding) { SetAcceptEncoding(accept_encoding); }\nvoid Session::SetOption(AcceptEncoding&& accept_encoding) { SetAcceptEncoding(std::move(accept_encoding)); }\nvoid Session::SetOption(const ConnectionPool& pool) { SetConnectionPool(pool); }\n// clang-format on\n\nvoid Session::SetCancellationParam(std::shared_ptr<std::atomic_bool> param) {\n    cbs_->cancellationcb_ = CancellationCallback{std::move(param)};\n    isCancellable = true;\n#if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0\n    curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<CancellationCallback>);\n    curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cbs_->cancellationcb_);\n#else\n    curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<CancellationCallback>);\n    curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cbs_->cancellationcb_);\n#endif\n    curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L);\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/sse.cpp",
    "content": "#include \"cpr/sse.h\"\n\n#include <charconv>\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <string_view>\n#include <system_error>\n#include <utility>\n\nnamespace cpr {\n\nbool ServerSentEventParser::parse(std::string_view data, const std::function<bool(ServerSentEvent&&)>& callback) {\n    // Append incoming data to buffer\n    buffer_.append(data);\n\n    // Process complete lines\n    size_t pos = 0;\n    while ((pos = buffer_.find('\\n')) != std::string::npos) {\n        std::string line = buffer_.substr(0, pos);\n        buffer_.erase(0, pos + 1);\n\n        // Remove trailing \\r if present (handles both \\n and \\r\\n)\n        if (!line.empty() && line.back() == '\\r') {\n            line.pop_back();\n        }\n\n        if (!processLine(line, callback)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nvoid ServerSentEventParser::reset() {\n    buffer_.clear();\n    current_event_ = ServerSentEvent();\n}\n\nbool ServerSentEventParser::processLine(const std::string& line, const std::function<bool(ServerSentEvent&&)>& callback) {\n    // Empty line means end of event\n    if (line.empty()) {\n        return dispatchEvent(callback);\n    }\n\n    // Lines starting with ':' are comments, ignore them\n    if (line[0] == ':') {\n        return true;\n    }\n\n    // Find the colon separator\n    const size_t colon_pos = line.find(':');\n\n    std::string field;\n    std::string value;\n\n    if (colon_pos == std::string::npos) {\n        // No colon, entire line is the field name\n        field = line;\n        value = \"\";\n    } else {\n        field = line.substr(0, colon_pos);\n        // Skip the colon and optional leading space\n        size_t value_start = colon_pos + 1;\n        if (value_start < line.size() && line[value_start] == ' ') {\n            value_start++;\n        }\n        value = line.substr(value_start);\n    }\n\n    // Process the field\n    if (field == \"event\") {\n        current_event_.event = value;\n    } else if (field == \"data\") {\n        // Multiple data fields are concatenated with newlines\n        if (!current_event_.data.empty()) {\n            current_event_.data += '\\n';\n        }\n        current_event_.data += value;\n    } else if (field == \"id\") {\n        // Only set id if the value doesn't contain null character\n        if (value.find('\\0') == std::string::npos) {\n            current_event_.id = value;\n        }\n    } else if (field == \"retry\") {\n        // Parse retry value as integer\n        size_t retry_value = 0;\n        const std::string_view sv(value);\n        const char* begin = sv.data();\n        const char* end = begin + sv.size(); // NOLINT (cppcoreguidelines-pro-bounds-pointer-arithmetic) Required here since Windows and Clang/GCC have different std::string_view iterator implementations\n        auto [ptr, ec] = std::from_chars(begin, end, retry_value);\n        if (ec == std::errc()) {\n            current_event_.retry = retry_value;\n        }\n    }\n    // Unknown fields are ignored per spec\n\n    return true;\n}\n\nbool ServerSentEventParser::dispatchEvent(const std::function<bool(ServerSentEvent&&)>& callback) {\n    // Don't dispatch if data is empty\n    if (current_event_.data.empty()) {\n        current_event_ = ServerSentEvent();\n        return true;\n    }\n\n    // Invoke callback with the current event\n    const bool continue_parsing = callback(std::move(current_event_));\n\n    // Reset for next event (but keep event type as \"message\")\n    current_event_ = ServerSentEvent();\n\n    return continue_parsing;\n}\n\nbool ServerSentEventCallback::handleData(std::string_view data) {\n    return parser_.parse(data, [this](ServerSentEvent&& event) { return (*this)(std::move(event)); });\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/ssl_ctx.cpp",
    "content": "\n#include \"cpr/ssl_ctx.h\"\n#include \"cpr/ssl_options.h\"\n#include <cstddef>\n#include <curl/curl.h>\n#include <iostream>\n#include <memory>\n\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n\n#ifdef OPENSSL_BACKEND_USED\n\n#include <openssl/bio.h>\n#include <openssl/err.h>\n#include <openssl/opensslv.h>\n#include <openssl/pem.h>\n#include <openssl/ssl.h>\n#include <openssl/x509.h>\n#include <openssl/x509_vfy.h>\n\n// openssl/types.h was added in later version of openssl (starting from 3.0.0) and is therefore not always available.\n// This is for example the case on Ubuntu 20.04 or Centos 7.\n// We try to include it if available to satisfy clang-tidy.\n// Ref: https://github.com/openssl/openssl/commit/50cd4768c6b89c757645f28519236bb989216f8d\n#if OPENSSL_VERSION_NUMBER >= 0x30000000L\n#include <openssl/types.h>\n#else\n#include <openssl/ossl_typ.h>\n#endif\n\n// openssl/pemerr.h was added in 1.1.1a, but not in BoringSSL\n// Ref https://github.com/libcpr/cpr/issues/333#issuecomment-2425104338\n#if OPENSSL_VERSION_NUMBER >= 0x1010101fL && !defined(OPENSSL_IS_BORINGSSL)\n#include <openssl/pemerr.h>\n#endif\n\nnamespace cpr {\n\n/**\n * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL.\n * If an error is returned from the callback no attempt to establish a connection is made and\n * the perform operation will return the callback's error code.\n *\n * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html\n *          https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html\n */\n\ntemplate <auto fn>\nstruct deleter_from_fn {\n    template <typename T>\n    constexpr void operator()(T* arg) const {\n        fn(arg);\n    }\n};\n\ntemplate <typename T, auto fn>\nusing custom_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;\nusing x509_ptr = custom_unique_ptr<X509, X509_free>;\nusing bio_ptr = custom_unique_ptr<BIO, BIO_free>;\n\nCURLcode sslctx_function_load_ca_cert_from_buffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) {\n    // Check arguments\n    if (raw_cert_buf == nullptr || sslctx == nullptr) {\n        std::cerr << \"Invalid callback arguments!\\n\";\n        return CURLE_ABORTED_BY_CALLBACK;\n    }\n\n    // Create a memory BIO using the data of cert_buf\n    // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen\n    const char* cert_buf = static_cast<char*>(raw_cert_buf);\n    BIO* bio = BIO_new_mem_buf(cert_buf, -1);\n\n    // Get a pointer to the current certificate verification storage\n    X509_STORE* store = SSL_CTX_get_cert_store(static_cast<SSL_CTX*>(sslctx));\n    if (store == nullptr) {\n        std::cerr << \"SSL_CTX_get_cert_store failed!\\n\";\n        ERR_print_errors_fp(stderr);\n        BIO_free(bio);\n        return CURLE_ABORTED_BY_CALLBACK;\n    }\n\n    // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use\n    // PEM_read_bio_X509 can read multiple certificates from the same buffer in a loop.\n    // The buffer should be in PEM format, which is a base64 encoded format\n    // with header and footer lines like\n    //\n    //    CA 1\n    //    ============\n    //    -----BEGIN CERTIFICATE-----\n    //    ... base64 data ...\n    //    -----END CERTIFICATE-----\n    //\n    //    CA 2\n    //    ============\n    //    -----BEGIN CERTIFICATE-----\n    //    ... base64 data ...\n    //    -----END CERTIFICATE-----\n    //\n    size_t certs_loaded = 0;\n    X509* cert = nullptr;\n    while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) != nullptr) {\n        const int status = X509_STORE_add_cert(store, cert);\n        // Fail if any loaded cert is invalid\n        if (status == 0) {\n            std::cerr << \"[CPR] while adding certificate to store\\n\";\n            ERR_print_errors_fp(stderr);\n            BIO_free(bio);\n            return CURLE_ABORTED_BY_CALLBACK;\n        }\n        certs_loaded++;\n        // Free cert so we can load another one\n        X509_free(cert);\n        cert = nullptr;\n    }\n\n    // NOLINTNEXTLINE(google-runtime-int) Ignored here since it is an API return value\n    const unsigned long err = ERR_peek_last_error();\n    if (certs_loaded == 0 && err != 0) {\n        // Check if the error is just EOF or an actual parsing error\n        if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {\n            // This is expected if the buffer was empty or contains no valid\n            // PEM certs\n            std::cerr << \"No PEM certificates found or end of stream\\n\";\n        } else {\n            std::cerr << \"PEM_read_bio_X509 failed after loading \" << certs_loaded << \" certificates\\n\";\n            ERR_print_errors_fp(stderr);\n            BIO_free(bio);\n            return CURLE_ABORTED_BY_CALLBACK;\n        }\n    }\n\n    // Free the entire bio chain\n    BIO_free(bio);\n\n    // The CA certificate was loaded successfully into the verification storage\n    return CURLE_OK;\n}\n\n} // namespace cpr\n\n#endif // OPENSSL_BACKEND_USED\n\n#endif // SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n"
  },
  {
    "path": "cpr/threadpool.cpp",
    "content": "#include \"cpr/threadpool.h\"\n#include <algorithm>\n#include <chrono>\n#include <cstddef>\n#include <memory>\n#include <mutex>\n#include <thread>\n#include <utility>\n\nnamespace cpr {\n\nThreadPool::ThreadPool(size_t min_threads, size_t max_threads, std::chrono::milliseconds max_idle_ms) : min_thread_num(min_threads), max_thread_num(max_threads), max_idle_time(max_idle_ms) {}\n\nThreadPool::~ThreadPool() {\n    Stop();\n}\n\nint ThreadPool::Start(size_t start_threads) {\n    if (status != Status::STOP) {\n        return -1;\n    }\n    status = Status::RUNNING;\n    start_threads = std::clamp(start_threads, min_thread_num, max_thread_num);\n    for (size_t i = 0; i < start_threads; ++i) {\n        CreateThread();\n    }\n    return 0;\n}\n\nint ThreadPool::Stop() {\n    const std::unique_lock status_lock(status_wait_mutex);\n    if (status == Status::STOP) {\n        return -1;\n    }\n\n    status = Status::STOP;\n    status_wait_cond.notify_all();\n    task_cond.notify_all();\n\n    for (auto& i : threads) {\n        if (i.thread->joinable()) {\n            i.thread->join();\n        }\n    }\n\n    threads.clear();\n    cur_thread_num = 0;\n    idle_thread_num = 0;\n    return 0;\n}\n\nint ThreadPool::Pause() {\n    if (status == Status::RUNNING) {\n        status = Status::PAUSE;\n    }\n    return 0;\n}\n\nint ThreadPool::Resume() {\n    const std::unique_lock status_lock(status_wait_mutex);\n    if (status == Status::PAUSE) {\n        status = Status::RUNNING;\n        status_wait_cond.notify_all();\n    }\n    return 0;\n}\n\nvoid ThreadPool::Wait() {\n    while (true) {\n        if (status == Status::STOP || (tasks.empty() && idle_thread_num == cur_thread_num)) {\n            break;\n        }\n        std::this_thread::yield();\n    }\n}\n\nbool ThreadPool::CreateThread() {\n    if (cur_thread_num >= max_thread_num) {\n        return false;\n    }\n    auto thread = std::make_shared<std::thread>([this] {\n        bool initialRun = true;\n        while (status != Status::STOP) {\n            {\n                std::unique_lock status_lock(status_wait_mutex);\n                status_wait_cond.wait(status_lock, [this]() { return status != Status::PAUSE; });\n            }\n\n            Task task;\n            {\n                std::unique_lock<std::mutex> locker(task_mutex);\n                task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() { return status == Status::STOP || !tasks.empty(); });\n                if (status == Status::STOP) {\n                    return;\n                }\n                if (tasks.empty()) {\n                    if (cur_thread_num > min_thread_num) {\n                        DelThread(std::this_thread::get_id());\n                        return;\n                    }\n                    continue;\n                }\n                if (!initialRun) {\n                    --idle_thread_num;\n                }\n                task = std::move(tasks.front());\n                tasks.pop();\n            }\n            if (task) {\n                task();\n                ++idle_thread_num;\n                initialRun = false;\n            }\n        }\n    });\n    AddThread(thread);\n    return true;\n}\n\nvoid ThreadPool::AddThread(const std::shared_ptr<std::thread>& thread) {\n    thread_mutex.lock();\n    ++cur_thread_num;\n    ThreadData data;\n    data.thread = thread;\n    data.id = thread->get_id();\n    data.status = Status::RUNNING;\n    data.start_time = std::chrono::steady_clock::now();\n    data.stop_time = std::chrono::steady_clock::time_point::max();\n    threads.emplace_back(data);\n    thread_mutex.unlock();\n}\n\nvoid ThreadPool::DelThread(std::thread::id id) {\n    const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n\n    thread_mutex.lock();\n    --cur_thread_num;\n    --idle_thread_num;\n    auto iter = threads.begin();\n    while (iter != threads.end()) {\n        if (iter->status == Status::STOP && now > iter->stop_time) {\n            if (iter->thread->joinable()) {\n                iter->thread->join();\n                iter = threads.erase(iter);\n                continue;\n            }\n        } else if (iter->id == id) {\n            iter->status = Status::STOP;\n            iter->stop_time = std::chrono::steady_clock::now();\n        }\n        ++iter;\n    }\n    thread_mutex.unlock();\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/timeout.cpp",
    "content": "#include \"cpr/timeout.h\"\n\n#include <chrono>\n#include <limits>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n\nnamespace cpr {\n\n// No way around since curl uses a long here.\n// NOLINTNEXTLINE(google-runtime-int)\nlong Timeout::Milliseconds() const {\n    static_assert(std::is_same_v<std::chrono::milliseconds, decltype(ms)>, \"Following casting expects milliseconds.\");\n\n    // No way around since curl uses a long here.\n    // NOLINTNEXTLINE(google-runtime-int)\n    if (ms.count() > static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<long>::max())) {\n        throw std::overflow_error(\"cpr::Timeout: timeout value overflow: \" + std::to_string(ms.count()) + \" ms.\");\n    }\n    // No way around since curl uses a long here.\n    // NOLINTNEXTLINE(google-runtime-int)\n    if (ms.count() < static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<long>::min())) {\n        throw std::underflow_error(\"cpr::Timeout: timeout value underflow: \" + std::to_string(ms.count()) + \" ms.\");\n    }\n\n    // No way around since curl uses a long here.\n    // NOLINTNEXTLINE(google-runtime-int)\n    return static_cast<long>(ms.count());\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "cpr/unix_socket.cpp",
    "content": "\n#include \"cpr/unix_socket.h\"\n\nnamespace cpr {\nconst char* UnixSocket::GetUnixSocketString() const noexcept {\n    return unix_socket_.data();\n}\n} // namespace cpr\n"
  },
  {
    "path": "cpr/util.cpp",
    "content": "#include \"cpr/util.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/curlholder.h\"\n#include \"cpr/secure_string.h\"\n#include \"cpr/sse.h\"\n#include <algorithm>\n#include <cctype>\n#include <chrono>\n#include <cstdint>\n#include <ctime>\n#include <curl/curl.h>\n#include <fstream>\n#include <ios>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#ifdef _Win32\n#include <Windows.h>\n#else\n#ifdef __clang__\n#pragma clang diagnostic push\n#if __has_warning(\"-Wreserved-macro-identifier\") // Not all versions of clang support this flag like the one used on Ubuntu 18.04\n#pragma clang diagnostic ignored \"-Wreserved-macro-identifier\"\n#endif\n#pragma clang diagnostic ignored \"-Wunused-macros\"\n#endif\n// https://en.cppreference.com/w/c/string/byte/memset\n// NOLINTNEXTLINE(bugprone-reserved-identifier, cert-dcl37-c, cert-dcl51-cpp, cppcoreguidelines-macro-usage)\n#define __STDC_WANT_LIB_EXT1__ 1\n#ifdef __clang__\n#pragma clang diagnostic pop\n#endif\n#include <cstring>\n#endif\n\nnamespace cpr::util {\n\nenum class CurlHTTPCookieField : uint8_t {\n    Domain = 0,\n    IncludeSubdomains,\n    Path,\n    HttpsOnly,\n    Expires,\n    Name,\n    Value,\n};\n\nCookies parseCookies(curl_slist* raw_cookies) {\n    const int CURL_HTTP_COOKIE_SIZE = static_cast<int>(CurlHTTPCookieField::Value) + 1;\n    Cookies cookies;\n    for (const curl_slist* nc = raw_cookies; nc; nc = nc->next) {\n        std::vector<std::string> tokens = cpr::util::split(nc->data, '\\t');\n        while (tokens.size() < CURL_HTTP_COOKIE_SIZE) {\n            tokens.emplace_back(\"\");\n        }\n        const std::time_t expires = sTimestampToT(tokens.at(static_cast<size_t>(CurlHTTPCookieField::Expires)));\n        cookies.emplace_back(Cookie{\n                tokens.at(static_cast<size_t>(CurlHTTPCookieField::Name)),\n                tokens.at(static_cast<size_t>(CurlHTTPCookieField::Value)),\n                tokens.at(static_cast<size_t>(CurlHTTPCookieField::Domain)),\n                isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::IncludeSubdomains))),\n                tokens.at(static_cast<size_t>(CurlHTTPCookieField::Path)),\n                isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::HttpsOnly))),\n                std::chrono::system_clock::from_time_t(expires),\n        });\n    }\n    return cookies;\n}\n\nHeader parseHeader(const std::string& headers, std::string* status_line, std::string* reason) {\n    Header header;\n    std::istringstream stream(headers);\n    std::string line;\n    while (std::getline(stream, line, '\\n')) {\n        if (line.substr(0, 5) == \"HTTP/\") {\n            // set the status_line if it was given\n            if ((status_line != nullptr) || (reason != nullptr)) {\n                line.resize(std::min<size_t>(line.size(), line.find_last_not_of(\"\\t\\n\\r \") + 1));\n                if (status_line != nullptr) {\n                    *status_line = line;\n                }\n\n                // set the reason if it was given\n                if (reason != nullptr) {\n                    const size_t pos1 = line.find_first_of(\"\\t \");\n                    size_t pos2 = std::string::npos;\n                    if (pos1 != std::string::npos) {\n                        pos2 = line.find_first_of(\"\\t \", pos1 + 1);\n                    }\n                    if (pos2 != std::string::npos) {\n                        line.erase(0, pos2 + 1);\n                        *reason = line;\n                    }\n                }\n            }\n            header.clear();\n        }\n\n        if (!line.empty()) {\n            const size_t found = line.find(':');\n            if (found != std::string::npos) {\n                std::string value = line.substr(found + 1);\n                value.erase(0, value.find_first_not_of(\"\\t \"));\n                value.resize(std::min<size_t>(value.size(), value.find_last_not_of(\"\\t\\n\\r \") + 1));\n                header[line.substr(0, found)] = value;\n            }\n        }\n    }\n\n    return header;\n}\n\nstd::vector<std::string> split(const std::string& to_split, char delimiter) {\n    std::vector<std::string> tokens;\n\n    std::stringstream stream(to_split);\n    std::string item;\n    while (std::getline(stream, item, delimiter)) {\n        tokens.push_back(item);\n    }\n\n    return tokens;\n}\n\nsize_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read) {\n    size *= nitems;\n    return (*read)(ptr, size) ? size : CURL_READFUNC_ABORT;\n}\n\nsize_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header) {\n    size *= nmemb;\n    return (*header)({ptr, size}) ? size : 0;\n}\n\nsize_t writeFunction(char* ptr, size_t size, size_t nmemb, void* data) {\n    size *= nmemb;\n    static_cast<std::string*>(data)->append(ptr, size);\n    return size;\n}\n\nsize_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file) {\n    size *= nmemb;\n    file->write(ptr, static_cast<std::streamsize>(size));\n    return size;\n}\n\nsize_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write) {\n    size *= nmemb;\n    return (*write)({ptr, size}) ? size : 0;\n}\n\nsize_t writeSSEFunction(char* ptr, size_t size, size_t nmemb, ServerSentEventCallback* sse) {\n    size *= nmemb;\n    return sse->handleData({ptr, size}) ? size : 0;\n}\n\nint debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, const DebugCallback* debug) {\n    (*debug)(static_cast<DebugCallback::InfoType>(type), std::string(data, size));\n    return 0;\n}\n\n/**\n * Creates a temporary CurlHolder object and uses it to escape the given string.\n * If you plan to use this methode on a regular basis think about creating a CurlHolder\n * object and calling urlEncode(std::string) on it.\n *\n * Example:\n * CurlHolder holder;\n * std::string input = \"Hello World!\";\n * std::string result = holder.urlEncode(input);\n **/\nutil::SecureString urlEncode(std::string_view s) {\n    const CurlHolder holder; // Create a temporary new holder for URL encoding\n    return holder.urlEncode(s);\n}\n\n/**\n * Creates a temporary CurlHolder object and uses it to unescape the given string.\n * If you plan to use this methode on a regular basis think about creating a CurlHolder\n * object and calling urlDecode(std::string) on it.\n *\n * Example:\n * CurlHolder holder;\n * std::string input = \"Hello%20World%21\";\n * std::string result = holder.urlDecode(input);\n **/\nutil::SecureString urlDecode(std::string_view s) {\n    const CurlHolder holder; // Create a temporary new holder for URL decoding\n    return holder.urlDecode(s);\n}\n\nbool isTrue(const std::string& s) {\n    constexpr std::string_view tmp = \"true\";\n    auto [s_it, tmp_it] = std::mismatch(s.begin(), s.end(), tmp.begin(), tmp.end(), [](auto s_c, auto t_c) { return std::tolower(s_c) == t_c; });\n    return s_it == s.end() && tmp_it == tmp.end();\n}\n\ntime_t sTimestampToT(const std::string& st) {\n    // NOLINTNEXTLINE(google-runtime-int)\n    if (std::is_same_v<time_t, unsigned long>) {\n        return static_cast<time_t>(std::stoul(st));\n    }\n    // NOLINTNEXTLINE(google-runtime-int)\n    if (std::is_same_v<time_t, unsigned long long>) {\n        return static_cast<time_t>(std::stoull(st));\n    }\n    if (std::is_same_v<time_t, int>) {\n        return static_cast<time_t>(std::stoi(st));\n    }\n    // NOLINTNEXTLINE(google-runtime-int)\n    if (std::is_same_v<time_t, long>) {\n        return static_cast<time_t>(std::stol(st));\n    }\n    return static_cast<time_t>(std::stoll(st));\n}\n\n} // namespace cpr::util\n"
  },
  {
    "path": "cpr-config.cmake",
    "content": "# - C++ Requests, Curl for People\n# This module is a libcurl wrapper written in modern C++.\n# It provides an easy, intuitive, and efficient interface to\n# a host of networking methods.\n#\n# Finding this module will define the following variables:\n#  CPR_FOUND - True if the core library has been found\n#  CPR_LIBRARIES - Path to the core library archive\n#  CPR_INCLUDE_DIRS - Path to the include directories. Gives access\n#                     to cpr.h, which must be included in every\n#                     file that uses this interface\n\nfind_path(CPR_INCLUDE_DIR\n          NAMES cpr.h)\n\nfind_library(CPR_LIBRARY\n             NAMES cpr\n             HINTS ${CPR_LIBRARY_ROOT})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(CPR REQUIRED_VARS CPR_LIBRARY CPR_INCLUDE_DIR)\n\nif(CPR_FOUND)\n    set(CPR_LIBRARIES ${CPR_LIBRARY})\n    set(CPR_INCLUDE_DIRS ${CPR_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "include/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\ntarget_include_directories(cpr PUBLIC\n    $<INSTALL_INTERFACE:include>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n    $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/cpr_generated_includes/>)\n\ntarget_sources(cpr PRIVATE\n    # Header files (useful in IDEs)\n    cpr/accept_encoding.h\n    cpr/api.h\n    cpr/async.h\n    cpr/async_wrapper.h\n    cpr/auth.h\n    cpr/bearer.h\n    cpr/body.h\n    cpr/body_view.h\n    cpr/buffer.h\n    cpr/cert_info.h\n    cpr/cookies.h\n    cpr/cpr.h\n    cpr/cprtypes.h\n    cpr/curlholder.h\n    cpr/curlholder.h\n    cpr/error.h\n    cpr/file.h\n    cpr/limit_rate.h\n    cpr/local_port.h\n    cpr/local_port_range.h\n    cpr/multipart.h\n    cpr/parameters.h\n    cpr/payload.h\n    cpr/proxies.h\n    cpr/proxyauth.h\n    cpr/response.h\n    cpr/secure_string.h\n    cpr/session.h\n    cpr/singleton.h\n    cpr/ssl_ctx.h\n    cpr/ssl_options.h\n    cpr/threadpool.h\n    cpr/timeout.h\n    cpr/unix_socket.h\n    cpr/util.h\n    cpr/verbose.h\n    cpr/interface.h\n    cpr/redirect.h\n    cpr/http_version.h\n    cpr/interceptor.h\n    cpr/filesystem.h\n    cpr/curlmultiholder.h\n    cpr/multiperform.h\n    cpr/resolve.h\n    ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h\n)\n\n# Filesystem\nif(CPR_USE_BOOST_FILESYSTEM)\n  find_package(Boost 1.77 REQUIRED COMPONENTS filesystem)\n  if(Boost_FOUND)\n    target_link_libraries(cpr PUBLIC Boost::filesystem)\n  endif()\nelse()\n  try_compile(\n    STD_FS_SUPPORTED\n    \"${CMAKE_CURRENT_BINARY_DIR}/std_fs\"\n    SOURCES \"${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp\"\n    CXX_STANDARD ${CMAKE_CXX_STANDARD}\n    CXX_STANDARD_REQUIRED ON\n    CXX_EXTENSIONS OFF\n  )\n  if(NOT STD_FS_SUPPORTED)\n    try_compile(\n      STDCXXFS_SUPPORTED\n      \"${CMAKE_CURRENT_BINARY_DIR}/stdcxxfs\"\n      SOURCES \"${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp\"\n      LINK_LIBRARIES \"stdc++fs\"\n      CXX_STANDARD ${CMAKE_CXX_STANDARD}\n      CXX_STANDARD_REQUIRED ON\n      CXX_EXTENSIONS OFF\n    )\n    if(STDCXXFS_SUPPORTED)\n      target_link_libraries(cpr PUBLIC stdc++fs)\n    else()\n      try_compile(\n        CXXFS_SUPPORTED\n        \"${CMAKE_CURRENT_BINARY_DIR}/cxxfs\"\n        SOURCES \"${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp\"\n        LINK_LIBRARIES \"c++fs\"\n        CXX_STANDARD ${CMAKE_CXX_STANDARD}\n        CXX_STANDARD_REQUIRED ON\n        CXX_EXTENSIONS OFF\n      )\n      if(CXXFS_SUPPORTED)\n        target_link_libraries(cpr PUBLIC c++fs)\n      else()\n        message(FATAL_ERROR \"Your compiler does not support the `<filesystem>` include or cpr is unable to determine the proper compiler flags for it.\")\n      endif()\n    endif()\n  endif()\nendif()\n\ninstall(DIRECTORY cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})\ninstall(DIRECTORY ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})\n"
  },
  {
    "path": "include/cpr/accept_encoding.h",
    "content": "#ifndef CPR_ACCEPT_ENCODING_H\n#define CPR_ACCEPT_ENCODING_H\n\n#include <cstdint>\n#include <curl/curlver.h>\n#include <initializer_list>\n#include <map>\n#include <string>\n#include <sys/types.h>\n#include <unordered_set>\n\nnamespace cpr {\n\nenum class AcceptEncodingMethods : uint8_t {\n    identity,\n    deflate,\n    zlib,\n    gzip,\n    disabled,\n};\n\n// NOLINTNEXTLINE(cert-err58-cpp)\nstatic const std::map<AcceptEncodingMethods, std::string> AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, \"identity\"}, {AcceptEncodingMethods::deflate, \"deflate\"}, {AcceptEncodingMethods::zlib, \"zlib\"}, {AcceptEncodingMethods::gzip, \"gzip\"}, {AcceptEncodingMethods::disabled, \"disabled\"}};\n\nclass AcceptEncoding {\n  public:\n    AcceptEncoding() = default;\n    AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods);\n    AcceptEncoding(const std::initializer_list<std::string>& methods);\n\n    [[nodiscard]] bool empty() const noexcept;\n    [[nodiscard]] const std::string getString() const;\n    [[nodiscard]] bool disabled() const;\n\n  private:\n    std::unordered_set<std::string> methods_;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/api.h",
    "content": "#ifndef CPR_API_H\n#define CPR_API_H\n\n#include <fstream>\n#include <functional>\n#include <future>\n#include <string>\n#include <utility>\n\n#include \"cpr/async.h\"\n#include \"cpr/async_wrapper.h\"\n#include \"cpr/auth.h\"\n#include \"cpr/bearer.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/filesystem.h\"\n#include \"cpr/multipart.h\"\n#include \"cpr/multiperform.h\"\n#include \"cpr/payload.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n\nnamespace cpr {\n\nusing AsyncResponse = AsyncWrapper<Response>;\n\nnamespace priv {\n\ntemplate <bool processed_header, typename CurrentType>\nvoid set_option_internal(Session& session, CurrentType&& current_option) {\n    session.SetOption(std::forward<CurrentType>(current_option));\n}\n\ntemplate <>\ninline void set_option_internal<true, Header>(Session& session, Header&& current_option) {\n    // Header option was already provided -> Update previous header\n    session.UpdateHeader(std::forward<Header>(current_option));\n}\n\ntemplate <bool processed_header, typename CurrentType, typename... Ts>\nvoid set_option_internal(Session& session, CurrentType&& current_option, Ts&&... ts) {\n    set_option_internal<processed_header, CurrentType>(session, std::forward<CurrentType>(current_option));\n\n    if constexpr (std::is_same_v<CurrentType, Header>) {\n        set_option_internal<true, Ts...>(session, std::forward<Ts>(ts)...);\n    } else {\n        set_option_internal<processed_header, Ts...>(session, std::forward<Ts>(ts)...);\n    }\n}\n\ntemplate <typename... Ts>\nvoid set_option(Session& session, Ts&&... ts) {\n    set_option_internal<false, Ts...>(session, std::forward<Ts>(ts)...);\n}\n\n// Idea: https://stackoverflow.com/a/19060157\ntemplate <typename Tuple, std::size_t... I>\nvoid apply_set_option_internal(Session& session, Tuple&& t, std::index_sequence<I...>) {\n    set_option(session, std::get<I>(std::forward<Tuple>(t))...);\n}\n\n// Idea: https://stackoverflow.com/a/19060157\ntemplate <typename Tuple>\nvoid apply_set_option(Session& session, Tuple&& t) {\n    using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;\n    apply_set_option_internal(session, std::forward<Tuple>(t), Indices());\n}\n\ntemplate <typename T>\nvoid setup_multiperform_internal(MultiPerform& multiperform, T&& t) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    apply_set_option(*session, t);\n    multiperform.AddSession(session);\n}\n\ntemplate <typename T, typename... Ts>\nvoid setup_multiperform_internal(MultiPerform& multiperform, T&& t, Ts&&... ts) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    apply_set_option(*session, t);\n    multiperform.AddSession(session);\n    setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);\n}\n\ntemplate <typename... Ts>\nvoid setup_multiperform(MultiPerform& multiperform, Ts&&... ts) {\n    setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);\n}\n\nusing session_action_t = cpr::Response (cpr::Session::*)();\n\ntemplate <session_action_t SessionAction, typename T>\nvoid setup_multiasync(std::vector<AsyncWrapper<Response, true>>& responses, T&& parameters) {\n    std::shared_ptr<std::atomic_bool> cancellation_state = std::make_shared<std::atomic_bool>(false);\n\n    std::function<Response(T)> execFn{[cancellation_state](T params) {\n        if (cancellation_state->load()) {\n            return Response{};\n        }\n        cpr::Session s{};\n        s.SetCancellationParam(cancellation_state);\n        apply_set_option(s, std::forward<T>(params));\n        return std::invoke(SessionAction, s);\n    }};\n    responses.emplace_back(GlobalThreadPool::GetInstance()->Submit(std::move(execFn), std::forward<T>(parameters)), std::move(cancellation_state));\n}\n\ntemplate <session_action_t SessionAction, typename T, typename... Ts>\nvoid setup_multiasync(std::vector<AsyncWrapper<Response, true>>& responses, T&& head, Ts&&... tail) {\n    setup_multiasync<SessionAction>(responses, std::forward<T>(head));\n    if constexpr (sizeof...(Ts) > 0) {\n        setup_multiasync<SessionAction>(responses, std::forward<Ts>(tail)...);\n    }\n}\n\n} // namespace priv\n\n// Get methods\ntemplate <typename... Ts>\nResponse Get(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Get();\n}\n\n// Get async methods\ntemplate <typename... Ts>\nAsyncResponse GetAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Get callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto GetCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Post methods\ntemplate <typename... Ts>\nResponse Post(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Post();\n}\n\n// Post async methods\ntemplate <typename... Ts>\nAsyncResponse PostAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Post callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto PostCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Put methods\ntemplate <typename... Ts>\nResponse Put(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Put();\n}\n\n// Put async methods\ntemplate <typename... Ts>\nAsyncResponse PutAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Put callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto PutCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Head methods\ntemplate <typename... Ts>\nResponse Head(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Head();\n}\n\n// Head async methods\ntemplate <typename... Ts>\nAsyncResponse HeadAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Head callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto HeadCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Delete methods\ntemplate <typename... Ts>\nResponse Delete(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Delete();\n}\n\n// Delete async methods\ntemplate <typename... Ts>\nAsyncResponse DeleteAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Delete callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto DeleteCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Options methods\ntemplate <typename... Ts>\nResponse Options(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Options();\n}\n\n// Options async methods\ntemplate <typename... Ts>\nAsyncResponse OptionsAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Options callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto OptionsCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Patch methods\ntemplate <typename... Ts>\nResponse Patch(Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Patch();\n}\n\n// Patch async methods\ntemplate <typename... Ts>\nAsyncResponse PatchAsync(Ts... ts) {\n    return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...);\n}\n\n// Patch callback methods\ntemplate <typename Then, typename... Ts>\n// NOLINTNEXTLINE(fuchsia-trailing-return)\nauto PatchCallback(Then then, Ts... ts) {\n    return cpr::async<true>([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);\n}\n\n// Download methods\ntemplate <typename... Ts>\nResponse Download(std::ofstream& file, Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Download(file);\n}\n\n// Download async method\ntemplate <typename... Ts>\nAsyncResponse DownloadAsync(fs::path local_path, Ts... ts) {\n    return AsyncWrapper{std::async(\n            std::launch::async,\n            [](fs::path local_path_, Ts... ts_) {\n                std::ofstream f(local_path_.c_str());\n                return Download(f, std::move(ts_)...);\n            },\n            std::move(local_path), std::move(ts)...)};\n}\n\n// Download with user callback\ntemplate <typename... Ts>\nResponse Download(const WriteCallback& write, Ts&&... ts) {\n    Session session;\n    priv::set_option(session, std::forward<Ts>(ts)...);\n    return session.Download(write);\n}\n\n// Multi requests\ntemplate <typename... Ts>\nstd::vector<Response> MultiGet(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Get();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiDelete(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Delete();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiPut(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Put();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiHead(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Head();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiOptions(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Options();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiPatch(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Patch();\n}\n\ntemplate <typename... Ts>\nstd::vector<Response> MultiPost(Ts&&... ts) {\n    MultiPerform multiperform;\n    priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);\n    return multiperform.Post();\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiGetAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Get>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiDeleteAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Delete>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiHeadAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Head>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiOptionsAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Options>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiPatchAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Patch>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiPostAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Post>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\ntemplate <typename... Ts>\nstd::vector<AsyncWrapper<Response, true>> MultiPutAsync(Ts&&... ts) {\n    std::vector<AsyncWrapper<Response, true>> ret{};\n    priv::setup_multiasync<&cpr::Session::Put>(ret, std::forward<Ts>(ts)...);\n    return ret;\n}\n\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/async.h",
    "content": "#ifndef CPR_ASYNC_H\n#define CPR_ASYNC_H\n\n#include \"async_wrapper.h\"\n#include \"singleton.h\"\n#include \"threadpool.h\"\n\nnamespace cpr {\n\nclass GlobalThreadPool : public ThreadPool {\n    CPR_SINGLETON_DECL(GlobalThreadPool)\n  protected:\n    GlobalThreadPool() = default;\n\n  public:\n    ~GlobalThreadPool() override = default;\n};\n\n/**\n * Return a wrapper for a future, calling future.get() will wait until the task is done and return RetType.\n * async(fn, args...)\n * async(std::bind(&Class::mem_fn, &obj))\n * async(std::mem_fn(&Class::mem_fn, &obj))\n **/\ntemplate <bool isCancellable = false, class Fn, class... Args>\nauto async(Fn&& fn, Args&&... args) {\n    std::future future = GlobalThreadPool::GetInstance()->Submit(std::forward<Fn>(fn), std::forward<Args>(args)...);\n    using async_wrapper_t = AsyncWrapper<decltype(future.get()), isCancellable>;\n    if constexpr (isCancellable) {\n        return async_wrapper_t{std::move(future), std::make_shared<std::atomic_bool>(false)};\n    } else {\n        return async_wrapper_t{std::move(future)};\n    }\n}\n\nclass async {\n  public:\n    static void startup(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {\n        GlobalThreadPool* gtp = GlobalThreadPool::GetInstance();\n        if (gtp->IsStarted()) {\n            return;\n        }\n        gtp->SetMinThreadNum(min_threads);\n        gtp->SetMaxThreadNum(max_threads);\n        gtp->SetMaxIdleTime(max_idle_ms);\n        gtp->Start();\n    }\n\n    static void cleanup() {\n        GlobalThreadPool::ExitInstance();\n    }\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/async_wrapper.h",
    "content": "#ifndef CPR_ASYNC_WRAPPER_H\n#define CPR_ASYNC_WRAPPER_H\n\n#include <atomic>\n#include <future>\n#include <memory>\n\nnamespace cpr {\nenum class [[nodiscard]] CancellationResult : uint8_t { failure, success, invalid_operation };\n\n/**\n * A class template intended to wrap results of async operations (instances of std::future<T>)\n * and also provide extended capablilities relaed to these requests, for example cancellation.\n *\n * The RAII semantics are the same as std::future<T> - moveable, not copyable.\n */\ntemplate <typename T, bool isCancellable = false>\nclass AsyncWrapper;\n\ntemplate <typename T>\nclass AsyncWrapper<T, false> {\n  private:\n    friend class AsyncWrapper<T, true>;\n    std::future<T> future;\n\n    void throw_if_invalid(const char* error) const {\n        if (!future.valid()) {\n            throw std::logic_error{error};\n        }\n    }\n\n  public:\n    // Constructors\n    AsyncWrapper() = default;\n    explicit AsyncWrapper(std::future<T>&& f) : future{std::move(f)} {}\n\n    // Copy Semantics\n    AsyncWrapper(const AsyncWrapper&) = delete;\n    AsyncWrapper& operator=(const AsyncWrapper&) = delete;\n\n    // Move Semantics\n    AsyncWrapper(AsyncWrapper&&) noexcept = default;\n    AsyncWrapper& operator=(AsyncWrapper&&) noexcept = default;\n\n    // Destructor\n    ~AsyncWrapper() = default;\n\n    // These methods replicate the behaviour of std::future<T>\n    [[nodiscard]] T get() {\n        throw_if_invalid(\"Calling AsyncWrapper::get when the associated future instance is invalid!\");\n        return future.get();\n    }\n\n    [[nodiscard]] bool valid() const noexcept {\n        return future.valid();\n    }\n\n    void wait() const {\n        throw_if_invalid(\"Calling AsyncWrapper::wait when the associated future is invalid!\");\n        future.wait();\n    }\n\n    template <class Rep, class Period>\n    std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {\n        throw_if_invalid(\"Calling AsyncWrapper::wait_for when the associated future is invalid!\");\n        return future.wait_for(timeout_duration);\n    }\n\n    template <class Clock, class Duration>\n    std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {\n        throw_if_invalid(\"Calling AsyncWrapper::wait_until when the associated future is invalid!\");\n        return future.wait_until(timeout_time);\n    }\n\n    std::shared_future<T> share() noexcept {\n        return future.share();\n    }\n};\n\ntemplate <typename T>\nclass AsyncWrapper<T, true> : public AsyncWrapper<T, false> {\n  private:\n    using base = AsyncWrapper<T, false>;\n    std::shared_ptr<std::atomic_bool> is_cancelled;\n\n    void throw_if_cancelled(const char* error) const {\n        if (is_cancelled->load()) {\n            throw std::logic_error{error};\n        }\n    }\n\n  public:\n    // Constructors\n    AsyncWrapper(std::future<T>&& f, std::shared_ptr<std::atomic_bool>&& cancelledState) : base{std::move(f)}, is_cancelled{std::move(cancelledState)} {}\n\n    // Copy Semantics\n    AsyncWrapper(const AsyncWrapper&) = delete;\n    AsyncWrapper& operator=(const AsyncWrapper&) = delete;\n\n    // Move Semantics\n    AsyncWrapper(AsyncWrapper&&) noexcept = default;\n    AsyncWrapper& operator=(AsyncWrapper&&) noexcept = default;\n\n    // Destructor\n    ~AsyncWrapper() {\n        if (is_cancelled) {\n            is_cancelled->store(true);\n        }\n    }\n\n    [[nodiscard]] T get() {\n        throw_if_cancelled(\"Calling AsyncWrapper::get on a cancelled request!\");\n        return base::get();\n    }\n\n    [[nodiscard]] bool valid() const noexcept {\n        return !is_cancelled->load() && base::future.valid();\n    }\n\n    void wait() const {\n        throw_if_cancelled(\"Calling AsyncWrapper::wait when the associated future is invalid or cancelled!\");\n        base::wait();\n    }\n\n    template <class Rep, class Period>\n    std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {\n        throw_if_cancelled(\"Calling AsyncWrapper::wait_for when the associated future is cancelled!\");\n        return base::wait_for(timeout_duration);\n    }\n\n    template <class Clock, class Duration>\n    std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {\n        throw_if_cancelled(\"Calling AsyncWrapper::wait_until when the associated future is cancelled!\");\n        return base::wait_until(timeout_time);\n    }\n\n    // Cancellation-related methods\n    CancellationResult Cancel() {\n        if (!base::future.valid() || is_cancelled->load()) {\n            return CancellationResult::invalid_operation;\n        }\n        is_cancelled->store(true);\n        return CancellationResult::success;\n    }\n\n    [[nodiscard]] bool IsCancelled() const {\n        return is_cancelled->load();\n    }\n};\n\n// Deduction guides\ntemplate <typename T>\nAsyncWrapper(std::future<T>&&) -> AsyncWrapper<T, false>;\n\ntemplate <typename T>\nAsyncWrapper(std::future<T>&&, std::shared_ptr<std::atomic_bool>&&) -> AsyncWrapper<T, true>;\n\n} // namespace cpr\n\n\n#endif\n"
  },
  {
    "path": "include/cpr/auth.h",
    "content": "#ifndef CPR_AUTH_H\n#define CPR_AUTH_H\n\n#include <cstdint>\n#include <string>\n#include <string_view>\n\n#include \"cpr/util.h\"\n\nnamespace cpr {\n\nenum class AuthMode : uint8_t { BASIC, DIGEST, NTLM, NEGOTIATE, ANY, ANYSAFE };\n\nclass Authentication {\n  public:\n    Authentication(std::string_view username, std::string_view password, AuthMode auth_mode);\n\n    [[nodiscard]] const char* GetAuthString() const noexcept;\n    [[nodiscard]] AuthMode GetAuthMode() const noexcept;\n\n  private:\n    util::SecureString auth_string_;\n    AuthMode auth_mode_;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/bearer.h",
    "content": "#ifndef CPR_BEARER_H\n#define CPR_BEARER_H\n\n#include <curl/curlver.h>\n#include <string>\n\n#include <utility>\n\n#include \"cpr/util.h\"\n\nnamespace cpr {\n\n// Only supported with libcurl >= 7.61.0.\n// As an alternative use SetHeader and add the token manually.\n#if LIBCURL_VERSION_NUM >= 0x073D00\nclass Bearer {\n  public:\n    Bearer(std::string_view token) : token_string_{token} {}\n    Bearer(const Bearer& other) = default;\n    Bearer(Bearer&& old) noexcept = default;\n    virtual ~Bearer() noexcept = default;\n\n    Bearer& operator=(Bearer&& old) noexcept = default;\n    Bearer& operator=(const Bearer& other) = default;\n\n    [[nodiscard]] virtual const char* GetToken() const noexcept {\n        return token_string_.c_str();\n    }\n\n  protected:\n    util::SecureString token_string_;\n};\n#endif\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/body.h",
    "content": "#ifndef CPR_BODY_H\n#define CPR_BODY_H\n\n#include <exception>\n#include <fstream>\n#include <initializer_list>\n#include <string>\n\n#include \"cpr/buffer.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/file.h\"\n\nnamespace cpr {\n\nclass Body : public StringHolder<Body> {\n  public:\n    Body() = default;\n    Body(std::string body) : StringHolder<Body>(std::move(body)) {}\n    Body(std::string_view body) : StringHolder<Body>(body) {}\n    Body(const char* body) : StringHolder<Body>(body) {}\n    Body(const char* str, size_t len) : StringHolder<Body>(str, len) {}\n    Body(const std::initializer_list<std::string> args) : StringHolder<Body>(args) {}\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    Body(const Buffer& buffer) : StringHolder<Body>(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}\n    Body(const File& file) {\n        std::ifstream is(file.filepath, std::ifstream::binary);\n        if (!is) {\n            throw std::invalid_argument(\"Can't open the file for HTTP request body!\");\n        }\n\n        is.seekg(0, std::ios::end);\n        const std::streampos length = is.tellg();\n        is.seekg(0, std::ios::beg);\n        std::string buffer;\n        buffer.resize(static_cast<size_t>(length));\n        is.read(buffer.data(), length);\n        str_ = std::move(buffer);\n    }\n    Body(const Body& other) = default;\n    Body(Body&& old) noexcept = default;\n    ~Body() override = default;\n\n    Body& operator=(Body&& old) noexcept = default;\n    Body& operator=(const Body& other) = default;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/body_view.h",
    "content": "#ifndef CPR_BODY_VIEW_H\n#define CPR_BODY_VIEW_H\n\n#include <string_view>\n\n#include \"cpr/buffer.h\"\n\nnamespace cpr {\n\nclass BodyView final {\n  public:\n    BodyView() = default;\n    BodyView(std::string_view body) : m_body(body) {}\n    BodyView(const char* body) : m_body(body) {}\n    BodyView(const char* str, size_t len) : m_body(str, len) {}\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    BodyView(const Buffer& buffer) : m_body(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}\n\n    BodyView(const BodyView& other) = default;\n    BodyView(BodyView&& old) noexcept = default;\n    ~BodyView() = default;\n\n    BodyView& operator=(BodyView&& old) noexcept = default;\n    BodyView& operator=(const BodyView& other) = default;\n\n    [[nodiscard]] std::string_view str() const {\n        return m_body;\n    }\n\n  private:\n    std::string_view m_body;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/buffer.h",
    "content": "#ifndef CPR_BUFFER_H\n#define CPR_BUFFER_H\n\n#include <string>\n\n#include \"cpr/filesystem.h\"\n\nnamespace cpr {\n\nstruct Buffer {\n    using data_t = const char*;\n\n    template <typename Iterator>\n    Buffer(Iterator begin, Iterator end, fs::path&& p_filename)\n            // Ignored here since libcurl requires a long.\n            // There is also no way around the reinterpret_cast.\n            // NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast)\n            : data{reinterpret_cast<data_t>(&(*begin))}, datalen{static_cast<size_t>(std::distance(begin, end))}, filename(std::move(p_filename)) {\n        is_random_access_iterator(begin, end);\n        static_assert(sizeof(*begin) == 1, \"Only byte buffers can be used\");\n    }\n\n    template <typename Iterator>\n    static std::enable_if_t<std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>> is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {}\n\n    data_t data;\n    size_t datalen;\n    const fs::path filename;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/callback.h",
    "content": "#ifndef CPR_CALLBACK_H\n#define CPR_CALLBACK_H\n\n#include \"cprtypes.h\"\n\n#include <atomic>\n#include <cstdint>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <utility>\n\nnamespace cpr {\n\nclass ReadCallback {\n  public:\n    ReadCallback() = default;\n    ReadCallback(std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {}\n    ReadCallback(cpr_off_t p_size, std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{p_size}, callback{std::move(p_callback)} {}\n    bool operator()(char* buffer, size_t& buffer_size) const {\n        if (!callback) {\n            return true;\n        }\n        return callback(buffer, buffer_size, userdata);\n    }\n\n    intptr_t userdata{};\n    cpr_off_t size{};\n    std::function<bool(char* buffer, size_t& size, intptr_t userdata)> callback;\n};\n\nclass HeaderCallback {\n  public:\n    HeaderCallback() = default;\n    HeaderCallback(std::function<bool(std::string_view header, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}\n    bool operator()(std::string_view header) const {\n        if (!callback) {\n            return true;\n        }\n        return callback(header, userdata);\n    }\n\n    intptr_t userdata{};\n    std::function<bool(std::string_view header, intptr_t userdata)> callback;\n};\n\nclass WriteCallback {\n  public:\n    WriteCallback() = default;\n    WriteCallback(std::function<bool(std::string_view data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}\n    bool operator()(std::string_view data) const {\n        if (!callback) {\n            return true;\n        }\n        return callback(data, userdata);\n    }\n\n    intptr_t userdata{};\n    std::function<bool(std::string_view data, intptr_t userdata)> callback;\n};\n\nclass ProgressCallback {\n  public:\n    ProgressCallback() = default;\n    ProgressCallback(std::function<bool(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}\n    bool operator()(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow) const {\n        if (!callback) {\n            return true;\n        }\n        return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata);\n    }\n\n    intptr_t userdata{};\n    std::function<bool(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow, intptr_t userdata)> callback;\n};\n\nclass DebugCallback {\n  public:\n    enum class InfoType : uint8_t {\n        TEXT = 0,\n        HEADER_IN = 1,\n        HEADER_OUT = 2,\n        DATA_IN = 3,\n        DATA_OUT = 4,\n        SSL_DATA_IN = 5,\n        SSL_DATA_OUT = 6,\n    };\n    DebugCallback() = default;\n    DebugCallback(std::function<void(InfoType type, std::string_view data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}\n    void operator()(InfoType type, std::string_view data) const {\n        if (!callback) {\n            return;\n        }\n        callback(type, data, userdata);\n    }\n\n    intptr_t userdata{};\n    std::function<void(InfoType type, std::string_view data, intptr_t userdata)> callback;\n};\n\n/**\n * Functor class for progress functions that will be used in cancellable requests.\n */\nclass CancellationCallback {\n  public:\n    CancellationCallback() = default;\n    explicit CancellationCallback(std::shared_ptr<std::atomic_bool>&& cs) : cancellation_state{std::move(cs)} {}\n\n    CancellationCallback(std::shared_ptr<std::atomic_bool>&& cs, ProgressCallback& u_cb) : cancellation_state{std::move(cs)}, user_cb{std::reference_wrapper{u_cb}} {}\n\n    bool operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const;\n\n    void SetProgressCallback(ProgressCallback& u_cb);\n\n  private:\n    std::shared_ptr<std::atomic_bool> cancellation_state{nullptr};\n    std::optional<std::reference_wrapper<ProgressCallback>> user_cb;\n};\n\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/cert_info.h",
    "content": "#ifndef CPR_CERT_INFO_H\n#define CPR_CERT_INFO_H\n\n#include <initializer_list>\n#include <string>\n#include <vector>\n\nnamespace cpr {\n\nclass CertInfo {\n  private:\n    std::vector<std::string> cert_info_;\n\n  public:\n    CertInfo() = default;\n    CertInfo(const CertInfo& other) = default;\n    CertInfo(CertInfo&& old) = default;\n    CertInfo(const std::initializer_list<std::string>& entry) : cert_info_{entry} {}\n    ~CertInfo() noexcept = default;\n\n    using iterator = std::vector<std::string>::iterator;\n    using const_iterator = std::vector<std::string>::const_iterator;\n\n    std::string& operator[](const size_t& pos);\n    iterator begin();\n    iterator end();\n    [[nodiscard]] const_iterator begin() const;\n    [[nodiscard]] const_iterator end() const;\n    [[nodiscard]] const_iterator cbegin() const;\n    [[nodiscard]] const_iterator cend() const;\n    void emplace_back(const std::string& str);\n    void push_back(const std::string& str);\n    void pop_back();\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/connect_timeout.h",
    "content": "#ifndef CPR_CONNECT_TIMEOUT_H\n#define CPR_CONNECT_TIMEOUT_H\n\n#include \"cpr/timeout.h\"\n\nnamespace cpr {\n\nclass ConnectTimeout : public Timeout {\n  public:\n    ConnectTimeout(const std::chrono::milliseconds& duration) : Timeout{duration} {}\n    ConnectTimeout(const std::int32_t& milliseconds) : Timeout{milliseconds} {}\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/connection_pool.h",
    "content": "#ifndef CPR_CONNECTION_POOL_H\n#define CPR_CONNECTION_POOL_H\n\n#include <curl/curl.h>\n#include <memory>\n#include <mutex>\n\nnamespace cpr {\n/**\n * cpr connection pool implementation for sharing connections between HTTP requests.\n *\n * The ConnectionPool enables connection reuse across multiple HTTP requests to the same host,\n * which can significantly improve performance by avoiding the overhead of establishing new\n * connections for each request. It uses libcurl's CURLSH (share) interface to manage\n * connection sharing in a thread-safe manner.\n *\n * Example:\n * ```cpp\n * // Create a connection pool\n * cpr::ConnectionPool pool;\n *\n * // Use the pool with requests to reuse connections\n * cpr::Response r1 = cpr::Get(cpr::Url{\"http://example.com/api/data\"}, pool);\n * cpr::Response r2 = cpr::Get(cpr::Url{\"http://example.com/api/more\"}, pool);\n *\n * // Or with async requests\n * auto future1 = cpr::GetAsync(cpr::Url{\"http://example.com/api/data\"}, pool);\n * auto future2 = cpr::GetAsync(cpr::Url{\"http://example.com/api/more\"}, pool);\n * ```\n **/\nclass ConnectionPool {\n  public:\n    /**\n     * Creates a new connection pool with shared connection state.\n     * Initializes the underlying CURLSH handle and sets up thread-safe locking mechanisms.\n     **/\n    ConnectionPool();\n\n    /**\n     * Copy constructor - creates a new connection pool sharing the same connection state.\n     * Multiple ConnectionPool instances can share the same underlying connection pool.\n     **/\n    ConnectionPool(const ConnectionPool&) = default;\n\n    /**\n     * Copy assignment operator is deleted to prevent accidental copying.\n     * Use the copy constructor if you need to share the connection pool.\n     **/\n    ConnectionPool& operator=(const ConnectionPool&) = delete;\n\n    /**\n     * Configures a CURL easy handle to use this connection pool.\n     * This method sets up the easy handle to participate in connection sharing\n     * managed by this pool.\n     *\n     * @param easy_handler The CURL easy handle to configure for connection sharing.\n     **/\n    void SetupHandler(CURL* easy_handler) const;\n\n  private:\n    /**\n     * Thread-safe mutex used for synchronizing access to shared connections.\n     * This mutex is passed to libcurl's locking callbacks to ensure thread safety\n     * when multiple threads access the same connection pool. It's declared first\n     * to ensure it's destroyed last, after the CURLSH handle that references it.\n     **/\n    std::shared_ptr<std::mutex> connection_mutex_;\n\n    /**\n     * Shared CURL handle (CURLSH) that manages the actual connection sharing.\n     * This handle maintains the pool of reusable connections and is configured\n     * with appropriate locking callbacks for thread safety. The shared_ptr uses\n     * a custom deleter that safely resets the lock/unlock callbacks before\n     * calling curl_share_cleanup() to prevent use-after-free issues during destruction.\n     * Declared last to ensure it's destroyed first, before the mutex it references.\n     **/\n    std::shared_ptr<CURLSH> curl_sh_;\n};\n} // namespace cpr\n#endif"
  },
  {
    "path": "include/cpr/cookies.h",
    "content": "#ifndef CPR_COOKIES_H\n#define CPR_COOKIES_H\n\n#include \"cpr/curlholder.h\"\n#include <chrono>\n#include <initializer_list>\n#include <string>\n#include <vector>\n\nnamespace cpr {\n/**\n * EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable.\n * To be used for \"std::array\", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value.\n **/\nstatic const std::size_t EXPIRES_STRING_SIZE = 100;\n\nclass Cookie {\n  public:\n    Cookie() = default;\n    /**\n     * Some notes for the default value used by expires:\n     * std::chrono::system_clock::time_point::min() won't work on Windows due to the min, max clash there.\n     * So we fall back to std::chrono::system_clock::from_time_t(0) for the minimum value here.\n     **/\n    Cookie(const std::string& name, const std::string& value, const std::string& domain = \"\", bool p_isIncludingSubdomains = false, const std::string& path = \"/\", bool p_isHttpsOnly = false, std::chrono::system_clock::time_point expires = std::chrono::system_clock::from_time_t(0)) : name_{name}, value_{value}, domain_{domain}, includeSubdomains_{p_isIncludingSubdomains}, path_{path}, httpsOnly_{p_isHttpsOnly}, expires_{expires} {}\n    [[nodiscard]] const std::string& GetDomain() const;\n    [[nodiscard]] bool IsIncludingSubdomains() const;\n    [[nodiscard]] const std::string& GetPath() const;\n    [[nodiscard]] bool IsHttpsOnly() const;\n    [[nodiscard]] std::chrono::system_clock::time_point GetExpires() const;\n    [[nodiscard]] std::string GetExpiresString() const;\n    [[nodiscard]] const std::string& GetName() const;\n    [[nodiscard]] const std::string& GetValue() const;\n\n  private:\n    std::string name_;\n    std::string value_;\n    std::string domain_;\n    bool includeSubdomains_{};\n    std::string path_;\n    bool httpsOnly_{};\n    /**\n     * TODO: Update the implementation using `std::chrono::utc_clock` of C++20\n     **/\n    std::chrono::system_clock::time_point expires_;\n};\n\nclass Cookies {\n  public:\n    /**\n     * Should we URL-encode cookies when making a request.\n     * Based on RFC6265, it is recommended but not mandatory to encode cookies.\n     *\n     * -------\n     * To maximize compatibility with user agents, servers that wish to\n     * store arbitrary data in a cookie-value SHOULD encode that data, for\n     * example, using Base64 [RFC4648].\n     * -------\n     * Source: RFC6265 (https://www.ietf.org/rfc/rfc6265.txt)\n     **/\n    bool encode{true};\n\n    Cookies(bool p_encode = true) : encode{p_encode} {}\n    Cookies(const std::initializer_list<cpr::Cookie>& cookies, bool p_encode = true) : encode{p_encode}, cookies_{cookies} {}\n    Cookies(const cpr::Cookie& cookie, bool p_encode = true) : encode{p_encode}, cookies_{cookie} {}\n\n    cpr::Cookie& operator[](size_t pos);\n    [[nodiscard]] std::string GetEncoded(const CurlHolder& holder) const;\n\n    using iterator = std::vector<cpr::Cookie>::iterator;\n    using const_iterator = std::vector<cpr::Cookie>::const_iterator;\n\n    iterator begin();\n    iterator end();\n    [[nodiscard]] const_iterator begin() const;\n    [[nodiscard]] const_iterator end() const;\n    [[nodiscard]] const_iterator cbegin() const;\n    [[nodiscard]] const_iterator cend() const;\n    void emplace_back(const Cookie& str);\n    [[nodiscard]] bool empty() const;\n    void push_back(const Cookie& str);\n    void pop_back();\n\n  private:\n    std::vector<cpr::Cookie> cookies_;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/cpr.h",
    "content": "#ifndef CPR_CPR_H\n#define CPR_CPR_H\n\n#include \"cpr/api.h\"\n#include \"cpr/auth.h\"\n#include \"cpr/bearer.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/cert_info.h\"\n#include \"cpr/connect_timeout.h\"\n#include \"cpr/connection_pool.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/cprver.h\"\n#include \"cpr/curl_container.h\"\n#include \"cpr/curlholder.h\"\n#include \"cpr/error.h\"\n#include \"cpr/http_version.h\"\n#include \"cpr/interceptor.h\"\n#include \"cpr/interface.h\"\n#include \"cpr/limit_rate.h\"\n#include \"cpr/local_port.h\"\n#include \"cpr/local_port_range.h\"\n#include \"cpr/low_speed.h\"\n#include \"cpr/multipart.h\"\n#include \"cpr/multiperform.h\"\n#include \"cpr/parameters.h\"\n#include \"cpr/payload.h\"\n#include \"cpr/proxies.h\"\n#include \"cpr/proxyauth.h\"\n#include \"cpr/range.h\"\n#include \"cpr/redirect.h\"\n#include \"cpr/reserve_size.h\"\n#include \"cpr/resolve.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include \"cpr/sse.h\"\n#include \"cpr/ssl_ctx.h\"\n#include \"cpr/ssl_options.h\"\n#include \"cpr/status_codes.h\"\n#include \"cpr/timeout.h\"\n#include \"cpr/unix_socket.h\"\n#include \"cpr/user_agent.h\"\n#include \"cpr/util.h\"\n#include \"cpr/verbose.h\"\n\n#define CPR_LIBCURL_VERSION_NUM LIBCURL_VERSION_NUM\n\n#endif\n"
  },
  {
    "path": "include/cpr/cprtypes.h",
    "content": "#ifndef CPR_CPRTYPES_H\n#define CPR_CPRTYPES_H\n\n#include <curl/curl.h>\n#include <curl/system.h>\n#include <initializer_list>\n#include <map>\n#include <numeric>\n#include <string>\n#include <string_view>\n\nnamespace cpr {\n\n/**\n * Wrapper around \"curl_off_t\" to prevent applications from having to link against libcurl.\n **/\nusing cpr_off_t = curl_off_t;\n\n/**\n * The argument type for progress functions, dependent on libcurl version\n **/\n#if LIBCURL_VERSION_NUM < 0x072000\nusing cpr_pf_arg_t = double;\n#else\nusing cpr_pf_arg_t = cpr_off_t;\n#endif\n\ntemplate <class T>\nclass StringHolder {\n  public:\n  private:\n    StringHolder() = default;\n\n  public:\n  private:\n    explicit StringHolder(std::string str) : str_(std::move(str)) {}\n\n  public:\n  private:\n    explicit StringHolder(std::string_view str) : str_(str) {}\n\n  public:\n  private:\n    explicit StringHolder(const char* str) : str_(str) {}\n\n  public:\n  private:\n    StringHolder(const char* str, size_t len) : str_(str, len) {}\n\n  public:\n  private:\n    StringHolder(const std::initializer_list<std::string> args) {\n        str_ = std::accumulate(args.begin(), args.end(), str_);\n    }\n\n  public:\n  private:\n    StringHolder(const StringHolder& other) = default;\n\n  public:\n  private:\n    StringHolder(StringHolder&& old) noexcept = default;\n\n  public:\n    virtual ~StringHolder() = default;\n\n    StringHolder& operator=(StringHolder&& old) noexcept = default;\n\n    StringHolder& operator=(const StringHolder& other) = default;\n\n    explicit operator std::string() const {\n        return str_;\n    }\n\n    T operator+(const char* rhs) const {\n        return T(str_ + rhs);\n    }\n\n    T operator+(const std::string& rhs) const {\n        return T(str_ + rhs);\n    }\n\n    T operator+(const StringHolder<T>& rhs) const {\n        return T(str_ + rhs.str_);\n    }\n\n    void operator+=(const char* rhs) {\n        str_ += rhs;\n    }\n    void operator+=(const std::string& rhs) {\n        str_ += rhs;\n    }\n    void operator+=(const StringHolder<T>& rhs) {\n        str_ += rhs;\n    }\n\n    bool operator==(const char* rhs) const {\n        return str_ == rhs;\n    }\n    bool operator==(const std::string& rhs) const {\n        return str_ == rhs;\n    }\n    bool operator==(const StringHolder<T>& rhs) const {\n        return str_ == rhs.str_;\n    }\n\n    bool operator!=(const char* rhs) const {\n        return str_.c_str() != rhs;\n    }\n    bool operator!=(const std::string& rhs) const {\n        return str_ != rhs;\n    }\n    bool operator!=(const StringHolder<T>& rhs) const {\n        return str_ != rhs.str_;\n    }\n\n    const std::string& str() {\n        return str_;\n    }\n    [[nodiscard]] const std::string& str() const {\n        return str_;\n    }\n    [[nodiscard]] const char* c_str() const {\n        return str_.c_str();\n    }\n    [[nodiscard]] const char* data() const {\n        return str_.data();\n    }\n\n  protected:\n    std::string str_;\n    friend T;\n    friend T;\n    friend T;\n    friend T;\n    friend T;\n    friend T;\n    friend T;\n    friend T;\n};\n\ntemplate <class T>\nstd::ostream& operator<<(std::ostream& os, const StringHolder<T>& s) {\n    os << s.str();\n    return os;\n}\n\nclass Url : public StringHolder<Url> {\n  public:\n    Url() = default;\n    Url(std::string url) : StringHolder<Url>(std::move(url)) {}\n    Url(std::string_view url) : StringHolder<Url>(url) {}\n    Url(const char* url) : StringHolder<Url>(url) {}\n    Url(const char* str, size_t len) : StringHolder<Url>(std::string(str, len)) {}\n    Url(const std::initializer_list<std::string> args) : StringHolder<Url>(args) {}\n    Url(const Url& other) = default;\n    Url(Url&& old) noexcept = default;\n    ~Url() override = default;\n\n    Url& operator=(Url&& old) noexcept = default;\n    Url& operator=(const Url& other) = default;\n};\n\nstruct CaseInsensitiveCompare {\n    bool operator()(const std::string& a, const std::string& b) const noexcept;\n};\n\nusing Header = std::map<std::string, std::string, CaseInsensitiveCompare>;\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/curl_container.h",
    "content": "#ifndef CPR_CURL_CONTAINER_H\n#define CPR_CURL_CONTAINER_H\n\n#include <initializer_list>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cpr/curlholder.h\"\n\n\nnamespace cpr {\n\nstruct Parameter {\n    Parameter(std::string p_key, std::string p_value) : key{std::move(p_key)}, value{std::move(p_value)} {}\n\n    std::string key;\n    std::string value;\n};\n\nstruct Pair {\n    Pair(std::string p_key, std::string p_value) : key(std::move(p_key)), value(std::move(p_value)) {}\n\n    std::string key;\n    std::string value;\n};\n\n\ntemplate <class T>\nclass CurlContainer {\n  public:\n    /**\n     * Enables or disables URL encoding for keys and values when calling GetContent(...).\n     **/\n    bool encode = true;\n\n    CurlContainer() = default;\n    CurlContainer(const std::initializer_list<T>& /*containerList*/);\n\n    void Add(const std::initializer_list<T>& /*containerList*/);\n    void Add(const T& /*element*/);\n\n    /**\n     * Returns the URL using curl_easy_escape(...) for escaping the given parameters.\n     * Requires `CurlHolder`.\n     **/\n    [[nodiscard]] const std::string GetContent(const CurlHolder& /*holder*/) const;\n\n    /**\n     * Returns the URL while ignoring `encode`. This allows calling without\n     * active `CurlHolder`.\n     **/\n    [[nodiscard]] const std::string GetContent() const;\n\n  protected:\n    std::vector<T> containerList_;\n};\n\n} // namespace cpr\n\n#endif // CPR_CURL_CONTAINER_H\n"
  },
  {
    "path": "include/cpr/curlholder.h",
    "content": "#ifndef CPR_CURLHOLDER_H\n#define CPR_CURLHOLDER_H\n\n#include <array>\n#include <curl/curl.h>\n#include <mutex>\n\n#include \"cpr/secure_string.h\"\n\nnamespace cpr {\n\nstruct CurlHolder {\n  private:\n    /**\n     * Mutex for curl_easy_init().\n     * curl_easy_init() is not thread save.\n     * References:\n     * https://curl.haxx.se/libcurl/c/curl_easy_init.html\n     * https://curl.haxx.se/libcurl/c/threadsafe.html\n     **/\n\n    // Avoids initalization order problems in a static build\n    static std::mutex& curl_easy_init_mutex_() {\n        static std::mutex curl_easy_init_mutex_;\n        return curl_easy_init_mutex_;\n    }\n\n  public:\n    CURL* handle{nullptr};\n    struct curl_slist* chunk{nullptr};\n    struct curl_slist* resolveCurlList{nullptr};\n    curl_mime* multipart{nullptr};\n    std::array<char, CURL_ERROR_SIZE> error{};\n\n    CurlHolder();\n    CurlHolder(const CurlHolder& other) = delete;\n    CurlHolder(CurlHolder&& old) noexcept;\n    ~CurlHolder();\n\n    CurlHolder& operator=(const CurlHolder& other) = delete;\n    CurlHolder& operator=(CurlHolder&& old) noexcept;\n\n    /**\n     * Uses curl_easy_escape(...) for escaping the given string.\n     **/\n    [[nodiscard]] util::SecureString urlEncode(std::string_view s) const;\n\n    /**\n     * Uses curl_easy_unescape(...) for unescaping the given string.\n     **/\n    [[nodiscard]] util::SecureString urlDecode(std::string_view s) const;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/curlmultiholder.h",
    "content": "#ifndef CPR_CURLMULTIHOLDER_H\n#define CPR_CURLMULTIHOLDER_H\n\n#include <curl/curl.h>\n\nnamespace cpr {\n\nclass CurlMultiHolder {\n  public:\n    CurlMultiHolder();\n    ~CurlMultiHolder();\n\n    CURLM* handle{nullptr};\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/error.h",
    "content": "#ifndef CPR_ERROR_H\n#define CPR_ERROR_H\n\n#include <cstdint>\n#include <string>\n#include <unordered_map>\n\n#include \"cpr/cprtypes.h\"\n#include <utility>\n\nnamespace cpr {\n\n/**\n * cpr error codes that match the ones found inside 'curl.h'.\n * These error codes only include relevant error codes meaning no support for e.g. FTP errors since cpr does only support HTTP.\n **/\nenum class ErrorCode : uint16_t {\n    /**\n     * Everything is good and no error occurred.\n     **/\n    OK = 0,\n    UNSUPPORTED_PROTOCOL = 1,\n    FAILED_INIT = 2,\n    URL_MALFORMAT = 3,\n    NOT_BUILT_IN = 4,\n    COULDNT_RESOLVE_PROXY = 5,\n    COULDNT_RESOLVE_HOST = 6,\n    COULDNT_CONNECT = 7,\n    WEIRD_SERVER_REPLY = 8,\n    REMOTE_ACCESS_DENIED = 9,\n    HTTP2 = 10,\n    PARTIAL_FILE = 11,\n    QUOTE_ERROR = 12,\n    HTTP_RETURNED_ERROR = 13,\n    WRITE_ERROR = 14,\n    UPLOAD_FAILED = 15,\n    READ_ERROR = 16,\n    OUT_OF_MEMORY = 17,\n    OPERATION_TIMEDOUT = 18,\n    RANGE_ERROR = 19,\n    HTTP_POST_ERROR = 20,\n    SSL_CONNECT_ERROR = 21,\n    BAD_DOWNLOAD_RESUME = 22,\n    FILE_COULDNT_READ_FILE = 23,\n    FUNCTION_NOT_FOUND = 24,\n    ABORTED_BY_CALLBACK = 25,\n    BAD_FUNCTION_ARGUMENT = 26,\n    INTERFACE_FAILED = 27,\n    TOO_MANY_REDIRECTS = 28,\n    UNKNOWN_OPTION = 29,\n    SETOPT_OPTION_SYNTAX = 30,\n    GOT_NOTHING = 31,\n    SSL_ENGINE_NOTFOUND = 32,\n    SSL_ENGINE_SETFAILED = 33,\n    SEND_ERROR = 34,\n    RECV_ERROR = 35,\n    SSL_CERTPROBLEM = 36,\n    SSL_CIPHER = 37,\n    PEER_FAILED_VERIFICATION = 38,\n    BAD_CONTENT_ENCODING = 39,\n    FILESIZE_EXCEEDED = 40,\n    USE_SSL_FAILED = 41,\n    SEND_FAIL_REWIND = 42,\n    SSL_ENGINE_INITFAILED = 43,\n    LOGIN_DENIED = 44,\n    SSL_CACERT_BADFILE = 45,\n    SSL_SHUTDOWN_FAILED = 46,\n    AGAIN = 47,\n    SSL_CRL_BADFILE = 48,\n    SSL_ISSUER_ERROR = 49,\n    CHUNK_FAILED = 50,\n    NO_CONNECTION_AVAILABLE = 51,\n    SSL_PINNEDPUBKEYNOTMATCH = 52,\n    SSL_INVALIDCERTSTATUS = 53,\n    HTTP2_STREAM = 54,\n    RECURSIVE_API_CALL = 55,\n    AUTH_ERROR = 56,\n    HTTP3 = 57,\n    QUIC_CONNECT_ERROR = 58,\n    PROXY = 59,\n    SSL_CLIENTCERT = 60,\n    UNRECOVERABLE_POLL = 61,\n    TOO_LARGE = 62,\n    /**\n     * An unknown error inside curl occurred.\n     * Please try to reproduce it and then report it to us.\n     * It might be that there is a new curl error code we are not aware yet.\n     * Reporting bugs: https://github.com/libcpr/cpr\n     **/\n    UNKNOWN_ERROR = 1000,\n};\n\ninline const std::unordered_map<ErrorCode, std::string>& get_error_code_to_string_mapping() {\n    // Use a function-local static rather than inline global objects to avoid the 'double-destructor' problem in MSVC when using /MT flags.\n    static const std::unordered_map<ErrorCode, std::string> mapping = {{ErrorCode::OK, \"OK\"},\n                                                                       {ErrorCode::UNSUPPORTED_PROTOCOL, \"UNSUPPORTED_PROTOCOL\"},\n                                                                       {ErrorCode::FAILED_INIT, \"FAILED_INIT\"},\n                                                                       {ErrorCode::URL_MALFORMAT, \"URL_MALFORMAT\"},\n                                                                       {ErrorCode::NOT_BUILT_IN, \"NOT_BUILT_IN\"},\n                                                                       {ErrorCode::COULDNT_RESOLVE_PROXY, \"COULDNT_RESOLVE_PROXY\"},\n                                                                       {ErrorCode::COULDNT_RESOLVE_HOST, \"COULDNT_RESOLVE_HOST\"},\n                                                                       {ErrorCode::COULDNT_CONNECT, \"COULDNT_CONNECT\"},\n                                                                       {ErrorCode::WEIRD_SERVER_REPLY, \"WEIRD_SERVER_REPLY\"},\n                                                                       {ErrorCode::REMOTE_ACCESS_DENIED, \"REMOTE_ACCESS_DENIED\"},\n                                                                       {ErrorCode::HTTP2, \"HTTP2\"},\n                                                                       {ErrorCode::PARTIAL_FILE, \"PARTIAL_FILE\"},\n                                                                       {ErrorCode::QUOTE_ERROR, \"QUOTE_ERROR\"},\n                                                                       {ErrorCode::HTTP_RETURNED_ERROR, \"HTTP_RETURNED_ERROR\"},\n                                                                       {ErrorCode::WRITE_ERROR, \"WRITE_ERROR\"},\n                                                                       {ErrorCode::UPLOAD_FAILED, \"UPLOAD_FAILED\"},\n                                                                       {ErrorCode::READ_ERROR, \"READ_ERROR\"},\n                                                                       {ErrorCode::OUT_OF_MEMORY, \"OUT_OF_MEMORY\"},\n                                                                       {ErrorCode::OPERATION_TIMEDOUT, \"OPERATION_TIMEDOUT\"},\n                                                                       {ErrorCode::RANGE_ERROR, \"RANGE_ERROR\"},\n                                                                       {ErrorCode::HTTP_POST_ERROR, \"HTTP_POST_ERROR\"},\n                                                                       {ErrorCode::SSL_CONNECT_ERROR, \"SSL_CONNECT_ERROR\"},\n                                                                       {ErrorCode::BAD_DOWNLOAD_RESUME, \"BAD_DOWNLOAD_RESUME\"},\n                                                                       {ErrorCode::FILE_COULDNT_READ_FILE, \"FILE_COULDNT_READ_FILE\"},\n                                                                       {ErrorCode::FUNCTION_NOT_FOUND, \"FUNCTION_NOT_FOUND\"},\n                                                                       {ErrorCode::ABORTED_BY_CALLBACK, \"ABORTED_BY_CALLBACK\"},\n                                                                       {ErrorCode::BAD_FUNCTION_ARGUMENT, \"BAD_FUNCTION_ARGUMENT\"},\n                                                                       {ErrorCode::INTERFACE_FAILED, \"INTERFACE_FAILED\"},\n                                                                       {ErrorCode::TOO_MANY_REDIRECTS, \"TOO_MANY_REDIRECTS\"},\n                                                                       {ErrorCode::UNKNOWN_OPTION, \"UNKNOWN_OPTION\"},\n                                                                       {ErrorCode::SETOPT_OPTION_SYNTAX, \"SETOPT_OPTION_SYNTAX\"},\n                                                                       {ErrorCode::GOT_NOTHING, \"GOT_NOTHING\"},\n                                                                       {ErrorCode::SSL_ENGINE_NOTFOUND, \"SSL_ENGINE_NOTFOUND\"},\n                                                                       {ErrorCode::SSL_ENGINE_SETFAILED, \"SSL_ENGINE_SETFAILED\"},\n                                                                       {ErrorCode::SEND_ERROR, \"SEND_ERROR\"},\n                                                                       {ErrorCode::RECV_ERROR, \"RECV_ERROR\"},\n                                                                       {ErrorCode::SSL_CERTPROBLEM, \"SSL_CERTPROBLEM\"},\n                                                                       {ErrorCode::SSL_CIPHER, \"SSL_CIPHER\"},\n                                                                       {ErrorCode::PEER_FAILED_VERIFICATION, \"PEER_FAILED_VERIFICATION\"},\n                                                                       {ErrorCode::BAD_CONTENT_ENCODING, \"BAD_CONTENT_ENCODING\"},\n                                                                       {ErrorCode::FILESIZE_EXCEEDED, \"FILESIZE_EXCEEDED\"},\n                                                                       {ErrorCode::USE_SSL_FAILED, \"USE_SSL_FAILED\"},\n                                                                       {ErrorCode::SEND_FAIL_REWIND, \"SEND_FAIL_REWIND\"},\n                                                                       {ErrorCode::SSL_ENGINE_INITFAILED, \"SSL_ENGINE_INITFAILED\"},\n                                                                       {ErrorCode::LOGIN_DENIED, \"LOGIN_DENIED\"},\n                                                                       {ErrorCode::SSL_CACERT_BADFILE, \"SSL_CACERT_BADFILE\"},\n                                                                       {ErrorCode::SSL_SHUTDOWN_FAILED, \"SSL_SHUTDOWN_FAILED\"},\n                                                                       {ErrorCode::AGAIN, \"AGAIN\"},\n                                                                       {ErrorCode::SSL_CRL_BADFILE, \"SSL_CRL_BADFILE\"},\n                                                                       {ErrorCode::SSL_ISSUER_ERROR, \"SSL_ISSUER_ERROR\"},\n                                                                       {ErrorCode::CHUNK_FAILED, \"CHUNK_FAILED\"},\n                                                                       {ErrorCode::NO_CONNECTION_AVAILABLE, \"NO_CONNECTION_AVAILABLE\"},\n                                                                       {ErrorCode::SSL_PINNEDPUBKEYNOTMATCH, \"SSL_PINNEDPUBKEYNOTMATCH\"},\n                                                                       {ErrorCode::SSL_INVALIDCERTSTATUS, \"SSL_INVALIDCERTSTATUS\"},\n                                                                       {ErrorCode::HTTP2_STREAM, \"HTTP2_STREAM\"},\n                                                                       {ErrorCode::RECURSIVE_API_CALL, \"RECURSIVE_API_CALL\"},\n                                                                       {ErrorCode::AUTH_ERROR, \"AUTH_ERROR\"},\n                                                                       {ErrorCode::HTTP3, \"HTTP3\"},\n                                                                       {ErrorCode::QUIC_CONNECT_ERROR, \"QUIC_CONNECT_ERROR\"},\n                                                                       {ErrorCode::PROXY, \"PROXY\"},\n                                                                       {ErrorCode::SSL_CLIENTCERT, \"SSL_CLIENTCERT\"},\n                                                                       {ErrorCode::UNRECOVERABLE_POLL, \"UNRECOVERABLE_POLL\"},\n                                                                       {ErrorCode::TOO_LARGE, \"TOO_LARGE\"},\n                                                                       {ErrorCode::UNKNOWN_ERROR, \"UNKNOWN_ERROR\"}};\n    return mapping;\n}\n\nclass Error {\n  public:\n    ErrorCode code = ErrorCode::OK;\n    std::string message;\n\n    Error() = default;\n\n    Error(const std::int32_t& curl_code, std::string&& p_error_message) : code{getErrorCodeForCurlError(curl_code)}, message(std::move(p_error_message)) {}\n\n    explicit operator bool() const {\n        return code != ErrorCode::OK;\n    }\n\n  private:\n    static ErrorCode getErrorCodeForCurlError(std::int32_t curl_code);\n};\n\n} // namespace cpr\n\n// NOLINTBEGIN(cert-dcl58-cpp) required for to_string\nnamespace std {\ninline std::string to_string(const cpr::ErrorCode& code) {\n    return cpr::get_error_code_to_string_mapping().at(code);\n}\n} // namespace std\n// NOLINTEND(cert-dcl58-cpp) required for to_string\n\n\n#endif\n"
  },
  {
    "path": "include/cpr/file.h",
    "content": "#ifndef CPR_FILE_H\n#define CPR_FILE_H\n\n#include <initializer_list>\n#include <string>\n#include <vector>\n\n#include \"cpr/filesystem.h\"\n\nnamespace cpr {\n\nstruct File {\n    explicit File(std::string p_filepath, const std::string& p_overriden_filename = {}) : filepath(std::move(p_filepath)), overriden_filename(p_overriden_filename) {}\n\n    std::string filepath;\n    std::string overriden_filename;\n\n    [[nodiscard]] bool hasOverridenFilename() const noexcept {\n        return !overriden_filename.empty();\n    }\n};\n\nclass Files {\n  public:\n    Files() = default;\n    Files(const File& p_file) : files{p_file} {}\n\n    Files(const Files& other) = default;\n    Files(Files&& old) noexcept = default;\n\n    Files(const std::initializer_list<File>& p_files) : files{p_files} {}\n    Files(const std::initializer_list<std::string>& p_filepaths);\n\n    ~Files() noexcept = default;\n\n    Files& operator=(const Files& other);\n    Files& operator=(Files&& old) noexcept;\n\n    using iterator = std::vector<File>::iterator;\n    using const_iterator = std::vector<File>::const_iterator;\n\n    iterator begin();\n    iterator end();\n    [[nodiscard]] const_iterator begin() const;\n    [[nodiscard]] const_iterator end() const;\n    [[nodiscard]] const_iterator cbegin() const;\n    [[nodiscard]] const_iterator cend() const;\n    void emplace_back(const File& file);\n    void push_back(const File& file);\n    void pop_back();\n\n  private:\n    std::vector<File> files;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/filesystem.h",
    "content": "#ifndef CPR_FILESYSTEM_H\n#define CPR_FILESYSTEM_H\n\n// Include filesystem into the namespace \"fs\" from either \"filesystem\" or \"experimental/filesystem\" or \"boost/filesystem\"\n#ifdef CPR_USE_BOOST_FILESYSTEM\n#define BOOST_FILESYSTEM_VERSION 4 // Use the latest, with the closest behavior to std::filesystem.\n#include <boost/filesystem.hpp>\nnamespace cpr {\nnamespace fs = boost::filesystem;\n}\n// cppcheck-suppress preprocessorErrorDirective\n#elif __has_include(<filesystem>)\n#include <filesystem>\nnamespace cpr {\nnamespace fs = std::filesystem;\n} // namespace cpr\n#elif __has_include(\"experimental/filesystem\")\n#include <experimental/filesystem>\nnamespace cpr {\nnamespace fs = std::experimental::filesystem;\n}\n#else\n#error \"Failed to include <filesystem> header!\"\n#endif\n\n#endif\n"
  },
  {
    "path": "include/cpr/http_version.h",
    "content": "#ifndef CPR_HTTP_VERSION_H\n#define CPR_HTTP_VERSION_H\n\n#include <cstdint>\n#include <curl/curlver.h>\n\nnamespace cpr {\nenum class HttpVersionCode : uint8_t {\n    /**\n     * Let libcurl decide which version is the best.\n     **/\n    VERSION_NONE,\n    /**\n     * Enforce HTTP 1.0 requests.\n     **/\n    VERSION_1_0,\n    /**\n     * Enforce HTTP 1.1 requests.\n     **/\n    VERSION_1_1,\n#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0\n    /**\n     * Attempt HTTP 2.0 requests.\n     * Fallback to HTTP 1.1 if negotiation fails.\n     **/\n    VERSION_2_0,\n#endif\n#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0\n    /**\n     * Attempt HTTP 2.0 for HTTPS requests only.\n     * Fallback to HTTP 1.1 if negotiation fails.\n     * HTTP 1.1 will be used for HTTP connections.\n     **/\n    VERSION_2_0_TLS,\n#endif\n#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0\n    /**\n     * Start HTTP 2.0 for HTTP requests.\n     * Requires prior knowledge that the server supports HTTP 2.0.\n     * For HTTPS requests we will negotiate the protocol version in the TLS handshake.\n     **/\n    VERSION_2_0_PRIOR_KNOWLEDGE,\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0\n    /**\n     * Attempt HTTP 3.0 requests.\n     * Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade.\n     * Fallback to HTTP 1.1 if negotiation fails.\n     **/\n    VERSION_3_0,\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075701 // 7.87.1, but corresponds to 7.88.0 tag\n    /**\n     * Enforce HTTP 3.0 requests without fallback.\n     * Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade.\n     **/\n    VERSION_3_0_ONLY\n#endif\n};\n\nclass HttpVersion {\n  public:\n    /**\n     * The HTTP version that should be used by libcurl when initiating a HTTP(S) connection.\n     * Default: HttpVersionCode::VERSION_NONE\n     **/\n    HttpVersionCode code = HttpVersionCode::VERSION_NONE;\n\n    HttpVersion() = default;\n    explicit HttpVersion(HttpVersionCode _code) : code(_code) {}\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/interceptor.h",
    "content": "#ifndef CPR_INTERCEPTOR_H\n#define CPR_INTERCEPTOR_H\n\n#include \"cpr/multiperform.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include <vector>\n\nnamespace cpr {\nclass Interceptor {\n  public:\n    enum class ProceedHttpMethod : uint8_t {\n        GET_REQUEST = 0,\n        POST_REQUEST,\n        PUT_REQUEST,\n        DELETE_REQUEST,\n        PATCH_REQUEST,\n        HEAD_REQUEST,\n        OPTIONS_REQUEST,\n        DOWNLOAD_CALLBACK_REQUEST,\n        DOWNLOAD_FILE_REQUEST,\n    };\n\n    Interceptor() = default;\n    Interceptor(const Interceptor& other) = default;\n    Interceptor(Interceptor&& old) = default;\n    virtual ~Interceptor() = default;\n\n    Interceptor& operator=(const Interceptor& other) = default;\n    Interceptor& operator=(Interceptor&& old) = default;\n\n    virtual Response intercept(Session& session) = 0;\n\n  protected:\n    static Response proceed(Session& session);\n    static Response proceed(Session& session, ProceedHttpMethod httpMethod);\n    static Response proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file);\n    static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write);\n};\n\nclass InterceptorMulti {\n  public:\n    enum class ProceedHttpMethod : uint8_t {\n        GET_REQUEST = 0,\n        POST_REQUEST,\n        PUT_REQUEST,\n        DELETE_REQUEST,\n        PATCH_REQUEST,\n        HEAD_REQUEST,\n        OPTIONS_REQUEST,\n        DOWNLOAD_CALLBACK_REQUEST,\n        DOWNLOAD_FILE_REQUEST,\n    };\n\n    InterceptorMulti() = default;\n    InterceptorMulti(const InterceptorMulti& other) = default;\n    InterceptorMulti(InterceptorMulti&& old) = default;\n    virtual ~InterceptorMulti() = default;\n\n    InterceptorMulti& operator=(const InterceptorMulti& other) = default;\n    InterceptorMulti& operator=(InterceptorMulti&& old) = default;\n\n    virtual std::vector<Response> intercept(MultiPerform& multi) = 0;\n\n  protected:\n    static std::vector<Response> proceed(MultiPerform& multi);\n\n    static void PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write);\n};\n\n} // namespace cpr\n\n\n#endif\n"
  },
  {
    "path": "include/cpr/interface.h",
    "content": "#ifndef CPR_INTERFACE_H\n#define CPR_INTERFACE_H\n\n#include <initializer_list>\n#include <string>\n\n#include \"cpr/cprtypes.h\"\n\nnamespace cpr {\n\nclass Interface : public StringHolder<Interface> {\n  public:\n    Interface() = default;\n    Interface(std::string iface) : StringHolder<Interface>(std::move(iface)) {}\n    Interface(std::string_view iface) : StringHolder<Interface>(iface) {}\n    Interface(const char* iface) : StringHolder<Interface>(iface) {}\n    Interface(const char* str, size_t len) : StringHolder<Interface>(str, len) {}\n    Interface(const std::initializer_list<std::string> args) : StringHolder<Interface>(args) {}\n    Interface(const Interface& other) = default;\n    Interface(Interface&& old) noexcept = default;\n    ~Interface() override = default;\n\n    Interface& operator=(Interface&& old) noexcept = default;\n    Interface& operator=(const Interface& other) = default;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/limit_rate.h",
    "content": "#ifndef CPR_LIMIT_RATE_H\n#define CPR_LIMIT_RATE_H\n\n#include <cstdint>\n\nnamespace cpr {\n\nclass LimitRate {\n  public:\n    LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {}\n\n    std::int64_t downrate = 0;\n    std::int64_t uprate = 0;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/local_port.h",
    "content": "#ifndef CPR_LOCAL_PORT_H\n#define CPR_LOCAL_PORT_H\n\n#include <cstdint>\n\nnamespace cpr {\n\nclass LocalPort {\n  public:\n    LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {}\n\n    operator std::uint16_t() const {\n        return localport_;\n    }\n\n  private:\n    std::uint16_t localport_ = 0;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/local_port_range.h",
    "content": "#ifndef CPR_LOCAL_PORT_RANGE_H\n#define CPR_LOCAL_PORT_RANGE_H\n\n#include <cstdint>\n\nnamespace cpr {\n\nclass LocalPortRange {\n  public:\n    LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {}\n\n    operator std::uint16_t() const {\n        return localportrange_;\n    }\n\n  private:\n    std::uint16_t localportrange_ = 0;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/low_speed.h",
    "content": "#ifndef CPR_LOW_SPEED_H\n#define CPR_LOW_SPEED_H\n\n#include <chrono>\n#include <cstdint>\n\nnamespace cpr {\n\nclass LowSpeed {\n  public:\n    [[deprecated(\"Will be removed in CPR 2.x - Use the constructor with std::chrono::seconds instead of std::int32_t\")]]\n    LowSpeed(const std::int32_t p_limit, const std::int32_t p_time)\n            : limit(p_limit), time(std::chrono::seconds(p_time)) {}\n\n    LowSpeed(const std::int32_t p_limit, const std::chrono::seconds p_time) : limit(p_limit), time(p_time) {}\n\n    std::int32_t limit;\n    std::chrono::seconds time;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/multipart.h",
    "content": "#ifndef CPR_MULTIPART_H\n#define CPR_MULTIPART_H\n\n#include <cstdint>\n#include <initializer_list>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"buffer.h\"\n#include \"file.h\"\n\nnamespace cpr {\n\nstruct Part {\n    Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) : name{p_name}, value{p_value}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}\n    Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) : name{p_name}, value{std::to_string(p_value)}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}\n    Part(const std::string& p_name, const Files& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {}\n    Part(const std::string& p_name, Files&& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{std::move(p_files)} {}\n    Part(const std::string& p_name, const Buffer& buffer, const std::string& p_content_type = {}) : name{p_name}, value{buffer.filename.string()}, content_type{p_content_type}, data{buffer.data}, datalen{buffer.datalen}, is_file{false}, is_buffer{true} {}\n\n    std::string name;\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string value;\n    std::string content_type;\n    Buffer::data_t data{nullptr};\n    size_t datalen{0};\n    bool is_file;\n    bool is_buffer;\n\n    Files files;\n};\n\nclass Multipart {\n  public:\n    Multipart(const std::initializer_list<Part>& parts);\n    explicit Multipart(const std::vector<Part>& parts);\n    explicit Multipart(const std::vector<Part>&& parts);\n\n    std::vector<Part> parts;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/multiperform.h",
    "content": "#ifndef CPR_MULTIPERFORM_H\n#define CPR_MULTIPERFORM_H\n\n#include \"cpr/curlmultiholder.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include <functional>\n#include <memory>\n#include <queue>\n#include <stdexcept>\n#include <vector>\n\nnamespace cpr {\n\nclass InterceptorMulti;\n\nclass MultiPerform {\n  public:\n    enum class HttpMethod : uint8_t {\n        UNDEFINED = 0,\n        GET_REQUEST,\n        POST_REQUEST,\n        PUT_REQUEST,\n        DELETE_REQUEST,\n        PATCH_REQUEST,\n        HEAD_REQUEST,\n        OPTIONS_REQUEST,\n        DOWNLOAD_REQUEST,\n    };\n\n    MultiPerform();\n    MultiPerform(const MultiPerform& other) = delete;\n    MultiPerform(MultiPerform&& old) noexcept;\n    ~MultiPerform();\n\n    MultiPerform& operator=(const MultiPerform& other) = delete;\n    MultiPerform& operator=(MultiPerform&& old) noexcept;\n\n    std::vector<Response> Get();\n    std::vector<Response> Delete();\n    template <typename... DownloadArgTypes>\n    std::vector<Response> Download(DownloadArgTypes... args);\n    std::vector<Response> Put();\n    std::vector<Response> Head();\n    std::vector<Response> Options();\n    std::vector<Response> Patch();\n    std::vector<Response> Post();\n\n    std::vector<Response> Perform();\n    template <typename... DownloadArgTypes>\n    std::vector<Response> PerformDownload(DownloadArgTypes... args);\n\n    void AddSession(std::shared_ptr<Session>& session, HttpMethod method = HttpMethod::UNDEFINED);\n    void RemoveSession(const std::shared_ptr<Session>& session);\n    std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>>& GetSessions();\n    [[nodiscard]] const std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>>& GetSessions() const;\n\n    void AddInterceptor(const std::shared_ptr<InterceptorMulti>& pinterceptor);\n\n  private:\n    // Interceptors should be able to call the private proceed() and PrepareDownloadSessions() functions\n    friend InterceptorMulti;\n\n    void SetHttpMethod(HttpMethod method);\n\n    void PrepareSessions();\n    template <typename CurrentDownloadArgType, typename... DownloadArgTypes>\n    void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args);\n    template <typename CurrentDownloadArgType>\n    void PrepareDownloadSessions(size_t sessions_index, const CurrentDownloadArgType& current_arg);\n    void PrepareDownloadSession(size_t sessions_index, std::ofstream& file);\n    void PrepareDownloadSession(size_t sessions_index, const WriteCallback& write);\n\n    void PrepareGet();\n    void PrepareDelete();\n    void PreparePut();\n    void PreparePatch();\n    void PrepareHead();\n    void PrepareOptions();\n    void PreparePost();\n    template <typename... DownloadArgTypes>\n    void PrepareDownload(DownloadArgTypes... args);\n\n    const std::optional<std::vector<Response>> intercept();\n    std::vector<Response> proceed();\n    std::vector<Response> MakeRequest();\n    std::vector<Response> MakeDownloadRequest();\n\n    void DoMultiPerform();\n    std::vector<Response> ReadMultiInfo(const std::function<Response(Session&, CURLcode)>& complete_function);\n\n    std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>> sessions_;\n    std::unique_ptr<CurlMultiHolder> multicurl_;\n    bool is_download_multi_perform{false};\n\n    using InterceptorsContainer = std::list<std::shared_ptr<InterceptorMulti>>;\n    InterceptorsContainer interceptors_;\n    // Currently running interceptor\n    InterceptorsContainer::iterator current_interceptor_;\n    // Interceptor within the chain where to start with each repeated request\n    InterceptorsContainer::iterator first_interceptor_;\n};\n\ntemplate <typename CurrentDownloadArgType>\nvoid MultiPerform::PrepareDownloadSessions(size_t sessions_index, const CurrentDownloadArgType& current_arg) {\n    PrepareDownloadSession(sessions_index, current_arg);\n}\n\ntemplate <typename CurrentDownloadArgType, typename... DownloadArgTypes>\nvoid MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args) {\n    PrepareDownloadSession(sessions_index, current_arg);\n    PrepareDownloadSessions<DownloadArgTypes...>(sessions_index + 1, args...);\n}\n\n\ntemplate <typename... DownloadArgTypes>\nvoid MultiPerform::PrepareDownload(DownloadArgTypes... args) {\n    SetHttpMethod(HttpMethod::DOWNLOAD_REQUEST);\n    PrepareDownloadSessions<DownloadArgTypes...>(0, args...);\n}\n\ntemplate <typename... DownloadArgTypes>\nstd::vector<Response> MultiPerform::Download(DownloadArgTypes... args) {\n    if (sizeof...(args) != sessions_.size()) {\n        throw std::invalid_argument(\"Number of download arguments has to match the number of sessions added to the multiperform!\");\n    }\n    PrepareDownload(args...);\n    return MakeDownloadRequest();\n}\n\ntemplate <typename... DownloadArgTypes>\nstd::vector<Response> MultiPerform::PerformDownload(DownloadArgTypes... args) {\n    if (sizeof...(args) != sessions_.size()) {\n        throw std::invalid_argument(\"Number of download arguments has to match the number of sessions added to the multiperform!\");\n    }\n    PrepareDownloadSessions<DownloadArgTypes...>(0, args...);\n    return MakeDownloadRequest();\n}\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/parameters.h",
    "content": "#ifndef CPR_PARAMETERS_H\n#define CPR_PARAMETERS_H\n\n#include <initializer_list>\n\n#include \"cpr/curl_container.h\"\n\nnamespace cpr {\n\nclass Parameters : public CurlContainer<Parameter> {\n  public:\n    Parameters() = default;\n    Parameters(const std::initializer_list<Parameter>& parameters) : CurlContainer<Parameter>(parameters) {}\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/payload.h",
    "content": "#ifndef CPR_PAYLOAD_H\n#define CPR_PAYLOAD_H\n\n#include <initializer_list>\n\n#include \"cpr/curl_container.h\"\n\n\nnamespace cpr {\nclass Payload : public CurlContainer<Pair> {\n  public:\n    template <class It>\n    Payload(const It begin, const It end) {\n        for (It pair = begin; pair != end; ++pair) {\n            Add(*pair);\n        }\n    }\n    Payload(const std::initializer_list<Pair>& pairs) : CurlContainer<Pair>(pairs) {}\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/proxies.h",
    "content": "#ifndef CPR_PROXIES_H\n#define CPR_PROXIES_H\n\n#include <initializer_list>\n#include <map>\n#include <string>\n\nnamespace cpr {\nclass Proxies {\n  public:\n    Proxies() = default;\n    Proxies(const std::initializer_list<std::pair<const std::string, std::string>>& hosts);\n    explicit Proxies(const std::map<std::string, std::string>& hosts);\n\n    [[nodiscard]] bool has(const std::string& protocol) const;\n    const std::string& operator[](const std::string& protocol);\n\n  private:\n    std::map<std::string, std::string> hosts_;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/proxyauth.h",
    "content": "#ifndef CPR_PROXYAUTH_H\n#define CPR_PROXYAUTH_H\n\n#include <initializer_list>\n#include <map>\n#include <string>\n#include <string_view>\n\n#include \"cpr/auth.h\"\n#include \"cpr/util.h\"\n\nnamespace cpr {\nclass ProxyAuthentication;\n\nclass EncodedAuthentication {\n    friend ProxyAuthentication;\n\n  public:\n    EncodedAuthentication() = default;\n    EncodedAuthentication(std::string_view p_username, std::string_view p_password) : username(util::urlEncode(p_username)), password(util::urlEncode(p_password)) {}\n    EncodedAuthentication(const EncodedAuthentication& other) = default;\n    EncodedAuthentication(EncodedAuthentication&& old) noexcept = default;\n    virtual ~EncodedAuthentication() noexcept = default;\n\n    EncodedAuthentication& operator=(EncodedAuthentication&& old) noexcept = default;\n    EncodedAuthentication& operator=(const EncodedAuthentication& other) = default;\n\n    [[nodiscard]] std::string_view GetUsername() const;\n    [[nodiscard]] std::string_view GetPassword() const;\n    [[nodiscard]] const util::SecureString& GetUsernameUnderlying() const;\n    [[nodiscard]] const util::SecureString& GetPasswordUnderlying() const;\n\n  private:\n    util::SecureString username;\n    util::SecureString password;\n};\n\nclass ProxyAuthentication {\n  public:\n    ProxyAuthentication() = default;\n    ProxyAuthentication(const std::initializer_list<std::pair<const std::string, EncodedAuthentication>>& auths) : proxyAuth_{auths} {}\n    explicit ProxyAuthentication(const std::map<std::string, EncodedAuthentication>& auths) : proxyAuth_{auths} {}\n\n    [[nodiscard]] bool has(const std::string& protocol) const;\n    [[nodiscard]] std::string_view GetUsername(const std::string& protocol);\n    [[nodiscard]] std::string_view GetPassword(const std::string& protocol);\n    [[nodiscard]] const util::SecureString& GetUsernameUnderlying(const std::string& protocol) const;\n    [[nodiscard]] const util::SecureString& GetPasswordUnderlying(const std::string& protocol) const;\n\n  private:\n    std::map<std::string, EncodedAuthentication> proxyAuth_;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/range.h",
    "content": "#ifndef CPR_RANGE_H\n#define CPR_RANGE_H\n\n#include <cstdint>\n#include <optional>\n\nnamespace cpr {\n\nclass Range {\n  public:\n    explicit Range(const std::optional<std::int64_t> p_resume_from = std::nullopt, const std::optional<std::int64_t> p_finish_at = std::nullopt) {\n        resume_from = p_resume_from.value_or(0);\n        finish_at = p_finish_at.value_or(-1);\n    }\n\n    std::int64_t resume_from;\n    std::int64_t finish_at;\n\n    [[nodiscard]] const std::string str() const {\n        std::string const from_str = (resume_from < 0U) ? \"\" : std::to_string(resume_from);\n        std::string const to_str = (finish_at < 0U) ? \"\" : std::to_string(finish_at);\n        return from_str + \"-\" + to_str;\n    }\n};\n\nclass MultiRange {\n  public:\n    MultiRange(std::initializer_list<Range> rs) : ranges{rs} {}\n\n    [[nodiscard]] const std::string str() const {\n        std::string multi_range_string{};\n        for (Range const range : ranges) {\n            multi_range_string += ((multi_range_string.empty()) ? \"\" : \", \") + range.str();\n        }\n        return multi_range_string;\n    }\n\n  private:\n    std::vector<Range> ranges;\n}; // namespace cpr\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/redirect.h",
    "content": "#ifndef CPR_REDIRECT_H\n#define CPR_REDIRECT_H\n\n#include <cstdint>\n\nnamespace cpr {\nenum class PostRedirectFlags : uint8_t {\n    /**\n     * Respect RFC 7231 (section 6.4.2 to 6.4.4).\n     * Same as CURL_REDIR_POST_301 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).\n     **/\n    POST_301 = 0x1 << 0,\n    /**\n     * Maintain the request method after a 302 redirect.\n     * Same as CURL_REDIR_POST_302 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).\n     **/\n    POST_302 = 0x1 << 1,\n    /**\n     * Maintain the request method after a 303 redirect.\n     * Same as CURL_REDIR_POST_303 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).\n     **/\n    POST_303 = 0x1 << 2,\n    /**\n     * Convenience option to enable all flags.\n     * Same as CURL_REDIR_POST_ALL (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).\n     **/\n    POST_ALL = POST_301 | POST_302 | POST_303,\n    /**\n     * Default value.\n     * * Convenience option to disable all flags.\n     **/\n    NONE = 0x0\n};\n\nPostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs);\nPostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs);\nPostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs);\nPostRedirectFlags operator~(PostRedirectFlags flag);\nPostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs);\nPostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs);\nPostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs);\nbool any(PostRedirectFlags flag);\n\nclass Redirect {\n  public:\n    /**\n     * The maximum number of redirects to follow.\n     * 0: Refuse any redirects.\n     * -1: Infinite number of redirects.\n     * Default: 50\n     * https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html\n     **/\n    // NOLINTNEXTLINE (google-runtime-int)\n    long maximum{50L};\n    /**\n     * Follow 3xx redirects.\n     * Default: true\n     * https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html\n     **/\n    bool follow{true};\n    /**\n     * Continue to send authentication (user+password) credentials when following locations, even when hostname changed.\n     * Default: false\n     * https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html\n     **/\n    bool cont_send_cred{false};\n    /**\n     * Flags to control how to act after a redirect for a post request.\n     * Default: NONE (matches libcurl)\n     **/\n    PostRedirectFlags post_flags{PostRedirectFlags::NONE};\n\n    Redirect() = default;\n    // NOLINTNEXTLINE (google-runtime-int)\n    Redirect(long p_maximum, bool p_follow, bool p_cont_send_cred, PostRedirectFlags p_post_flags) : maximum(p_maximum), follow(p_follow), cont_send_cred(p_cont_send_cred), post_flags(p_post_flags) {}\n    // NOLINTNEXTLINE (google-runtime-int)\n    explicit Redirect(long p_maximum) : maximum(p_maximum) {}\n    explicit Redirect(bool p_follow) : follow(p_follow) {}\n    Redirect(bool p_follow, bool p_cont_send_cred) : follow(p_follow), cont_send_cred(p_cont_send_cred) {}\n    explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags) {}\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/reserve_size.h",
    "content": "#ifndef CPR_RESERVE_SIZE_H\n#define CPR_RESERVE_SIZE_H\n\n#include <cstdint>\n\nnamespace cpr {\n\nclass ReserveSize {\n  public:\n    ReserveSize(const std::size_t _size) : size(_size) {}\n\n    std::size_t size = 0;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/resolve.h",
    "content": "#ifndef CPR_RESOLVE_H\n#define CPR_RESOLVE_H\n\n#include <set>\n#include <string>\n\nnamespace cpr {\nclass Resolve {\n  public:\n    std::string host;\n    std::string addr;\n    std::set<uint16_t> ports;\n\n    Resolve(const std::string& host_param, const std::string& addr_param, const std::set<uint16_t>& ports_param = std::set<uint16_t>{80U, 443U}) : host(host_param), addr(addr_param), ports(ports_param) {\n        if (this->ports.empty()) {\n            this->ports.insert(80U);\n            this->ports.insert(443U);\n        }\n    }\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/response.h",
    "content": "#ifndef CPR_RESPONSE_H\n#define CPR_RESPONSE_H\n\n#include <cassert>\n#include <cstdint>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"cpr/cert_info.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/error.h\"\n#include \"cpr/ssl_options.h\"\n#include \"cpr/util.h\"\n\nnamespace cpr {\n\nclass MultiPerform;\n\nclass Response {\n  private:\n    friend MultiPerform;\n    std::shared_ptr<CurlHolder> curl_{nullptr};\n\n  public:\n    // Ignored here since libcurl uses a long for this.\n    // NOLINTNEXTLINE(google-runtime-int)\n    long status_code{};\n    std::string text;\n    Header header;\n    Url url;\n    double elapsed{};\n    Cookies cookies;\n    Error error;\n    std::string raw_header;\n    std::string status_line;\n    std::string reason;\n    cpr_off_t uploaded_bytes{};\n    cpr_off_t downloaded_bytes{};\n    // Ignored here since libcurl uses a long for this.\n    // NOLINTNEXTLINE(google-runtime-int)\n    long redirect_count{};\n    std::string primary_ip;\n    std::uint16_t primary_port{};\n\n    Response() = default;\n    Response(std::shared_ptr<CurlHolder> curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies, Error&& p_error);\n    [[nodiscard]] std::vector<CertInfo> GetCertInfos() const;\n    Response(const Response& other) = default;\n    Response(Response&& old) noexcept = default;\n    ~Response() noexcept = default;\n\n    Response& operator=(Response&& old) noexcept = default;\n    Response& operator=(const Response& other) = default;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/secure_string.h",
    "content": "#ifndef CPR_SECURE_STRING_H\n#define CPR_SECURE_STRING_H\n\n#include <memory>\n#include <string>\n#include <string_view>\n\nnamespace cpr::util {\n\n// This is an allocator that overwrites memory with zero values before\n// deallocating the memory, so as to not leave secrets in unallocated memory\n// sections.\ntemplate <typename T>\nstruct SecureAllocator : private std::allocator<T> {\n    template <typename U>\n    friend struct SecureAllocator;\n    SecureAllocator() = default;\n    template <typename U>\n    SecureAllocator(const SecureAllocator<U>& rhs) noexcept : std::allocator<T>(static_cast<const std::allocator<U>&>(rhs)) {}\n    template <typename U>\n    SecureAllocator(SecureAllocator<U>&& rhs) noexcept : std::allocator<T>(static_cast<std::allocator<U>&&>(std::move(rhs))) {}\n    template <typename U>\n    SecureAllocator& operator=(const SecureAllocator<U>& rhs) noexcept {\n        static_cast<std::allocator<T>&>(*this) = static_cast<const std::allocator<U>&>(rhs);\n        return *this;\n    }\n    template <typename U>\n    SecureAllocator& operator=(SecureAllocator<U>&& rhs) noexcept {\n        static_cast<std::allocator<T>&>(*this) = static_cast<std::allocator<U>&&>(std::move(rhs));\n        return *this;\n    }\n\n    using value_type = T;\n\n    // NOLINTNEXTLINE(readability-identifier-naming)\n    T* allocate(std::size_t n) {\n        return static_cast<std::allocator<T>&>(*this).allocate(n);\n    }\n\n    // NOLINTNEXTLINE(readability-identifier-naming)\n    void deallocate(T* p, std::size_t n) {\n        std::fill_n(p, n, T{});\n        static_cast<std::allocator<T>&>(*this).deallocate(p, n);\n    }\n\n    template <typename U>\n    [[nodiscard]] bool IsEqual(const SecureAllocator<U>& rhs) const noexcept {\n        return static_cast<const std::allocator<T>&>(*this) == static_cast<const std::allocator<U>&>(rhs);\n    }\n};\ntemplate <typename T, typename U>\nbool operator==(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) noexcept {\n    return lhs.IsEqual(rhs);\n}\ntemplate <typename T, typename U>\nbool operator!=(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) noexcept {\n    return !lhs.IsEqual(rhs);\n}\n\nusing SecureString = std::basic_string<char, std::char_traits<char>, SecureAllocator<char>>;\n\n} // namespace cpr::util\n\n#endif // CPR_SECURE_STRING_H\n"
  },
  {
    "path": "include/cpr/session.h",
    "content": "#ifndef CPR_SESSION_H\n#define CPR_SESSION_H\n\n#include <cstdint>\n#include <fstream>\n#include <functional>\n#include <future>\n#include <list>\n#include <memory>\n#include <optional>\n#include <variant>\n\n#include \"cpr/accept_encoding.h\"\n#include \"cpr/async_wrapper.h\"\n#include \"cpr/auth.h\"\n#include \"cpr/bearer.h\"\n#include \"cpr/body.h\"\n#include \"cpr/body_view.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/connect_timeout.h\"\n#include \"cpr/connection_pool.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/curlholder.h\"\n#include \"cpr/http_version.h\"\n#include \"cpr/interface.h\"\n#include \"cpr/limit_rate.h\"\n#include \"cpr/local_port.h\"\n#include \"cpr/local_port_range.h\"\n#include \"cpr/low_speed.h\"\n#include \"cpr/multipart.h\"\n#include \"cpr/parameters.h\"\n#include \"cpr/payload.h\"\n#include \"cpr/proxies.h\"\n#include \"cpr/proxyauth.h\"\n#include \"cpr/range.h\"\n#include \"cpr/redirect.h\"\n#include \"cpr/reserve_size.h\"\n#include \"cpr/resolve.h\"\n#include \"cpr/response.h\"\n#include \"cpr/sse.h\"\n#include \"cpr/ssl_options.h\"\n#include \"cpr/timeout.h\"\n#include \"cpr/unix_socket.h\"\n#include \"cpr/user_agent.h\"\n#include \"cpr/util.h\"\n#include \"cpr/verbose.h\"\n\nnamespace cpr {\n\nusing AsyncResponse = AsyncWrapper<Response>;\nusing Content = std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::BodyView, cpr::Multipart>;\n\nclass Interceptor;\nclass MultiPerform;\n\nclass Session : public std::enable_shared_from_this<Session> {\n  public:\n    Session();\n    Session(const Session& other) = delete;\n    Session(Session&& old) = delete;\n\n    ~Session() = default;\n\n    Session& operator=(Session&& old) noexcept = delete;\n    Session& operator=(const Session& other) = delete;\n\n    void SetUrl(const Url& url);\n    void SetParameters(const Parameters& parameters);\n    void SetParameters(Parameters&& parameters);\n    void SetHeader(const Header& header);\n    void UpdateHeader(const Header& header);\n    [[nodiscard]] Header& GetHeader();\n    [[nodiscard]] const Header& GetHeader() const;\n    void SetTimeout(const Timeout& timeout);\n    void SetConnectTimeout(const ConnectTimeout& timeout);\n    void SetConnectionPool(const ConnectionPool& pool);\n    void SetAuth(const Authentication& auth);\n// Only supported with libcurl >= 7.61.0.\n// As an alternative use SetHeader and add the token manually.\n#if LIBCURL_VERSION_NUM >= 0x073D00\n    void SetBearer(const Bearer& token);\n#endif\n    void SetUserAgent(const UserAgent& ua);\n    void SetPayload(Payload&& payload);\n    void SetPayload(const Payload& payload);\n    void SetProxies(Proxies&& proxies);\n    void SetProxies(const Proxies& proxies);\n    void SetProxyAuth(ProxyAuthentication&& proxy_auth);\n    void SetProxyAuth(const ProxyAuthentication& proxy_auth);\n    void SetMultipart(Multipart&& multipart);\n    void SetMultipart(const Multipart& multipart);\n    void SetRedirect(const Redirect& redirect);\n    void SetCookies(const Cookies& cookies);\n    void SetBody(Body&& body);\n    void SetBody(const Body& body);\n    void SetBodyView(BodyView body);\n    void SetLowSpeed(const LowSpeed& low_speed);\n    void SetVerifySsl(const VerifySsl& verify);\n    void SetUnixSocket(const UnixSocket& unix_socket);\n    void SetSslOptions(const SslOptions& options);\n    void SetReadCallback(const ReadCallback& read);\n    void SetHeaderCallback(const HeaderCallback& header);\n    void SetWriteCallback(const WriteCallback& write);\n    void SetProgressCallback(const ProgressCallback& progress);\n    void SetDebugCallback(const DebugCallback& debug);\n    void SetServerSentEventCallback(const ServerSentEventCallback& sse);\n    void SetVerbose(const Verbose& verbose);\n    void SetInterface(const Interface& iface);\n    void SetLocalPort(const LocalPort& local_port);\n    void SetLocalPortRange(const LocalPortRange& local_port_range);\n    void SetHttpVersion(const HttpVersion& version);\n    void SetRange(const Range& range);\n    void SetResolve(const Resolve& resolve);\n    void SetResolves(const std::vector<Resolve>& resolves);\n    void SetMultiRange(const MultiRange& multi_range);\n    void SetReserveSize(const ReserveSize& reserve_size);\n    void SetAcceptEncoding(const AcceptEncoding& accept_encoding);\n    void SetAcceptEncoding(AcceptEncoding&& accept_encoding);\n    void SetLimitRate(const LimitRate& limit_rate);\n\n    /**\n     * Returns a reference to the content sent in previous request.\n     **/\n    [[nodiscard]] const Content& GetContent() const;\n\n    /**\n     * Removes the content sent in previous request from internal state, so it will not be sent with the next request.\n     * Call this before doing a request that is specified not to send a body, e.g. GET.\n     **/\n    void RemoveContent();\n\n    // For cancellable requests\n    void SetCancellationParam(std::shared_ptr<std::atomic_bool> param);\n\n    // Used in templated functions\n    void SetOption(const Url& url);\n    void SetOption(const Parameters& parameters);\n    void SetOption(Parameters&& parameters);\n    void SetOption(const Header& header);\n    void SetOption(const Timeout& timeout);\n    void SetOption(const ConnectTimeout& timeout);\n    void SetOption(const Authentication& auth);\n    void SetOption(const ConnectionPool& pool);\n// Only supported with libcurl >= 7.61.0.\n// As an alternative use SetHeader and add the token manually.\n#if LIBCURL_VERSION_NUM >= 0x073D00\n    void SetOption(const Bearer& auth);\n#endif\n    void SetOption(const UserAgent& ua);\n    void SetOption(Payload&& payload);\n    void SetOption(const Payload& payload);\n    void SetOption(const LimitRate& limit_rate);\n    void SetOption(Proxies&& proxies);\n    void SetOption(const Proxies& proxies);\n    void SetOption(ProxyAuthentication&& proxy_auth);\n    void SetOption(const ProxyAuthentication& proxy_auth);\n    void SetOption(Multipart&& multipart);\n    void SetOption(const Multipart& multipart);\n    void SetOption(const Redirect& redirect);\n    void SetOption(const Cookies& cookies);\n    void SetOption(Body&& body);\n    void SetOption(const Body& body);\n    void SetOption(BodyView body);\n    void SetOption(const ReadCallback& read);\n    void SetOption(const HeaderCallback& header);\n    void SetOption(const WriteCallback& write);\n    void SetOption(const ProgressCallback& progress);\n    void SetOption(const DebugCallback& debug);\n    void SetOption(const ServerSentEventCallback& sse);\n    void SetOption(const LowSpeed& low_speed);\n    void SetOption(const VerifySsl& verify);\n    void SetOption(const Verbose& verbose);\n    void SetOption(const UnixSocket& unix_socket);\n    void SetOption(const SslOptions& options);\n    void SetOption(const Interface& iface);\n    void SetOption(const LocalPort& local_port);\n    void SetOption(const LocalPortRange& local_port_range);\n    void SetOption(const HttpVersion& version);\n    void SetOption(const Range& range);\n    void SetOption(const MultiRange& multi_range);\n    void SetOption(const ReserveSize& reserve_size);\n    void SetOption(const AcceptEncoding& accept_encoding);\n    void SetOption(AcceptEncoding&& accept_encoding);\n    void SetOption(const Resolve& resolve);\n    void SetOption(const std::vector<Resolve>& resolves);\n\n    cpr_off_t GetDownloadFileLength();\n    /**\n     * Attempt to preallocate enough memory for specified number of characters in the response string.\n     * Pass 0 to disable this behavior and let the response string be allocated dynamically on demand.\n     *\n     * Example:\n     * cpr::Session session;\n     * session.SetUrl(cpr::Url{\"http://xxx/file\"});\n     * session.ResponseStringReserve(1024 * 512); // Reserve space for at least 1024 * 512 characters\n     * cpr::Response r = session.Get();\n     **/\n    void ResponseStringReserve(size_t size);\n    Response Delete();\n    Response Download(const WriteCallback& write);\n    Response Download(std::ofstream& file);\n    Response Get();\n    Response Head();\n    Response Options();\n    Response Patch();\n    Response Post();\n    Response Put();\n\n    AsyncResponse GetAsync();\n    AsyncResponse DeleteAsync();\n    AsyncResponse DownloadAsync(const WriteCallback& write);\n    AsyncResponse DownloadAsync(std::ofstream& file);\n    AsyncResponse HeadAsync();\n    AsyncResponse OptionsAsync();\n    AsyncResponse PatchAsync();\n    AsyncResponse PostAsync();\n    AsyncResponse PutAsync();\n\n    template <typename Then>\n    auto GetCallback(Then then);\n    template <typename Then>\n    auto PostCallback(Then then);\n    template <typename Then>\n    auto PutCallback(Then then);\n    template <typename Then>\n    auto HeadCallback(Then then);\n    template <typename Then>\n    auto DeleteCallback(Then then);\n    template <typename Then>\n    auto OptionsCallback(Then then);\n    template <typename Then>\n    auto PatchCallback(Then then);\n\n    std::shared_ptr<CurlHolder> GetCurlHolder();\n    std::string GetFullRequestUrl();\n\n    void PrepareDelete();\n    void PrepareGet();\n    void PrepareHead();\n    void PrepareOptions();\n    void PreparePatch();\n    void PreparePost();\n    void PreparePut();\n    void PrepareDownload(const WriteCallback& write);\n    void PrepareDownload(std::ofstream& file);\n    Response Complete(CURLcode curl_error);\n    Response CompleteDownload(CURLcode curl_error);\n\n    void AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor);\n\n    std::shared_ptr<Session> GetSharedPtrFromThis();\n\n  private:\n    // Interceptors should be able to call the private proceed() function\n    friend Interceptor;\n    friend MultiPerform;\n\n\n    bool chunkedTransferEncoding_{false};\n    Content content_{std::monostate{}};\n    std::shared_ptr<CurlHolder> curl_;\n    Url url_;\n    Parameters parameters_;\n    Proxies proxies_;\n    ProxyAuthentication proxyAuth_;\n    Header header_;\n    AcceptEncoding acceptEncoding_;\n\n\n    struct Callbacks {\n        /**\n         * Will be set by the read callback.\n         * Ensures that the \"Transfer-Encoding\" is set to \"chunked\", if not overriden in header_.\n         **/\n        ReadCallback readcb_;\n        HeaderCallback headercb_;\n        WriteCallback writecb_;\n        ProgressCallback progresscb_;\n        DebugCallback debugcb_;\n        CancellationCallback cancellationcb_;\n        ServerSentEventCallback ssecb_;\n    };\n\n    std::unique_ptr<Callbacks> cbs_{std::make_unique<Callbacks>()};\n\n    size_t response_string_reserve_size_{0};\n    std::string response_string_;\n    std::string header_string_;\n    // Container type is required to keep iterator valid on elem insertion. E.g. list but not vector.\n    using InterceptorsContainer = std::list<std::shared_ptr<Interceptor>>;\n    InterceptorsContainer interceptors_;\n    // Currently running interceptor\n    InterceptorsContainer::const_iterator current_interceptor_;\n    // Interceptor within the chain where to start with each repeated request\n    InterceptorsContainer::const_iterator first_interceptor_;\n    bool isUsedInMultiPerform{false};\n    bool isCancellable{false};\n\n#if SUPPORT_SSL_NO_REVOKE\n    bool sslNoRevoke_{false};\n#endif\n\n    Response makeDownloadRequest();\n    Response makeRequest();\n    Response proceed();\n    const std::optional<Response> intercept();\n    /**\n     * Prepares the curl object for a request with everything used by all requests.\n     **/\n    void prepareCommonShared();\n    /**\n     * Prepares the curl object for a request with everything used by all non download related requests.\n     **/\n    void prepareCommon();\n    /**\n     * Prepares the curl object for a request with everything used by the download request.\n     **/\n    void prepareCommonDownload();\n    void prepareHeader();\n    void prepareProxy();\n    CURLcode DoEasyPerform();\n    void prepareBodyPayloadOrMultipart() const;\n    /**\n     * Returns true in case content_ is of type cpr::Body or cpr::Payload.\n     **/\n    [[nodiscard]] bool hasBodyOrPayload() const;\n};\n\ntemplate <typename Then>\nauto Session::GetCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Get()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::PostCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Post()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::PutCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Put()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::HeadCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Head()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::DeleteCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Delete()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::OptionsCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Options()); }, std::move(then));\n}\n\ntemplate <typename Then>\nauto Session::PatchCallback(Then then) {\n    return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Patch()); }, std::move(then));\n}\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/singleton.h",
    "content": "#ifndef CPR_SINGLETON_H\n#define CPR_SINGLETON_H\n\n#include <cassert>\n#include <mutex>\n\n// NOLINTBEGIN(cppcoreguidelines-macro-usage, bugprone-macro-parentheses)\n\n#ifndef CPR_DISABLE_COPY\n#define CPR_DISABLE_COPY(Class)   \\\n    Class(const Class&) = delete; \\\n    Class& operator=(const Class&) = delete;\n#endif\n\n#ifndef CPR_SINGLETON_DECL\n#define CPR_SINGLETON_DECL(Class)    \\\n  public:                            \\\n    static Class* GetInstance();     \\\n    static void ExitInstance();      \\\n                                     \\\n  private:                           \\\n    CPR_DISABLE_COPY(Class)          \\\n    static Class* s_pInstance;       \\\n    static std::once_flag s_getFlag; \\\n    static std::once_flag s_exitFlag;\n#endif\n\n#ifndef CPR_SINGLETON_IMPL\n#define CPR_SINGLETON_IMPL(Class)                                            \\\n    Class* Class::s_pInstance = nullptr;                                     \\\n    std::once_flag Class::s_getFlag{};                                       \\\n    std::once_flag Class::s_exitFlag{};                                      \\\n    Class* Class::GetInstance() {                                            \\\n        std::call_once(Class::s_getFlag, []() { s_pInstance = new Class; }); \\\n        return s_pInstance;                                                  \\\n    }                                                                        \\\n    void Class::ExitInstance() {                                             \\\n        std::call_once(Class::s_exitFlag, []() {                             \\\n            assert(s_pInstance);                                             \\\n            delete s_pInstance;                                              \\\n            s_pInstance = nullptr;                                           \\\n        });                                                                  \\\n    }\n#endif\n\n// NOLINTEND(cppcoreguidelines-macro-usage, bugprone-macro-parentheses)\n\n#endif"
  },
  {
    "path": "include/cpr/sse.h",
    "content": "#ifndef CPR_SSE_H\n#define CPR_SSE_H\n\n#include <cstdint>\n#include <functional>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <utility>\n\nnamespace cpr {\n\n/**\n * Represents a Server-Sent Event (SSE) as defined in the HTML5 specification.\n * https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nstruct ServerSentEvent {\n    /**\n     * The event ID. Can be used to track the last received event and resume from there.\n     */\n    std::optional<std::string> id;\n\n    /**\n     * The event type. If not specified, defaults to \"message\".\n     */\n    std::string event{\"message\"};\n\n    /**\n     * The event data. Multiple data fields are concatenated with newlines.\n     */\n    std::string data;\n\n    /**\n     * The retry time in milliseconds. Used to set the reconnection time.\n     */\n    std::optional<size_t> retry;\n\n    ServerSentEvent() = default;\n};\n\n/**\n * Parser for Server-Sent Events (SSE) streams.\n * This parser handles incoming SSE data according to the HTML5 specification.\n */\nclass ServerSentEventParser {\n  public:\n    ServerSentEventParser() = default;\n\n    /**\n     * Parse incoming SSE data and invoke the callback for each complete event.\n     * @param data The incoming data chunk\n     * @param callback The callback to invoke for each parsed event\n     * @return true to continue receiving data, false to abort\n     */\n    bool parse(std::string_view data, const std::function<bool(ServerSentEvent&&)>& callback);\n\n    /**\n     * Reset the parser state.\n     */\n    void reset();\n\n  private:\n    std::string buffer_;\n    ServerSentEvent current_event_;\n\n    bool processLine(const std::string& line, const std::function<bool(ServerSentEvent&&)>& callback);\n    bool dispatchEvent(const std::function<bool(ServerSentEvent&&)>& callback);\n};\n\n/**\n * Callback for handling Server-Sent Events.\n * The callback receives each parsed SSE event and can return false to abort the connection.\n */\nclass ServerSentEventCallback {\n  public:\n    ServerSentEventCallback() = default;\n    ServerSentEventCallback(std::function<bool(ServerSentEvent&& event, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}\n\n    bool operator()(ServerSentEvent&& event) const {\n        if (!callback) {\n            return true;\n        }\n        return callback(std::move(event), userdata);\n    }\n\n    /**\n     * Internal function used to handle raw data chunks and parse them into SSE events.\n     * This is called by the underlying write callback mechanism.\n     */\n    bool handleData(std::string_view data);\n\n    intptr_t userdata{};\n    std::function<bool(ServerSentEvent&& event, intptr_t userdata)> callback;\n\n  private:\n    ServerSentEventParser parser_;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/ssl_ctx.h",
    "content": "#ifndef CPR_SSL_CTX_H\n#define CPR_SSL_CTX_H\n\n#include \"cpr/ssl_options.h\"\n#include <curl/curl.h>\n\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n\nnamespace cpr {\n\n/**\n * This callback function loads a CA certificate from raw_cert_buf and gets called by libcurl\n * just before the initialization of an SSL connection.\n * The raw_cert_buf argument is set with the CURLOPT_SSL_CTX_DATA option and has to be a nul\n * terminated buffer.\n *\n * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html\n *         https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html\n */\nCURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf);\n\n} // Namespace cpr\n\n#endif\n\n#endif\n"
  },
  {
    "path": "include/cpr/ssl_options.h",
    "content": "#ifndef CPR_SSL_OPTIONS_H\n#define CPR_SSL_OPTIONS_H\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cpr/filesystem.h\"\n#include <curl/curl.h>\n\n#include \"cpr/util.h\"\n#include \"util.h\"\n#include <utility>\n\n#ifndef SUPPORT_ALPN\n#define SUPPORT_ALPN LIBCURL_VERSION_NUM >= 0x072400 // 7.36.0\n#endif\n#ifndef SUPPORT_NPN\n#define SUPPORT_NPN LIBCURL_VERSION_NUM >= 0x072400 && LIBCURL_VERSION_NUM < 0x075600 // 7.36.0 - 7.85.0\n#endif\n\n#ifndef SUPPORT_SSLv2\n#define SUPPORT_SSLv2 LIBCURL_VERSION_NUM <= 0x071300 // 7.19.0\n#endif\n#ifndef SUPPORT_SSLv3\n#define SUPPORT_SSLv3 LIBCURL_VERSION_NUM <= 0x072700 // 7.39.0\n#endif\n#ifndef SUPPORT_TLSv1_0\n#define SUPPORT_TLSv1_0 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0\n#endif\n#ifndef SUPPORT_TLSv1_1\n#define SUPPORT_TLSv1_1 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0\n#endif\n#ifndef SUPPORT_TLSv1_2\n#define SUPPORT_TLSv1_2 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0\n#endif\n#ifndef SUPPORT_TLSv1_3\n#define SUPPORT_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073400 // 7.52.0\n#endif\n#ifndef SUPPORT_MAX_TLS_VERSION\n#define SUPPORT_MAX_TLS_VERSION LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0\n#endif\n#ifndef SUPPORT_MAX_TLSv1_1\n#define SUPPORT_MAX_TLSv1_1 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0\n#endif\n#ifndef SUPPORT_MAX_TLSv1_2\n#define SUPPORT_MAX_TLSv1_2 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0\n#endif\n#ifndef SUPPORT_MAX_TLSv1_3\n#define SUPPORT_MAX_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0\n#endif\n#ifndef SUPPORT_TLSv13_CIPHERS\n#define SUPPORT_TLSv13_CIPHERS LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0\n#endif\n#ifndef SUPPORT_SESSIONID_CACHE\n#define SUPPORT_SESSIONID_CACHE LIBCURL_VERSION_NUM >= 0x071000 // 7.16.0\n#endif\n#ifndef SUPPORT_SSL_FALSESTART\n#define SUPPORT_SSL_FALSESTART LIBCURL_VERSION_NUM >= 0x072A00 // 7.42.0\n#endif\n#ifndef SUPPORT_SSL_NO_REVOKE\n#define SUPPORT_SSL_NO_REVOKE LIBCURL_VERSION_NUM >= 0x072C00 // 7.44.0\n#endif\n#ifndef SUPPORT_CURLOPT_SSLKEY_BLOB\n#define SUPPORT_CURLOPT_SSLKEY_BLOB LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0\n#endif\n#ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n#define SUPPORT_CURLOPT_SSL_CTX_FUNCTION LIBCURL_VERSION_NUM >= 0x070B00 // 7.11.0\n#endif\n#ifndef SUPPORT_CURLOPT_CAINFO_BLOB\n#define SUPPORT_CURLOPT_CAINFO_BLOB LIBCURL_VERSION_NUM >= 0x074D00 // 7.77.0\n#endif\n#ifndef SUPPORT_CURLOPT_SSLCERT_BLOB\n#define SUPPORT_CURLOPT_SSLCERT_BLOB LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0\n#endif\n\nnamespace cpr {\n\nclass VerifySsl {\n  public:\n    VerifySsl() = default;\n    VerifySsl(bool p_verify) : verify(p_verify) {}\n\n    explicit operator bool() const {\n        return verify;\n    }\n\n    bool verify = true;\n};\n\nnamespace ssl {\n\n// set SSL client certificate\nclass CertFile {\n  public:\n    CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}\n\n    virtual ~CertFile() = default;\n\n    const fs::path filename;\n\n    [[nodiscard]] virtual const char* GetCertType() const {\n        return \"PEM\";\n    }\n};\n\nusing PemCert = CertFile;\n\nclass DerCert : public CertFile {\n  public:\n    DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {}\n\n    ~DerCert() override = default;\n\n    [[nodiscard]] const char* GetCertType() const override {\n        return \"DER\";\n    }\n};\n\n\n#if SUPPORT_CURLOPT_SSLCERT_BLOB\nclass CertBlob {\n  public:\n    CertBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}\n\n    virtual ~CertBlob() = default;\n\n    std::string blob;\n\n    [[nodiscard]] virtual const char* GetCertType() const {\n        return \"PEM\";\n    }\n};\n\nusing PemBlob = CertBlob;\n\nclass DerBlob : public CertBlob {\n  public:\n    template <typename BlobType>\n    // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)\n    DerBlob(BlobType&& p_blob) : CertBlob(std::forward<BlobType>(p_blob)) {}\n\n    ~DerBlob() override = default;\n\n    [[nodiscard]] const char* GetCertType() const override {\n        return \"DER\";\n    }\n};\n#endif\n\n// specify private keyfile for TLS and SSL client cert\nclass KeyFile {\n  public:\n    KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}\n\n    template <typename FileType, typename PassType>\n    KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward<FileType>(p_filename)), password(std::move(p_password)) {}\n\n    virtual ~KeyFile() = default;\n\n    fs::path filename;\n    util::SecureString password;\n\n    [[nodiscard]] virtual const char* GetKeyType() const {\n        return \"PEM\";\n    }\n};\n\n#if SUPPORT_CURLOPT_SSLKEY_BLOB\nclass KeyBlob {\n  public:\n    KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}\n\n    template <typename BlobType, typename PassType>\n    KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward<BlobType>(p_blob)), password(std::move(p_password)) {}\n\n    virtual ~KeyBlob() = default;\n\n    std::string blob;\n    util::SecureString password;\n\n    [[nodiscard]] virtual const char* GetKeyType() const {\n        return \"PEM\";\n    }\n};\n#endif\n\nusing PemKey = KeyFile;\n\nclass DerKey : public KeyFile {\n  public:\n    DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {}\n\n    template <typename FileType, typename PassType>\n    DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward<FileType>(p_filename), std::move(p_password)) {}\n\n    ~DerKey() override = default;\n\n    [[nodiscard]] const char* GetKeyType() const override {\n        return \"DER\";\n    }\n};\n\nclass PinnedPublicKey {\n  public:\n    PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {}\n\n    const std::string pinned_public_key;\n};\n\n#if SUPPORT_ALPN\n// This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to\n// use supports it), which can be used to negotiate http2.\nclass ALPN {\n  public:\n    ALPN() = default;\n    ALPN(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = true;\n};\n#endif // SUPPORT_ALPN\n\n#if SUPPORT_NPN\n//  This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to\n//  use supports it), which can be used to negotiate http2.\nclass NPN {\n  public:\n    NPN() = default;\n    NPN(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = true;\n};\n#endif // SUPPORT_NPN\n\n// This option determines whether libcurl verifies that the server cert is for the server it is\n// known as.\nclass VerifyHost {\n  public:\n    VerifyHost() = default;\n    VerifyHost(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = true;\n};\n\n// This option determines whether libcurl verifies the authenticity of the peer's certificate.\nclass VerifyPeer {\n  public:\n    VerifyPeer() = default;\n    VerifyPeer(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = true;\n};\n\n// This option determines whether libcurl verifies the status of the server cert using the\n// \"Certificate Status Request\" TLS extension (aka. OCSP stapling).\nclass VerifyStatus {\n  public:\n    VerifyStatus(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = false;\n};\n\n// TLS v1.0 or later\nstruct TLSv1 {};\n#if SUPPORT_SSLv2\n// SSL v2 (but not SSLv3)\nstruct SSLv2 {};\n#endif\n#if SUPPORT_SSLv3\n// SSL v3 (but not SSLv2)\nstruct SSLv3 {};\n#endif\n#if SUPPORT_TLSv1_0\n// TLS v1.0 or later (Added in 7.34.0)\nstruct TLSv1_0 {};\n#endif\n#if SUPPORT_TLSv1_1\n// TLS v1.1 or later (Added in 7.34.0)\nstruct TLSv1_1 {};\n#endif\n#if SUPPORT_TLSv1_2\n// TLS v1.2 or later (Added in 7.34.0)\nstruct TLSv1_2 {};\n#endif\n#if SUPPORT_TLSv1_3\n// TLS v1.3 or later (Added in 7.52.0)\nstruct TLSv1_3 {};\n#endif\n#if SUPPORT_MAX_TLS_VERSION\n// The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL\n// library is used.\nstruct MaxTLSVersion {};\n#endif\n#if SUPPORT_MAX_TLSv1_0\n// The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0)\nstruct MaxTLSv1_0 {};\n#endif\n#if SUPPORT_MAX_TLSv1_1\n// The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0)\nstruct MaxTLSv1_1 {};\n#endif\n#if SUPPORT_MAX_TLSv1_2\n// The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0)\nstruct MaxTLSv1_2 {};\n#endif\n#if SUPPORT_MAX_TLSv1_3\n// The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0)\nstruct MaxTLSv1_3 {};\n#endif\n\n// path to Certificate Authority (CA) bundle\nclass CaInfo {\n  public:\n    CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {}\n\n    fs::path filename;\n};\n\n#if SUPPORT_CURLOPT_CAINFO_BLOB\n// Certificate Authority (CA) bundle as blob\nclass CaInfoBlob {\n  public:\n    CaInfoBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}\n\n    std::string blob;\n};\n#endif\n\n// specify directory holding CA certificates\nclass CaPath {\n  public:\n    CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {}\n\n    fs::path filename;\n};\n\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\nclass CaBuffer {\n  public:\n    CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {}\n\n    const std::string buffer;\n};\n#endif\n\n// specify a Certificate Revocation List file\nclass Crl {\n  public:\n    Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {}\n\n    fs::path filename;\n};\n\n// specify ciphers to use for TLS\nclass Ciphers {\n  public:\n    Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}\n\n    std::string ciphers;\n};\n\n#if SUPPORT_TLSv13_CIPHERS\n// specify ciphers suites to use for TLS 1.3\nclass TLS13_Ciphers {\n  public:\n    TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}\n\n    std::string ciphers;\n};\n#endif\n\n#if SUPPORT_SESSIONID_CACHE\n// enable/disable use of the SSL session-ID cache\nclass SessionIdCache {\n  public:\n    SessionIdCache() = default;\n    SessionIdCache(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = true;\n};\n#endif\n\n#if SUPPORT_SSL_FALSESTART\nclass SslFastStart {\n  public:\n    SslFastStart() = default;\n    SslFastStart(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = false;\n};\n#endif\n\nclass NoRevoke {\n  public:\n    NoRevoke() = default;\n    NoRevoke(bool p_enabled) : enabled(p_enabled) {}\n\n    explicit operator bool() const {\n        return enabled;\n    }\n\n    bool enabled = false;\n};\n\n} // namespace ssl\n\nstruct SslOptions {\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string cert_file;\n#if SUPPORT_CURLOPT_SSLCERT_BLOB\n    util::SecureString cert_blob;\n#endif\n    std::string cert_type;\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string key_file;\n#if SUPPORT_CURLOPT_SSLKEY_BLOB\n    util::SecureString key_blob;\n#endif\n    std::string key_type;\n    util::SecureString key_pass;\n    std::string pinned_public_key;\n#if SUPPORT_ALPN\n    bool enable_alpn = true;\n#endif // SUPPORT_ALPN\n#if SUPPORT_NPN\n    bool enable_npn = true;\n#endif // SUPPORT_ALPN\n    bool verify_host = true;\n    bool verify_peer = true;\n    bool verify_status = false;\n    int ssl_version = CURL_SSLVERSION_DEFAULT;\n#if SUPPORT_SSL_NO_REVOKE\n    bool ssl_no_revoke = false;\n#endif\n#if SUPPORT_MAX_TLS_VERSION\n    int max_version = CURL_SSLVERSION_MAX_DEFAULT;\n#endif\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string ca_info;\n#if SUPPORT_CURLOPT_CAINFO_BLOB\n    std::string ca_info_blob;\n#endif\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string ca_path;\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n    std::string ca_buffer;\n#endif\n    // We don't use fs::path here, as this leads to problems using windows\n    std::string crl_file;\n    std::string ciphers;\n#if SUPPORT_TLSv13_CIPHERS\n    std::string tls13_ciphers;\n#endif\n#if SUPPORT_SESSIONID_CACHE\n    bool session_id_cache = true;\n#endif\n\n    void SetOption(const ssl::CertFile& opt) {\n        cert_file = opt.filename.string();\n        cert_type = opt.GetCertType();\n    }\n#if SUPPORT_CURLOPT_SSLCERT_BLOB\n    void SetOption(const ssl::CertBlob& opt) {\n        cert_blob = opt.blob;\n        cert_type = opt.GetCertType();\n    }\n#endif\n    void SetOption(const ssl::KeyFile& opt) {\n        key_file = opt.filename.string();\n        key_type = opt.GetKeyType();\n        key_pass = opt.password;\n    }\n#if SUPPORT_CURLOPT_SSLKEY_BLOB\n    void SetOption(const ssl::KeyBlob& opt) {\n        key_blob = opt.blob;\n        key_type = opt.GetKeyType();\n        key_pass = opt.password;\n    }\n#endif\n    void SetOption(const ssl::PinnedPublicKey& opt) {\n        pinned_public_key = opt.pinned_public_key;\n    }\n\n#if SUPPORT_ALPN\n    void SetOption(const ssl::ALPN& opt) {\n        enable_alpn = opt.enabled;\n    }\n#endif // SUPPORT_ALPN\n#if SUPPORT_NPN\n    void SetOption(const ssl::NPN& opt) {\n        enable_npn = opt.enabled;\n    }\n#endif // SUPPORT_NPN\n    void SetOption(const ssl::VerifyHost& opt) {\n        verify_host = opt.enabled;\n    }\n    void SetOption(const ssl::VerifyPeer& opt) {\n        verify_peer = opt.enabled;\n    }\n    void SetOption(const ssl::VerifyStatus& opt) {\n        verify_status = opt.enabled;\n    }\n    void SetOption(const ssl::TLSv1& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_TLSv1;\n    }\n#if SUPPORT_SSL_NO_REVOKE\n    void SetOption(const ssl::NoRevoke& opt) {\n        ssl_no_revoke = opt.enabled;\n    }\n#endif\n#if SUPPORT_SSLv2\n    void SetOption(const ssl::SSLv2& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_SSLv2;\n    }\n#endif\n#if SUPPORT_SSLv3\n    void SetOption(const ssl::SSLv3& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_SSLv3;\n    }\n#endif\n#if SUPPORT_TLSv1_0\n    void SetOption(const ssl::TLSv1_0& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_TLSv1_0;\n    }\n#endif\n#if SUPPORT_TLSv1_1\n    void SetOption(const ssl::TLSv1_1& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_TLSv1_1;\n    }\n#endif\n#if SUPPORT_TLSv1_2\n    void SetOption(const ssl::TLSv1_2& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_TLSv1_2;\n    }\n#endif\n#if SUPPORT_TLSv1_3\n    void SetOption(const ssl::TLSv1_3& /*opt*/) {\n        ssl_version = CURL_SSLVERSION_TLSv1_3;\n    }\n#endif\n#if SUPPORT_MAX_TLS_VERSION\n    void SetOption(const ssl::MaxTLSVersion& /*opt*/) {\n        max_version = CURL_SSLVERSION_DEFAULT;\n    }\n#endif\n#if SUPPORT_MAX_TLSv1_0\n    void SetOption(const ssl::MaxTLSv1_0& opt) {\n        max_version = CURL_SSLVERSION_MAX_TLSv1_0;\n    }\n#endif\n#if SUPPORT_MAX_TLSv1_1\n    void SetOption(const ssl::MaxTLSv1_1& /*opt*/) {\n        max_version = CURL_SSLVERSION_MAX_TLSv1_1;\n    }\n#endif\n#if SUPPORT_MAX_TLSv1_2\n    void SetOption(const ssl::MaxTLSv1_2& /*opt*/) {\n        max_version = CURL_SSLVERSION_MAX_TLSv1_2;\n    }\n#endif\n#if SUPPORT_MAX_TLSv1_3\n    void SetOption(const ssl::MaxTLSv1_3& /*opt*/) {\n        max_version = CURL_SSLVERSION_MAX_TLSv1_3;\n    }\n#endif\n    void SetOption(const ssl::CaInfo& opt) {\n        ca_info = opt.filename.string();\n    }\n#if SUPPORT_CURLOPT_CAINFO_BLOB\n    void SetOption(const ssl::CaInfoBlob& opt) {\n        ca_info_blob = opt.blob;\n    }\n#endif\n    void SetOption(const ssl::CaPath& opt) {\n        ca_path = opt.filename.string();\n    }\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\n    void SetOption(const ssl::CaBuffer& opt) {\n        ca_buffer = opt.buffer;\n    }\n#endif\n    void SetOption(const ssl::Crl& opt) {\n        crl_file = opt.filename.string();\n    }\n    void SetOption(const ssl::Ciphers& opt) {\n        ciphers = opt.ciphers;\n    }\n#if SUPPORT_TLSv13_CIPHERS\n    void SetOption(const ssl::TLS13_Ciphers& opt) {\n        tls13_ciphers = opt.ciphers;\n    }\n#endif\n#if SUPPORT_SESSIONID_CACHE\n    void SetOption(const ssl::SessionIdCache& opt) {\n        session_id_cache = opt.enabled;\n    }\n#endif\n};\n\nnamespace priv {\n\ntemplate <typename T>\nvoid set_ssl_option(SslOptions& opts, T&& t) {\n    opts.SetOption(std::forward<T>(t));\n}\n\ntemplate <typename T, typename... Ts>\nvoid set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) {\n    set_ssl_option(opts, std::forward<T>(t));\n    set_ssl_option(opts, std::forward<Ts>(ts)...);\n}\n\n} // namespace priv\n\ntemplate <typename... Ts>\nSslOptions Ssl(Ts&&... ts) {\n    SslOptions opts;\n    priv::set_ssl_option(opts, std::forward<Ts>(ts)...);\n    return opts;\n}\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/status_codes.h",
    "content": "#ifndef CPR_STATUS_CODES\n#define CPR_STATUS_CODES\nnamespace cpr {\nnamespace status {\n// Information responses\nconstexpr long HTTP_CONTINUE = 100;\nconstexpr long HTTP_SWITCHING_PROTOCOL = 101;\nconstexpr long HTTP_PROCESSING = 102;\nconstexpr long HTTP_EARLY_HINTS = 103;\n// Successful responses\nconstexpr long HTTP_OK = 200;\nconstexpr long HTTP_CREATED = 201;\nconstexpr long HTTP_ACCEPTED = 202;\nconstexpr long HTTP_NON_AUTHORITATIVE_INFORMATION = 203;\nconstexpr long HTTP_NO_CONTENT = 204;\nconstexpr long HTTP_RESET_CONTENT = 205;\nconstexpr long HTTP_PARTIAL_CONTENT = 206;\nconstexpr long HTTP_MULTI_STATUS = 207;\nconstexpr long HTTP_ALREADY_REPORTED = 208;\nconstexpr long HTTP_IM_USED = 226;\n// Redirection messages\nconstexpr long HTTP_MULTIPLE_CHOICE = 300;\nconstexpr long HTTP_MOVED_PERMANENTLY = 301;\nconstexpr long HTTP_FOUND = 302;\nconstexpr long HTTP_SEE_OTHER = 303;\nconstexpr long HTTP_NOT_MODIFIED = 304;\nconstexpr long HTTP_USE_PROXY = 305;\nconstexpr long HTTP_UNUSED = 306;\nconstexpr long HTTP_TEMPORARY_REDIRECT = 307;\nconstexpr long HTTP_PERMANENT_REDIRECT = 308;\n// Client error responses\nconstexpr long HTTP_BAD_REQUEST = 400;\nconstexpr long HTTP_UNAUTHORIZED = 401;\nconstexpr long HTTP_PAYMENT_REQUIRED = 402;\nconstexpr long HTTP_FORBIDDEN = 403;\nconstexpr long HTTP_NOT_FOUND = 404;\nconstexpr long HTTP_METHOD_NOT_ALLOWED = 405;\nconstexpr long HTTP_NOT_ACCEPTABLE = 406;\nconstexpr long HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;\nconstexpr long HTTP_REQUEST_TIMEOUT = 408;\nconstexpr long HTTP_CONFLICT = 409;\nconstexpr long HTTP_GONE = 410;\nconstexpr long HTTP_LENGTH_REQUIRED = 411;\nconstexpr long HTTP_PRECONDITION_FAILED = 412;\nconstexpr long HTTP_PAYLOAD_TOO_LARGE = 413;\nconstexpr long HTTP_URI_TOO_LONG = 414;\nconstexpr long HTTP_UNSUPPORTED_MEDIA_TYPE = 415;\nconstexpr long HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;\nconstexpr long HTTP_EXPECTATION_FAILED = 417;\nconstexpr long HTTP_IM_A_TEAPOT = 418;\nconstexpr long HTTP_MISDIRECTED_REQUEST = 421;\nconstexpr long HTTP_UNPROCESSABLE_ENTITY = 422;\nconstexpr long HTTP_LOCKED = 423;\nconstexpr long HTTP_FAILED_DEPENDENCY = 424;\nconstexpr long HTTP_TOO_EARLY = 425;\nconstexpr long HTTP_UPGRADE_REQUIRED = 426;\nconstexpr long HTTP_PRECONDITION_REQUIRED = 428;\nconstexpr long HTTP_TOO_MANY_REQUESTS = 429;\nconstexpr long HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;\nconstexpr long HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;\n// Server response errors\nconstexpr long HTTP_INTERNAL_SERVER_ERROR = 500;\nconstexpr long HTTP_NOT_IMPLEMENTED = 501;\nconstexpr long HTTP_BAD_GATEWAY = 502;\nconstexpr long HTTP_SERVICE_UNAVAILABLE = 503;\nconstexpr long HTTP_GATEWAY_TIMEOUT = 504;\nconstexpr long HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;\nconstexpr long HTTP_VARIANT_ALSO_NEGOTIATES = 506;\nconstexpr long HTTP_INSUFFICIENT_STORAGE = 507;\nconstexpr long HTTP_LOOP_DETECTED = 508;\nconstexpr long HTTP_NOT_EXTENDED = 510;\nconstexpr long HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;\n\nconstexpr long INFO_CODE_OFFSET = 100;\nconstexpr long SUCCESS_CODE_OFFSET = 200;\nconstexpr long REDIRECT_CODE_OFFSET = 300;\nconstexpr long CLIENT_ERROR_CODE_OFFSET = 400;\nconstexpr long SERVER_ERROR_CODE_OFFSET = 500;\nconstexpr long MISC_CODE_OFFSET = 600;\n\nconstexpr bool is_informational(const long code) {\n    return (code >= INFO_CODE_OFFSET && code < SUCCESS_CODE_OFFSET);\n}\nconstexpr bool is_success(const long code) {\n    return (code >= SUCCESS_CODE_OFFSET && code < REDIRECT_CODE_OFFSET);\n}\nconstexpr bool is_redirect(const long code) {\n    return (code >= REDIRECT_CODE_OFFSET && code < CLIENT_ERROR_CODE_OFFSET);\n}\nconstexpr bool is_client_error(const long code) {\n    return (code >= CLIENT_ERROR_CODE_OFFSET && code < SERVER_ERROR_CODE_OFFSET);\n}\nconstexpr bool is_server_error(const long code) {\n    return (code >= SERVER_ERROR_CODE_OFFSET && code < MISC_CODE_OFFSET);\n}\n\n} // namespace status\n} // namespace cpr\n#endif\n"
  },
  {
    "path": "include/cpr/threadpool.h",
    "content": "#ifndef CPR_THREADPOOL_H\n#define CPR_THREADPOOL_H\n\n#include <atomic>\n#include <chrono>\n#include <condition_variable>\n#include <cstdint>\n#include <functional>\n#include <future>\n#include <list>\n#include <memory>\n#include <mutex>\n#include <queue>\n#include <thread>\n#include <utility>\n\n#define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()\n\nconstexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1;\nconstexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{250};\n\nnamespace cpr {\n\nclass ThreadPool {\n  public:\n    using Task = std::function<void()>;\n\n    explicit ThreadPool(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME);\n    ThreadPool(const ThreadPool& other) = delete;\n    ThreadPool(ThreadPool&& old) = delete;\n\n    virtual ~ThreadPool();\n\n    ThreadPool& operator=(const ThreadPool& other) = delete;\n    ThreadPool& operator=(ThreadPool&& old) = delete;\n\n    void SetMinThreadNum(size_t min_threads) {\n        min_thread_num = min_threads;\n    }\n\n    void SetMaxThreadNum(size_t max_threads) {\n        max_thread_num = max_threads;\n    }\n\n    void SetMaxIdleTime(std::chrono::milliseconds ms) {\n        max_idle_time = ms;\n    }\n\n    size_t GetCurrentThreadNum() {\n        return cur_thread_num;\n    }\n\n    size_t GetIdleThreadNum() {\n        return idle_thread_num;\n    }\n\n    [[nodiscard]] bool IsStarted() const {\n        return status != Status::STOP;\n    }\n\n    [[nodiscard]] bool IsStopped() const {\n        return status == Status::STOP;\n    }\n\n    int Start(size_t start_threads = 0);\n    int Stop();\n    int Pause();\n    int Resume();\n    void Wait();\n\n    /**\n     * Return a future, calling future.get() will wait task done and return RetType.\n     * Submit(fn, args...)\n     * Submit(std::bind(&Class::mem_fn, &obj))\n     * Submit(std::mem_fn(&Class::mem_fn, &obj))\n     **/\n    template <class Fn, class... Args>\n    auto Submit(Fn&& fn, Args&&... args) {\n        if (status == Status::STOP) {\n            Start();\n        }\n        if (idle_thread_num <= 0 && cur_thread_num < max_thread_num) {\n            CreateThread();\n        }\n        using RetType = decltype(fn(args...));\n        auto task = std::make_shared<std::packaged_task<RetType()>>([fn = std::forward<Fn>(fn), args...]() mutable { return std::invoke(fn, args...); });\n        std::future<RetType> future = task->get_future();\n        {\n            std::scoped_lock const locker(task_mutex);\n            tasks.emplace([task] { (*task)(); });\n        }\n\n        task_cond.notify_one();\n        return future;\n    }\n\n  private:\n    bool CreateThread();\n    void AddThread(const std::shared_ptr<std::thread>& thread);\n    void DelThread(std::thread::id id);\n\n  public:\n    size_t min_thread_num;\n    size_t max_thread_num;\n    std::chrono::milliseconds max_idle_time;\n\n  private:\n    enum class Status : uint8_t {\n        STOP,\n        RUNNING,\n        PAUSE,\n    };\n\n    struct ThreadData {\n        std::shared_ptr<std::thread> thread;\n        std::thread::id id;\n        Status status{Status::STOP};\n        std::chrono::steady_clock::time_point start_time;\n        std::chrono::steady_clock::time_point stop_time;\n    };\n\n    std::atomic<Status> status{Status::STOP};\n    std::condition_variable status_wait_cond;\n    std::mutex status_wait_mutex;\n\n    std::atomic<size_t> cur_thread_num{0};\n    std::atomic<size_t> idle_thread_num{0};\n\n    std::list<ThreadData> threads;\n    std::mutex thread_mutex;\n\n    std::queue<Task> tasks;\n    std::mutex task_mutex;\n    std::condition_variable task_cond;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/timeout.h",
    "content": "#ifndef CPR_TIMEOUT_H\n#define CPR_TIMEOUT_H\n\n#include <chrono>\n#include <cstdint>\n\nnamespace cpr {\n\nclass Timeout {\n  public:\n    // Template constructor to accept any chrono duration type and convert it to milliseconds\n    template <typename Rep, typename Period>\n    Timeout(const std::chrono::duration<Rep, Period>& duration) : ms{std::chrono::duration_cast<std::chrono::milliseconds>(duration)} {}\n\n    Timeout(const std::int32_t& milliseconds) : Timeout{std::chrono::milliseconds(milliseconds)} {}\n\n    // No way around since curl uses a long here.\n    // NOLINTNEXTLINE(google-runtime-int)\n    [[nodiscard]] long Milliseconds() const;\n\n    std::chrono::milliseconds ms;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/unix_socket.h",
    "content": "#ifndef CPR_UNIX_SOCKET_H\n#define CPR_UNIX_SOCKET_H\n\n#include <string>\n\nnamespace cpr {\n\nclass UnixSocket {\n  public:\n    UnixSocket(std::string unix_socket) : unix_socket_(std::move(unix_socket)) {}\n\n    [[nodiscard]] const char* GetUnixSocketString() const noexcept;\n\n  private:\n    const std::string unix_socket_;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/user_agent.h",
    "content": "#ifndef CPR_USER_AGENT_H\n#define CPR_USER_AGENT_H\n\n#include <initializer_list>\n#include <string>\n\n#include \"cpr/cprtypes.h\"\n\nnamespace cpr {\nclass UserAgent : public StringHolder<UserAgent> {\n  public:\n    UserAgent() = default;\n    UserAgent(std::string useragent) : StringHolder<UserAgent>(std::move(useragent)) {}\n    UserAgent(std::string_view useragent) : StringHolder<UserAgent>(useragent) {}\n    UserAgent(const char* useragent) : StringHolder<UserAgent>(useragent) {}\n    UserAgent(const char* str, size_t len) : StringHolder<UserAgent>(str, len) {}\n    UserAgent(const std::initializer_list<std::string> args) : StringHolder<UserAgent>(args) {}\n    UserAgent(const UserAgent& other) = default;\n    UserAgent(UserAgent&& old) noexcept = default;\n    ~UserAgent() override = default;\n\n    UserAgent& operator=(UserAgent&& old) noexcept = default;\n    UserAgent& operator=(const UserAgent& other) = default;\n};\n\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "include/cpr/util.h",
    "content": "#ifndef CPR_UTIL_H\n#define CPR_UTIL_H\n\n#include <fstream>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cpr/callback.h\"\n#include \"cpr/cookies.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/secure_string.h\"\n#include \"cpr/sse.h\"\n\nnamespace cpr::util {\n\nHeader parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr);\nCookies parseCookies(curl_slist* raw_cookies);\nsize_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read);\nsize_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header);\nsize_t writeFunction(char* ptr, size_t size, size_t nmemb, void* data);\nsize_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file);\nsize_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write);\nsize_t writeSSEFunction(char* ptr, size_t size, size_t nmemb, ServerSentEventCallback* sse);\n\ntemplate <typename T = ProgressCallback>\nint progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) {\n    const int cancel_retval{1};\n#ifdef CURL_PROGRESSFUNC_CONTINUE // Not always defined. Ref: https://github.com/libcpr/cpr/issues/932\n    static_assert(cancel_retval != CURL_PROGRESSFUNC_CONTINUE);\n#endif // CURL_PROGRESSFUNC_CONTINUE\n    return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : cancel_retval;\n}\nint debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug);\nstd::vector<std::string> split(const std::string& to_split, char delimiter);\nutil::SecureString urlEncode(std::string_view s);\nutil::SecureString urlDecode(std::string_view s);\n\nbool isTrue(const std::string& s);\n\n/**\n * Parses the given std::string into time_t (unix ms).\n * This parsing happens time_t size agnostic since time_t does not use the same underlying type on all systems/compilers.\n **/\ntime_t sTimestampToT(const std::string& /*st*/);\n\n} // namespace cpr::util\n\n#endif\n"
  },
  {
    "path": "include/cpr/verbose.h",
    "content": "#ifndef CPR_VERBOSE_H_\n#define CPR_VERBOSE_H_\n\nnamespace cpr {\n\nclass Verbose {\n  public:\n    Verbose() = default;\n    Verbose(const bool p_verbose) : verbose{p_verbose} {}\n\n    bool verbose = true;\n};\n\n} // namespace cpr\n\n\n#endif /* CPR_VERBOSE_H_ */\n"
  },
  {
    "path": "nuget/build/native/libcpr.props",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" ToolsVersion=\"15.0\">\n  <PropertyGroup>\n    <LibraryType Condition=\"'$(Configuration)'=='Debug'\">mdd</LibraryType>\n    <LibraryType Condition=\"'$(Configuration)'=='Release'\">md</LibraryType>\n  </PropertyGroup>\n  <ItemGroup>\n    <CprLibs Include=\"$(MSBuildThisFileDirectory)\\$(Platform)\\$(Configuration)\\lib\\*.lib\" />\n  </ItemGroup>\n  <ItemGroup>\n    <CprDlls Include=\"$(MSBuildThisFileDirectory)\\$(Platform)\\$(Configuration)\\bin\\*.dll\" />\n    <None Include=\"@(CprDlls)\">\n      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n  <PropertyGroup>\n    <CprLibraries>@(CprLibs)</CprLibraries>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <Link>\n      <AdditionalDependencies>$(CprLibraries);%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n</Project>"
  },
  {
    "path": "nuget/build/native/libcpr.targets",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\"\n  xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Target Name=\"PlatformCheck\" BeforeTargets=\"InjectReference\" Condition=\"('$(Platform)' != 'x86') AND ('$(Platform)' != 'x64')\">\n    <Error Text=\"$(MSBuildThisFileName) does not work correctly on this platform: '$(Platform)'. You need to specify platform x86 or x64.\" />\n  </Target>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\\$(Platform)\\$(Configuration)\\include\\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n  </ItemDefinitionGroup>\n</Project>"
  },
  {
    "path": "nuget/libcpr.nuspec",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd\">\n  <metadata>\n    <id>libcpr</id>\n    <version>$VERSION$</version>\n    <title>C++ Requests: Curl for People</title>\n    <authors>Fabian Sauter</authors>\n    <owners>Fabian Sauter, Kilian Traub, many other libcpr contributors</owners>\n    <requireLicenseAcceptance>false</requireLicenseAcceptance>\n    <license type=\"expression\">MIT</license>\n    <icon>resources/cpr.png</icon>\n    <readme>README.md</readme>\n    <projectUrl>https://github.com/libcpr</projectUrl>\n    <description>C++ Requests: Curl for People, a spiritual port of Python Requests.</description>\n    <tags>Native, native</tags>\n    <language>english</language>\n    <repository type=\"git\" url=\"https://github.com/libcpr/cpr\" branch=\"master\" commit=\"$COMMIT_HASH$\" />\n  </metadata>\n</package>"
  },
  {
    "path": "package-build/build-package.sh",
    "content": "#!/bin/bash\n\nSRC_DIR=$1\n\nLIB='libcpr'\n\nLIB_DIR=\"${LIB}_${VERSION}\"\nDEBIAN_DIR=\"${LIB_DIR}/debian\"\n\nARCHIVE_NAME=\"$LIB_DIR.orig.tar.gz\"\n\necho -e \"Preparing tar archive and directory\\n\"\ncp -r $SRC_DIR $LIB_DIR\n\ntar --exclude-vcs -czf $ARCHIVE_NAME $LIB_DIR\ntar -xzf $ARCHIVE_NAME\n\ncd $LIB_DIR\n\necho -e \"\\n\\nCopying prepared debian files to directory\\n\"\nmkdir debian\ncp -r package-build/debian-libcpr/* debian/\nsed -i \"s/\\%VERSION/$VERSION/g\" debian/changelog\nsed -i \"s/\\%DATE/$(date -R)/g\" debian/changelog\n\necho -e \"\\n\\nCalling debuild\\n\"\ndebuild\n"
  },
  {
    "path": "package-build/debian-libcpr/README.Debian",
    "content": "libcpr for Debian\n\nA package of the libcpr library.\n\n -- Philip Saendig <philip.saendig@gmail.com>  Tue, 24 May 2022 10:37:24 +0200\n"
  },
  {
    "path": "package-build/debian-libcpr/changelog",
    "content": "libcpr (%VERSION-1) UNRELEASED; urgency=low\n\n  [ Philip Saendig ]\n  * First package of libcpr %VERSION for debian.\n\n -- Philip Saendig <deb@libcpr.org>  %DATE\n"
  },
  {
    "path": "package-build/debian-libcpr/control",
    "content": "Source: libcpr\nSection: libs\nPriority: optional\nMaintainer: Philip Saendig <philip.saendig@gmail.com>\nBuild-Depends:\n debhelper-compat (= 12),\n cmake,\n libcurl4-openssl-dev,\n libssl-dev,\nStandards-Version: 4.5.0\nHomepage: https://github.com/libcpr/cpr\n\nPackage: libcpr-dev\nArchitecture: any\nMulti-Arch: same\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${misc:Depends}, ${shlibs:Depends}, libcpr1\nDescription: C++ wrapper around the libcurl library - development kit\n This package contains the header files and development\n libraries of cpr, Curl for People.\n .\n The project is inspried by the Python Request project.\n Using the more expressive language facilities of C++11,\n it captures the essence of making network calls into a\n few concise idioms.\n\nPackage: libcpr1\nArchitecture: any\nMulti-Arch: same\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${misc:Depends}, ${shlibs:Depends},\nDescription: C++ wrapper around the libcurl library - runtime library\n This package contains the runtime, shared library of cpr,\n Curl for People.\n .\n The project is inspried by the Python Request project.\n Using the more expressive language facilities of C++11,\n it captures the essence of making network calls into a\n few concise idioms.\n"
  },
  {
    "path": "package-build/debian-libcpr/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: libcpr\nSource: https://github.com/libcpr/cpr\n\nFiles:     .clang-format\n           .clang-tidy\n           .github/*\n           CMakeLists.txt\n           CODE_OF_CONDUCT.md\n           CONTRIBUTING.md\n           CppCheckSuppressions.txt\n           README.md\n           cmake/*\n           cpr-config.cmake\n           cpr/*\n           include/*\n           nuget/*\n           package-build/*\n           debian/*\nCopyright: 2017-2021 Huu Nguyen\n           2022 libcpr and many other contributors\nLicense:   Expat\n MIT License\n .\n Copyright (c) 2017-2021 Huu Nguyen\n Copyright (c) 2022 libcpr and many other contributors\n .\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n .\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n .\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n\nFiles:     test/*\nCopyright: 2022 libcpr and many other contributors\nLicense:   GPL-3\n On Debian systems, the full text of the GNU General Public\n License version 3 can be found in the file\n `/usr/share/common-licenses/GPL-3'.\n\n"
  },
  {
    "path": "package-build/debian-libcpr/libcpr-dev.install",
    "content": "usr/include\nusr/lib/*/*.so\nusr/lib/*/cmake\n"
  },
  {
    "path": "package-build/debian-libcpr/libcpr1.install",
    "content": "usr/lib/*/*.so.*\n"
  },
  {
    "path": "package-build/debian-libcpr/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DEB_BUILD_MAINT_OPTIONS = hardening=+all\nexport DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic\nexport DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed\n\n%:\n\tdh $@\n\noverride_dh_auto_configure:\n\tdh_auto_configure -- \\\n\t\t-DCMAKE_LIBRARY_ARCHITECTURE=\"$(DEB_TARGET_MULTIARCH)\" -DCMAKE_BUILD_TYPE=Release \\\n\t\t-DCPR_USE_SYSTEM_CURL=ON -DCURL_ZLIB=OFF -DBUILD_SHARED_LIBS=ON\n\n"
  },
  {
    "path": "package-build/debian-libcpr/source/format",
    "content": "3.0 (quilt)\n"
  },
  {
    "path": "scripts/check_clang_format.sh",
    "content": "#!/bin/bash\n\n# Based on: https://gist.github.com/leilee/1d0915a583f8f29414cc21cd86e7151b\n# Checks if all files are formatted based on the clang-format formatting rules.\n# Execute as follows:\n# ./scripts/check_clang_format.sh tests src\n\nprintf \"📑  Checking if your code fulfills all clang-format rules...\\n\"\n\nRET_CODE=0\n\nfunction format() {\n    for f in $(find $@ -name '*.h' -or -name '*.hpp' -or -name '*.c' -or -name '*.cpp'); do \n        clang-format -i --dry-run --Werror --style=file ${f};\n        ret=$?\n        if [ $ret -ne 0 ]; then\n            RET_CODE=$ret\n        fi\n    done\n\n    echo \"~~~ $@ directory checked ~~~\";\n}\n\n# Check all of the arguments first to make sure they're all directories\nfor dir in \"$@\"; do\n    if [ ! -d \"${dir}\" ]; then\n        echo \"${dir} is not a directory\";\n    else\n        format ${dir};\n    fi\ndone\n\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nNC='\\033[0m'\n\nif [ $RET_CODE -eq 0 ]; then\n    printf \"✅ ${GREEN}Everything up to standard :party: ${NC}\\n\"\nelse\n    printf \"❌ ${RED}Not up to formatting standard :sad_face: ${NC}\\n\"\n    echo \"Try running run_clang_format.sh to format all files.\"\nfi\n\nexit $RET_CODE"
  },
  {
    "path": "scripts/delete_build_dir.sh",
    "content": "#!/bin/bash\n\nprintf \"🗑️  Clearing your build directory...\\n\"\nrm -rf build/*\n\nGREEN='\\033[0;32m'\nNC='\\033[0m'\n\nprintf \"✅ ${GREEN}Done. Build directory deleted.${NC}\\n\""
  },
  {
    "path": "scripts/run_clang_format.sh",
    "content": "#!/bin/bash\n\n# Based on: https://gist.github.com/leilee/1d0915a583f8f29414cc21cd86e7151b\n# Run from the project root directory as follows:\n# ./scripts/run_clang_format.sh tests src\n\nprintf \"📝  Running clang-format...\\n\"\n\nfunction format() {\n    for f in $(find $@ -name '*.h' -or -name '*.hpp' -or -name '*.c' -or -name '*.cpp'); do \n        echo \"format ${f}\";\n        clang-format -i --style=file ${f};\n    done\n\n    echo \"~~~ $@ directory formatted ~~~\";\n}\n\n# Check all of the arguments first to make sure they're all directories\nfor dir in \"$@\"; do\n    if [ ! -d \"${dir}\" ]; then\n        echo \"${dir} is not a directory\";\n    else\n        format ${dir};\n    fi\ndone\n\nGREEN='\\033[0;32m'\nNC='\\033[0m'\n\nprintf \"✅ ${GREEN}Done. All files were formatted (if required).${NC}\\n\"\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\nfind_package(Threads REQUIRED)\n\nif (ENABLE_SSL_TESTS)\n    add_library(test_server STATIC\n                abstractServer.cpp\n                httpServer.cpp\n                httpsServer.cpp\n                testUtils.cpp)\nelse ()\n    add_library(test_server STATIC\n                abstractServer.cpp\n                httpServer.cpp\n                testUtils.cpp)\nendif()\nif(WIN32)\n    target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest\n                                      PUBLIC mongoose ws2_32 wsock32)\nelse()\n    target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest\n                                      PUBLIC mongoose)\nendif()\n\nmacro(add_cpr_test _TEST_NAME)\n    add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)\n    target_link_libraries(${_TEST_NAME}_tests PRIVATE\n        test_server\n        GTest::GTest\n        cpr::cpr\n        ${CURL_LIB})\n    add_test(NAME cpr_${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests)\n    # Group under the \"tests\" project folder in IDEs such as Visual Studio.\n    set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER \"tests\")\n    if(WIN32 AND BUILD_SHARED_LIBS)\n        add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:libcurl> $<TARGET_FILE_DIR:${_TEST_NAME}_tests>)\n        add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:cpr> $<TARGET_FILE_DIR:${_TEST_NAME}_tests>)\n    endif()\nendmacro()\n\nadd_cpr_test(get)\nadd_cpr_test(post)\nadd_cpr_test(session)\nadd_cpr_test(prepare)\nadd_cpr_test(async)\nif(CPR_BUILD_TESTS_PROXY)\n    add_cpr_test(proxy)\n    add_cpr_test(proxy_auth)\nendif()\nadd_cpr_test(head)\nadd_cpr_test(delete)\nadd_cpr_test(put)\nadd_cpr_test(callback)\nadd_cpr_test(raw_body)\nadd_cpr_test(options)\nadd_cpr_test(patch)\nadd_cpr_test(error)\nadd_cpr_test(alternating)\nadd_cpr_test(util)\nadd_cpr_test(structures)\nadd_cpr_test(encoded_auth)\nadd_cpr_test(version)\nadd_cpr_test(download)\nadd_cpr_test(interceptor)\nadd_cpr_test(interceptor_multi)\nadd_cpr_test(multiperform)\nadd_cpr_test(resolve)\nadd_cpr_test(multiasync)\nadd_cpr_test(file_upload)\nadd_cpr_test(singleton)\nadd_cpr_test(threadpool)\nadd_cpr_test(testUtils)\nadd_cpr_test(connection_pool)\nadd_cpr_test(sse)\nadd_cpr_test(curlholder)\n\nif (ENABLE_SSL_TESTS)\n    add_cpr_test(ssl)\n\n    # Install all ssl keys and certs. Explicit copy for each file to prevent issues when copying on Windows.\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:ssl_tests>/data/certificates $<TARGET_FILE_DIR:ssl_tests>/data/keys)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/client.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/client.crt)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/root-ca.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/root-ca.crt)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/sub-ca.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/sub-ca.crt)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/ca-bundle.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/ca-bundle.crt)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/server.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/server.crt)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/client.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/client.key)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/root-ca.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/root-ca.key)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/server.key)\n    add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.pub $<TARGET_FILE_DIR:ssl_tests>/data/keys/server.pub)\nendif()\n\nadd_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file_hello_äüöp_2585_你好.txt $<TARGET_FILE_DIR:file_upload_tests>/data/test_file_hello_äüöp_2585_你好.txt)\nadd_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file_hello_äüöp_2585.txt $<TARGET_FILE_DIR:file_upload_tests>/data/test_file_hello_äüöp_2585.txt)\nadd_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file.txt $<TARGET_FILE_DIR:file_upload_tests>/data/test_file.txt)\n\nfile(INSTALL data DESTINATION data)\n"
  },
  {
    "path": "test/LICENSE",
    "content": "This license applies to everything inside this directory and all\nsubdirectories.\n\n                     GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>."
  },
  {
    "path": "test/abstractServer.cpp",
    "content": "#include \"abstractServer.hpp\"\n\nnamespace cpr {\nvoid AbstractServer::SetUp() {\n    Start();\n}\n\nvoid AbstractServer::TearDown() {\n    Stop();\n}\n\nvoid AbstractServer::Start() {\n    should_run = true;\n    serverThread = std::make_shared<std::thread>(&AbstractServer::Run, this);\n    serverThread->detach();\n    std::unique_lock<std::mutex> server_lock(server_mutex);\n    server_start_cv.wait(server_lock);\n}\n\nvoid AbstractServer::Stop() {\n    should_run = false;\n    std::unique_lock<std::mutex> server_lock(server_mutex);\n    server_stop_cv.wait(server_lock);\n}\n\nstatic void EventHandler(mg_connection* conn, int event, void* event_data, void* context) {\n    switch (event) {\n        case MG_EV_READ:\n        case MG_EV_WRITE:\n            /** Do nothing. Just for housekeeping. **/\n            break;\n        case MG_EV_POLL:\n            /** Do nothing. Just for housekeeping. **/\n            break;\n        case MG_EV_CLOSE:\n            /** Do nothing. Just for housekeeping. **/\n            break;\n        case MG_EV_ACCEPT:\n            /* Initialize HTTPS connection if Server is an HTTPS Server */\n            static_cast<AbstractServer*>(context)->acceptConnection(conn);\n            break;\n        case MG_EV_CONNECT:\n            /** Do nothing. Just for housekeeping. **/\n            break;\n\n        case MG_EV_HTTP_CHUNK: {\n            /** Do nothing. Just for housekeeping. **/\n        } break;\n\n        case MG_EV_HTTP_MSG: {\n            AbstractServer* server = static_cast<AbstractServer*>(context);\n            // Use the connection address as unique identifier instead\n            int port = AbstractServer::GetRemotePort(conn);\n            server->AddConnection(port);\n            server->OnRequest(conn, static_cast<mg_http_message*>(event_data));\n        } break;\n\n        default:\n            break;\n    }\n}\n\nvoid AbstractServer::Run() {\n    // Setup a new mongoose http server.\n    memset(&mgr, 0, sizeof(mg_mgr));\n    initServer(&mgr, EventHandler);\n\n    // Notify the main thread that the server is up and runing:\n    server_start_cv.notify_all();\n\n    // Main server loop:\n    while (should_run) {\n        // NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers)\n        mg_mgr_poll(&mgr, 100);\n    }\n\n    // Shutdown and cleanup:\n    timer_args.clear();\n    mg_mgr_free(&mgr);\n\n    // Notify the main thread that we have shut down everything:\n    server_stop_cv.notify_all();\n}\n\nvoid AbstractServer::AddConnection(int remote_port) {\n    unique_connections.insert(remote_port);\n}\n\nsize_t AbstractServer::GetConnectionCount() {\n    return unique_connections.size();\n}\n\nvoid AbstractServer::ResetConnectionCount() {\n    unique_connections.clear();\n}\n\nstatic const std::string base64_chars =\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n        \"abcdefghijklmnopqrstuvwxyz\"\n        \"0123456789+/\";\n/**\n * Decodes the given BASE64 string to a normal string.\n * Source: https://gist.github.com/williamdes/308b95ac9ef1ee89ae0143529c361d37\n **/\nstd::string AbstractServer::Base64Decode(const std::string& in) {\n    std::string out;\n\n    std::vector<int> T(256, -1);\n    for (size_t i = 0; i < 64; i++)\n        T[base64_chars[i]] = static_cast<int>(i);\n\n    int val = 0;\n    int valb = -8;\n    for (unsigned char c : in) {\n        if (T[c] == -1) {\n            break;\n        }\n        val = (val << 6) + T[c];\n        valb += 6;\n        if (valb >= 0) {\n            out.push_back(char((val >> valb) & 0xFF));\n            valb -= 8;\n        }\n    }\n    return out;\n}\n\n// Sends error similar like in mongoose 6 method mg_http_send_error\n// https://github.com/cesanta/mongoose/blob/6.18/mongoose.c#L7081-L7089\nvoid AbstractServer::SendError(mg_connection* conn, int code, std::string& reason) {\n    std::string headers{\"Content-Type: text/plain\\r\\nConnection: close\\r\\n\"};\n    mg_http_reply(conn, code, headers.c_str(), reason.c_str());\n}\n\n// Checks whether a pointer to a connection is still managed by a mg_mgr.\n// This check tells whether it is still possible to send a message via the given connection\n// Note that it is still possible that the pointer of an old connection object may be reused by mongoose.\n// In this case, the active connection might refer to a different connection than the one the caller refers to\nbool AbstractServer::IsConnectionActive(mg_mgr* mgr, mg_connection* conn) {\n    mg_connection* c{mgr->conns};\n    while (c) {\n        if (c == conn) {\n            return true;\n        }\n        c = c->next;\n    }\n    return false;\n}\n\nuint16_t AbstractServer::GetRemotePort(const mg_connection* conn) {\n    return (conn->rem.port >> 8) | (conn->rem.port << 8);\n}\n\nuint16_t AbstractServer::GetLocalPort(const mg_connection* conn) {\n    return (conn->loc.port >> 8) | (conn->loc.port << 8);\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "test/abstractServer.hpp",
    "content": "#ifndef CPR_TEST_ABSTRACT_SERVER_SERVER_H\n#define CPR_TEST_ABSTRACT_SERVER_SERVER_H\n\n#include <atomic>\n#include <condition_variable>\n#include <gtest/gtest.h>\n#include <memory>\n#include <mutex>\n#include <set>\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include \"mongoose.h\"\n\nnamespace cpr {\n\n// Helper struct for functions using timers to simulate slow connections\nstruct TimerArg {\n    mg_mgr* mgr;\n    mg_connection* connection;\n    unsigned long connection_id;\n    mg_timer timer;\n    unsigned counter;\n\n    explicit TimerArg(mg_mgr* m, mg_connection* c, mg_timer&& t) : mgr{m}, connection{c}, connection_id{0}, timer{t}, counter{0} {}\n\n    ~TimerArg() {\n        mg_timer_free(&mgr->timers, &timer);\n    }\n};\n\nclass AbstractServer : public testing::Environment {\n  public:\n    ~AbstractServer() override = default;\n\n    void SetUp() override;\n    void TearDown() override;\n\n    void Start();\n    void Stop();\n\n    size_t GetConnectionCount();\n    void ResetConnectionCount();\n    void AddConnection(int remote_port);\n\n    virtual std::string GetBaseUrl() = 0;\n    virtual uint16_t GetPort() = 0;\n\n    virtual void acceptConnection(mg_connection* conn) = 0;\n    virtual void OnRequest(mg_connection* conn, mg_http_message* msg) = 0;\n\n    static uint16_t GetRemotePort(const mg_connection* conn);\n    static uint16_t GetLocalPort(const mg_connection* conn);\n\n  private:\n    std::shared_ptr<std::thread> serverThread{nullptr};\n    std::mutex server_mutex;\n    std::condition_variable server_start_cv;\n    std::condition_variable server_stop_cv;\n    std::atomic<bool> should_run{false};\n    std::set<int> unique_connections;\n\n    void Run();\n\n  protected:\n    mg_mgr mgr{};\n    std::vector<std::unique_ptr<TimerArg>> timer_args{};\n    virtual mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) = 0;\n\n    static std::string Base64Decode(const std::string& in);\n    static void SendError(mg_connection* conn, int code, std::string& reason);\n    static bool IsConnectionActive(mg_mgr* mgr, mg_connection* conn);\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "test/alternating_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(AlternatingTests, PutGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Put(url, payload);\n        std::string expected_text{\"Header reflect PUT\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Response response = cpr::Get(url);\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(AlternatingTests, PutGetPutGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Put(url, payload);\n        std::string expected_text{\"Header reflect PUT\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Response response = cpr::Get(url);\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Put(url, payload);\n        std::string expected_text{\"Header reflect PUT\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Response response = cpr::Get(url);\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(AlternatingTests, HeadGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n\n    {\n        // Head shouldn't return a body\n        Response response = cpr::Head(url);\n        std::string expected_text{\"\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Response response = cpr::Get(url);\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(AlternatingTests, PutHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Put(url, payload);\n        std::string expected_text{\"Header reflect PUT\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        // Head shouldn't return a body\n        Response response = cpr::Head(url);\n        std::string expected_text{\"\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(AlternatingTests, PutPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Put(url, payload);\n        std::string expected_text{\"Header reflect PUT\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n\n    {\n        Payload payload{{\"x\", \"5\"}};\n        Response response = cpr::Post(url, payload);\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/async_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n#include <vector>\n\n#include \"cpr/cpr.h\"\n#include \"cpr/filesystem.h\"\n\n#include \"cpr/api.h\"\n#include \"cpr/response.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nTEST(AsyncTests, AsyncGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    cpr::AsyncResponse future = cpr::GetAsync(url);\n    std::string expected_text{\"Hello world!\"};\n    cpr::Response response = future.get();\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(response.primary_ip, \"127.0.0.1\");\n    EXPECT_EQ(response.primary_port, server->GetPort());\n}\n\nTEST(AsyncTests, AsyncGetMultipleTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::GetAsync(url));\n    }\n    for (cpr::AsyncResponse& future : responses) {\n        std::string expected_text{\"Hello world!\"};\n        cpr::Response response = future.get();\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n}\n\nTEST(AsyncTests, AsyncGetMultipleReflectTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 100; ++i) {\n        Parameters p{{\"key\", std::to_string(i)}};\n        responses.emplace_back(cpr::GetAsync(url, p));\n    }\n    int i = 0;\n    for (cpr::AsyncResponse& future : responses) {\n        std::string expected_text{\"Hello world!\"};\n        cpr::Response response = future.get();\n        EXPECT_EQ(expected_text, response.text);\n        Url expected_url{url + \"?key=\" + std::to_string(i)};\n        EXPECT_EQ(expected_url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        ++i;\n    }\n}\n\nTEST(AsyncTests, AsyncDownloadTest) {\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::AsyncResponse future = cpr::DownloadAsync(fs::path{\"/tmp/aync_download\"}, url, cpr::Header{{\"Accept-Encoding\", \"gzip\"}}, cpr::WriteCallback{write_data, 0});\n    cpr::Response response = future.get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/callback_tests.cpp",
    "content": "#include <cstddef>\n#include <gtest/gtest.h>\n\n#include <chrono>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include \"cpr/cpr.h\"\n\n#include \"cpr/cprtypes.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\nstd::chrono::milliseconds sleep_time{50};\nstd::chrono::seconds zero{0};\n\nint status_callback(int& status_code, Response r) {\n    status_code = r.status_code;\n    return r.status_code;\n}\n\nint status_callback_ref(int& status_code, const Response& r) {\n    status_code = r.status_code;\n    return r.status_code;\n}\n\nstd::string text_callback(std::string& expected_text, Response r) {\n    expected_text = r.text;\n    return r.text;\n}\n\nstd::string text_callback_ref(std::string& expected_text, const Response& r) {\n    expected_text = r.text;\n    return r.text;\n}\n\nTEST(CallbackGetTests, CallbackGetLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::GetCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::GetCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::GetCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::GetCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::GetCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::GetCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::GetCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackGetTests, CallbackGetFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::GetCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    int status_code = 0;\n    auto future = cpr::DeleteCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::string expected_text{};\n    auto future = cpr::DeleteCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    int status_code = 0;\n    auto future = cpr::DeleteCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::string expected_text{};\n    auto future = cpr::DeleteCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::DeleteCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::DeleteCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::DeleteCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackDeleteTests, CallbackDeleteFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::DeleteCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::HeadCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::HeadCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::HeadCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::HeadCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::HeadCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::HeadCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::HeadCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackHeadTests, CallbackHeadFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::HeadCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    // NOLINTNEXTLINE(google-runtime-int)\n    long status_code = 0;\n    auto future = cpr::PostCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PostCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    // NOLINTNEXTLINE(google-runtime-int)\n    long status_code = 0;\n    auto future = cpr::PostCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PostCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PostCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PostCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PostCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPostTests, CallbackPostFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PostCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto future = cpr::PutCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PutCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto future = cpr::PutCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PutCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PutCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PutCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PutCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPutTests, CallbackPutFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PutCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::OptionsCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::OptionsCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto future = cpr::OptionsCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto future = cpr::OptionsCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::OptionsCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::OptionsCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::OptionsCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackOptionsTests, CallbackOptionsFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::OptionsCallback(callback, url);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchLambdaStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto future = cpr::PatchCallback(\n            [&status_code](Response r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchLambdaTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PatchCallback(\n            [&expected_text](Response r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchLambdaStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto future = cpr::PatchCallback(\n            [&status_code](const Response& r) {\n                status_code = r.status_code;\n                return r.status_code;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchLambdaTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto future = cpr::PatchCallback(\n            [&expected_text](const Response& r) {\n                expected_text = r.text;\n                return r.text;\n            },\n            url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchFunctionStatusTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PatchCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PatchCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchFunctionStatusReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    int status_code = 0;\n    auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));\n    auto future = cpr::PatchCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(status_code, future.get());\n}\n\nTEST(CallbackPatchTests, CallbackPatchFunctionTextReferenceTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::string expected_text{};\n    auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));\n    auto future = cpr::PatchCallback(callback, url, payload);\n    std::this_thread::sleep_for(sleep_time);\n    EXPECT_EQ(future.wait_for(zero), std::future_status::ready);\n    EXPECT_EQ(expected_text, future.get());\n}\n\nTEST(CallbackDataTests, CallbackReadFunctionCancelTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, cpr::ReadCallback([](char* /*buffer*/, size_t& /*size*/, intptr_t /*userdata*/) -> size_t { return false; }));\n    EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));\n}\n\nTEST(CallbackDataTests, CallbackReadFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    unsigned count = 0;\n    Response response = cpr::Post(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t {\n                                                             std::string data;\n                                                             ++count;\n                                                             switch (count) {\n                                                                 case 1:\n                                                                     data = \"x=\";\n                                                                     break;\n                                                                 case 2:\n                                                                     data = \"5\";\n                                                                     break;\n                                                                 default:\n                                                                     return false;\n                                                             }\n                                                             std::copy(data.begin(), data.end(), buffer);\n                                                             size = data.size();\n                                                             return true;\n                                                         }});\n    EXPECT_EQ(2, count);\n    EXPECT_EQ(expected_text, response.text);\n}\n\nTEST(CallbackDataTests, CallbackReadFunctionTextTestPut) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    unsigned count = 0;\n    Response response = cpr::Put(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t {\n                                                            std::string data;\n                                                            ++count;\n                                                            switch (count) {\n                                                                case 1:\n                                                                    data = \"x=\";\n                                                                    break;\n                                                                case 2:\n                                                                    data = \"5\";\n                                                                    break;\n                                                                default:\n                                                                    return false;\n                                                            }\n                                                            std::copy(data.begin(), data.end(), buffer);\n                                                            size = data.size();\n                                                            return true;\n                                                        }});\n    EXPECT_EQ(2, count);\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\n/**\n * Checks if the \"Transfer-Encoding\" header will be kept when using headers and a read callback.\n * Issue: https://github.com/whoshuu/cpr/issues/517\n **/\nTEST(CallbackDataTests, CallbackReadFunctionHeaderTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::string data = \"Test\";\n    Response response = cpr::Post(url,\n                                  cpr::ReadCallback{-1,\n                                                    [&](char* /*buffer*/, size_t& size, intptr_t /*userdata*/) -> size_t {\n                                                        size = 0;\n                                                        return true;\n                                                    }},\n                                  Header{{\"TestHeader\", \"42\"}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n\n    // Check Header:\n    EXPECT_EQ(std::string{\"42\"}, response.header[\"TestHeader\"]); // Set by us\n    EXPECT_TRUE(response.header.find(\"TestHeader\") != response.header.end());\n    EXPECT_EQ(std::string{\"chunked\"}, response.header[\"Transfer-Encoding\"]); // Set by the read callback\n    EXPECT_TRUE(response.header.find(\"Transfer-Encoding\") != response.header.end());\n}\n\n/* cesanta mongoose doesn't support chunked requests yet\nTEST(CallbackDataTests, CallbackReadFunctionChunkedTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    unsigned count = 0;\n    Response response = cpr::Post(url, cpr::ReadCallback{[&count](char* buffer, size_t & size) -> size_t {\n        std::string data;\n        ++ count;\n        switch (count) {\n        case 1:\n            data = \"x=\";\n            break;\n        case 2:\n            data = \"5\";\n            break;\n        default:\n            data = \"\";\n            break;\n        }\n        std::copy(data.begin(), data.end(), buffer);\n        size = data.size();\n        return true;\n    }});\n    EXPECT_EQ(3, count);\n    EXPECT_EQ(expected_text, response.text);\n}\n*/\n\nTEST(CallbackDataTests, CallbackHeaderFunctionCancelTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = Post(url, HeaderCallback{[](const std::string_view /*header*/, intptr_t /*userdata*/) -> bool { return false; }});\n    EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));\n}\n\nTEST(CallbackDataTests, CallbackHeaderFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::vector<std::string> expected_headers{\"HTTP/1.1 201 Created\\r\\n\", \"Content-Type: application/json\\r\\n\", \"\\r\\n\"};\n    std::set<std::string> response_headers;\n    Post(url, HeaderCallback{[&response_headers](const std::string_view header, intptr_t /*userdata*/) -> bool {\n             response_headers.insert(std::string{header});\n             return true;\n         }});\n    for (const std::string& header : expected_headers) {\n        std::cout << header << '\\n';\n        EXPECT_TRUE(response_headers.count(header));\n    }\n}\n\nTEST(CallbackDataTests, CallbackWriteFunctionCancelTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = Post(url, WriteCallback{[](const std::string_view /*header*/, intptr_t /*userdata*/) -> bool { return false; }});\n    EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));\n}\n\nTEST(CallbackDataTests, CallbackWriteFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    std::string response_text;\n    Post(url, Payload{{\"x\", \"5\"}}, WriteCallback{[&response_text](const std::string_view header, intptr_t /*userdata*/) -> bool {\n             response_text.append(header);\n             return true;\n         }});\n    EXPECT_EQ(expected_text, response_text);\n}\n\nTEST(CallbackDataTests, CallbackProgressFunctionCancelTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = Post(url, ProgressCallback{[](size_t /*downloadTotal*/, size_t /*downloadNow*/, size_t /*uploadTotal*/, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool { return false; }});\n    EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));\n}\n\nTEST(CallbackDataTests, CallbackProgressFunctionTotalTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Body body{\"x=5\"};\n    size_t response_upload = 0;\n    size_t response_download = 0;\n    Response response = Post(url, body, ProgressCallback{[&](size_t downloadTotal, size_t /*downloadNow*/, size_t uploadTotal, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool {\n                                 response_upload = uploadTotal;\n                                 response_download = downloadTotal;\n                                 return true;\n                             }});\n    EXPECT_EQ(body.str().length(), response_upload);\n    EXPECT_EQ(response.text.length(), response_download);\n}\n\nTEST(CallbackDataTests, CallbackDebugFunctionTextTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Body body{\"x=5\"};\n    std::string debug_body;\n    Response response = Post(url, body, DebugCallback{[&](DebugCallback::InfoType type, std::string_view data, intptr_t /*userdata*/) {\n                                 if (type == DebugCallback::InfoType::DATA_OUT) {\n                                     debug_body = data;\n                                 }\n                             }});\n    EXPECT_EQ(body.str(), debug_body);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/connection_pool_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <chrono>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <cpr/cpr.h>\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\nconst size_t NUM_REQUESTS = 10;\n\nTEST(MultipleGetTests, PoolBasicMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    ConnectionPool pool;\n    server->ResetConnectionCount();\n\n    // Without shared connection pool - make 10 sequential requests\n    for (size_t i = 0; i < NUM_REQUESTS; ++i) {\n        Response response = cpr::Get(url);\n        EXPECT_EQ(std::string{\"Hello world!\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n    EXPECT_EQ(server->GetConnectionCount(), NUM_REQUESTS);\n\n    // With shared connection pool - make 10 sequential requests\n    server->ResetConnectionCount();\n    for (size_t i = 0; i < NUM_REQUESTS; ++i) {\n        Response response = cpr::Get(url, pool);\n        EXPECT_EQ(std::string{\"Hello world!\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n    EXPECT_LT(server->GetConnectionCount(), NUM_REQUESTS);\n}\n\nTEST(MultipleGetTests, PoolAsyncGetMultipleTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    ConnectionPool pool;\n    std::vector<AsyncResponse> responses;\n    server->ResetConnectionCount();\n\n    const size_t NUM_BATCHES = 2;\n    const size_t BATCH_SIZE = NUM_REQUESTS / 2; // 5 requests per batch\n\n    // Without shared connection pool - two batches with 10ms sleep\n    responses.reserve(NUM_REQUESTS);\n\n    for (size_t batch = 0; batch < NUM_BATCHES; ++batch) {\n        for (size_t i = 0; i < BATCH_SIZE; ++i) {\n            responses.emplace_back(cpr::GetAsync(url));\n        }\n\n        // Sleep between batches but not after the last batch\n        if (batch != NUM_BATCHES - 1) {\n            std::this_thread::sleep_for(std::chrono::milliseconds(5));\n        }\n    }\n\n    // Wait for all responses\n    for (AsyncResponse& future : responses) {\n        Response response = future.get();\n        EXPECT_EQ(std::string{\"Hello world!\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n    EXPECT_EQ(server->GetConnectionCount(), NUM_REQUESTS);\n\n    // With shared connection pool - same two-batch approach\n    server->ResetConnectionCount();\n    responses.clear();\n    responses.reserve(NUM_REQUESTS);\n\n    for (size_t batch = 0; batch < NUM_BATCHES; ++batch) {\n        for (size_t i = 0; i < BATCH_SIZE; ++i) {\n            responses.emplace_back(cpr::GetAsync(url, pool));\n        }\n\n        // Sleep between batches but not after the last batch\n        if (batch != NUM_BATCHES - 1) {\n            std::this_thread::sleep_for(std::chrono::milliseconds(5));\n        }\n    }\n\n    // Wait for all responses\n    for (AsyncResponse& future : responses) {\n        Response response = future.get();\n        EXPECT_EQ(std::string{\"Hello world!\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n\n    // With connection pooling, should use fewer connections than requests\n    EXPECT_LT(server->GetConnectionCount(), NUM_REQUESTS);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}"
  },
  {
    "path": "test/curlholder_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <utility>\n\n#include \"cpr/curlholder.h\"\n\n// Check if there is a double free in curl holder after move.\n// To reproduce this, run with address sanitizers enabled.\n// https://github.com/libcpr/cpr/issues/1286\nTEST(CurlholderTests, MoveOperator) {\n    cpr::CurlHolder a;\n    cpr::CurlHolder b;\n\n    a = std::move(b);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/data/certificates/ca-bundle.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBrjCCAWCgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdMwBQYDK2VwMDExCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0\nMDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMDELMAkGA1UEBhMCR0IxEDAOBgNV\nBAoMB0V4YW1wbGUxDzANBgNVBAMMBlN1YiBDQTAqMAUGAytlcAMhAL9vKw+Jb0jc\nTHPJj/0HKRBIusX9D0Xj4qZEvK3kqXX+o4GNMIGKMA8GA1UdEwEB/wQFMAMBAf8w\nDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSbsZshYdxmKzqt7YTxBbbOmYLB/DBI\nBgNVHR4EQTA/oD0wC4IJbG9jYWxob3N0MAqHCH8AAAH/AAAAMCKHIAAAAAAAAAAA\nAAAAAAAAAAH/////////////////////MAUGAytlcANBACspVj23xQ46wvlIWimf\nofVcl0Nlj1rW1CoTOoA4butJGfJJQoYMzW8Ui/sVokzPoTw7vdOw9u3Knps26c0T\nYgk=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIBrzCCAWGgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdIwBQYDK2VwMDExCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0\nMDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMTELMAkGA1UEBhMCR0IxEDAOBgNV\nBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwKjAFBgMrZXADIQDI4HsQNDKN\nxwtOvL2FI7Q+VIoqWLHmsoLaOe1L+JvbyKOBjTCBijAPBgNVHRMBAf8EBTADAQH/\nMA4GA1UdDwEB/wQEAwICBDAdBgNVHQ4EFgQUvMDvOfNgjMd7lZ6iDa/JCJcVLwkw\nSAYDVR0eBEEwP6A9MAuCCWxvY2FsaG9zdDAKhwh/AAAB/wAAADAihyAAAAAAAAAA\nAAAAAAAAAAAB/////////////////////zAFBgMrZXADQQBCMm6k6vanrNUO3vlc\nvsecQTSUVxsnl+bD6ANYhs10cuGafZ/lFRh1z4yBxz50b7EIePDeLP2pZlLmz8bm\nsN8M\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/certificates/client.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBejCCASygAwIBAgIRAKy+/CzeW5ALVVSDllVnZdUwBQYDK2VwMDAxCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw\nNTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LWNsaWVu\ndDAqMAUGAytlcAMhAPU88C62SwhVGcJhYDp97gf1x5pHjcVD/AZ3PqoQfU68o3Uw\nczAfBgNVHSMEGDAWgBSbsZshYdxmKzqt7YTxBbbOmYLB/DAMBgNVHRMBAf8EAjAA\nMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQU\nTU53uUDblDe4iFsDIV77hIwigPswBQYDK2VwA0EAX0aM10AEe8HxQNXcL2Qf1ryh\nStldRyLog/s1ZuGidfxwdr7xoZes0yjYaZYhkKLDIf+CR3BwEWik2ppNXE1bDw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/certificates/root-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBrzCCAWGgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdIwBQYDK2VwMDExCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0\nMDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMTELMAkGA1UEBhMCR0IxEDAOBgNV\nBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwKjAFBgMrZXADIQDI4HsQNDKN\nxwtOvL2FI7Q+VIoqWLHmsoLaOe1L+JvbyKOBjTCBijAPBgNVHRMBAf8EBTADAQH/\nMA4GA1UdDwEB/wQEAwICBDAdBgNVHQ4EFgQUvMDvOfNgjMd7lZ6iDa/JCJcVLwkw\nSAYDVR0eBEEwP6A9MAuCCWxvY2FsaG9zdDAKhwh/AAAB/wAAADAihyAAAAAAAAAA\nAAAAAAAAAAAB/////////////////////zAFBgMrZXADQQBCMm6k6vanrNUO3vlc\nvsecQTSUVxsnl+bD6ANYhs10cuGafZ/lFRh1z4yBxz50b7EIePDeLP2pZlLmz8bm\nsN8M\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/certificates/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBtDCCAWagAwIBAgIRAKy+/CzeW5ALVVSDllVnZdQwBQYDK2VwMDAxCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw\nNTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl\ncjAqMAUGAytlcAMhACdLUqJFSyspgGKJiXNlnOLU2dO/TLV+b8aIZNAX7EuVo4Gu\nMIGrMB8GA1UdIwQYMBaAFJuxmyFh3GYrOq3thPEFts6ZgsH8MAwGA1UdEwEB/wQC\nMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIF\noDAdBgNVHQ4EFgQUZkdU+CWXVppSVjW0p1JgDOdPMwkwLAYDVR0RBCUwI4IJbG9j\nYWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMAUGAytlcANBAG1j2RGjm8ef\ntiMSJ+k04KGjIL7734D+UwidjOSCQnbCVRPofIaDMwuan5IqP97pMnjAsbw/QukX\n+Z9sFTWjAQk=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/certificates/sub-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBrjCCAWCgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdMwBQYDK2VwMDExCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0\nMDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMDELMAkGA1UEBhMCR0IxEDAOBgNV\nBAoMB0V4YW1wbGUxDzANBgNVBAMMBlN1YiBDQTAqMAUGAytlcAMhAL9vKw+Jb0jc\nTHPJj/0HKRBIusX9D0Xj4qZEvK3kqXX+o4GNMIGKMA8GA1UdEwEB/wQFMAMBAf8w\nDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSbsZshYdxmKzqt7YTxBbbOmYLB/DBI\nBgNVHR4EQTA/oD0wC4IJbG9jYWxob3N0MAqHCH8AAAH/AAAAMCKHIAAAAAAAAAAA\nAAAAAAAAAAH/////////////////////MAUGAytlcANBACspVj23xQ46wvlIWimf\nofVcl0Nlj1rW1CoTOoA4butJGfJJQoYMzW8Ui/sVokzPoTw7vdOw9u3Knps26c0T\nYgk=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/client.cnf",
    "content": "# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html\n[req]\nprompt = no\ndistinguished_name = dn\n\n[dn]\nCN = test-client\n\n"
  },
  {
    "path": "test/data/generate-certificates.sh",
    "content": "#!/bin/sh\n\n# Generate a CA with a self-signed root certificate that then signs the server certificate\n# Based on the OpenSSL Cookbook by Ivan Ristic:\n# https://www.feistyduck.com/library/openssl-cookbook/online/\n#\n# Especially, see chapter 1.5. Creating a private Certification Authority:\n# https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca.html\n\nexport KEY_PATH=keys\nexport CRT_PATH=certificates\nexport CA_PATH=ca\n\n# Create environment. \n# $CA_PATH is deleted in the end. \n# If new certificates need to be issued, this needs to be done before the cleanup in the end.\nmkdir -p $KEY_PATH $CRT_PATH $CA_PATH/db $CA_PATH/private $CA_PATH/certificates\ntouch $CA_PATH/db/index\nopenssl rand -hex 16  > $CA_PATH/db/serial\n\n\n# Generate all private keys\nopenssl genpkey -algorithm ed25519 -out $KEY_PATH/root-ca.key\nopenssl genpkey -algorithm ed25519 -out $KEY_PATH/sub-ca.key\nopenssl genpkey -algorithm ed25519 -out $KEY_PATH/server.key\nopenssl genpkey -algorithm ed25519 -out $KEY_PATH/client.key\n\n# For the server, we also need the public key\nopenssl pkey -in $KEY_PATH/server.key -pubout -out $KEY_PATH/server.pub\n\n\n# Generate a Certificate Signing Request for the Root CA based on a config file\nopenssl req -new \\\n    -config root-ca.cnf -out root-ca.csr \\\n    -key $KEY_PATH/root-ca.key\n\n# Self-sign the root certificate\nopenssl ca -batch \\\n    -selfsign -config root-ca.cnf \\\n    -extensions ca_ext \\\n    -in root-ca.csr -out $CRT_PATH/root-ca.crt -notext\n\n# Create a Certificate Signing request for the Sub CA\nopenssl req -new \\\n    -config sub-ca.cnf -out sub-ca.csr \\\n    -key $KEY_PATH/sub-ca.key\n\n# Issue the Sub CA\nopenssl ca -batch \\\n    -config root-ca.cnf \\\n    -extensions ca_ext \\\n    -in sub-ca.csr -out $CRT_PATH/sub-ca.crt -notext\n\n# Create a Certificate Signing request for the server certificate\nopenssl req -new \\\n    -config server.cnf -out server.csr \\\n    -key $KEY_PATH/server.key\nopenssl req -text -in server.csr -noout\n\n# Issue the server certificate\nopenssl ca -batch \\\n    -config root-ca.cnf \\\n    -name sub_ca \\\n    -extensions server_ext \\\n    -in server.csr -out $CRT_PATH/server.crt -notext \\\n    -days 1825\n\n# Create a Certificate Signing request for the client certificate\nopenssl req -new \\\n    -config client.cnf -out client.csr \\\n    -key $KEY_PATH/client.key\n\n# Issue the client certificate\nopenssl ca -batch \\\n    -config root-ca.cnf \\\n    -name sub_ca \\\n    -extensions client_ext \\\n    -in client.csr -out $CRT_PATH/client.crt -notext \\\n    -days 1825\n\ncp $CRT_PATH/sub-ca.crt $CRT_PATH/ca-bundle.crt\ncat $CRT_PATH/root-ca.crt >> $CRT_PATH/ca-bundle.crt\n\n# Clean up\n# IMPORTANT: If new certificates should be issued, $CA_PATH and its files MUST NOT be deleted!\n# New certificates can be created in this script before cleaning up.\nrm -rf *.csr $CA_PATH\n\n"
  },
  {
    "path": "test/data/keys/client.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIIK4CYIlr3jGta1aSNICikX8V4CXv/i6IJTmj68CUQOU\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/data/keys/root-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEICJbx2nPwG8L2S/EKvCHI2q4InmAFAaNVBqdVq13ZpJz\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/data/keys/server.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIGqt/stoQYkwb24d3EUC0LpH2QwKuh+0tftML+wk/N1P\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/data/keys/server.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAJ0tSokVLKymAYomJc2Wc4tTZ079MtX5vxohk0BfsS5U=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "test/data/keys/sub-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIASqWiXeb8UOEbwjVVq/2j49JvbBX2aLAiqjUtHQK2qV\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/data/root-ca.cnf",
    "content": "# Based on: https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-creating-root.html\n[default]\nname                    = root-ca\ndefault_ca              = ca_default\nname_opt                = utf8,esc_ctrl,multiline,lname,align\n\n[ca_dn]\ncountryName             = \"GB\"\norganizationName        = \"Example\"\ncommonName              = \"Root CA\"\n\n[ca_default]\nhome                    = ./${ENV::CA_PATH}\ndatabase                = $home/db/index\nserial                  = $home/db/serial\ncertificate             = ./${ENV::CRT_PATH}/$name.crt\nprivate_key             = ./${ENV::KEY_PATH}/$name.key\nRANDFILE                = $home/private/random\nnew_certs_dir           = $home/certificates\nunique_subject          = no\ncopy_extensions         = none\ndefault_days            = 3650\ndefault_md              = sha256\npolicy                  = policy_cn_supplied\n\n[sub_ca]\nname                    = sub-ca\nname_opt                = utf8,esc_ctrl,multiline,lname,align\nhome                    = ./${ENV::CA_PATH}\ndatabase                = $home/db/index\nserial                  = $home/db/serial\ncertificate             = ./${ENV::CRT_PATH}/$name.crt\nprivate_key             = ./${ENV::KEY_PATH}/$name.key\nRANDFILE                = $home/private/random\nnew_certs_dir           = $home/certificates\nunique_subject          = no\ncopy_extensions         = none\ndefault_days            = 3650\ndefault_md              = sha256\npolicy                  = policy_cn_supplied\n\n[policy_cn_supplied]\ncountryName             = optional\nstateOrProvinceName     = optional\norganizationName        = optional\norganizationalUnitName  = optional\ncommonName              = supplied\nemailAddress            = optional\n\n[req]\ndefault_bits            = 4096\nencrypt_key             = yes\ndefault_md              = sha256\nutf8                    = yes\nstring_mask             = utf8only\nprompt                  = no\ndistinguished_name      = ca_dn\nreq_extensions          = ca_ext\n\n[ca_ext]\nbasicConstraints        = critical,CA:true\nkeyUsage                = critical,keyCertSign\nsubjectKeyIdentifier    = hash\nnameConstraints         = @name_constraints\n\n[server_ext]\nauthorityKeyIdentifier  = keyid:always\nbasicConstraints        = critical,CA:false\nextendedKeyUsage        = clientAuth,serverAuth\nkeyUsage                = critical,digitalSignature,keyEncipherment\nsubjectKeyIdentifier    = hash\nsubjectAltName          = DNS:localhost,IP:127.0.0.1,IP:::1\n\n[client_ext]\nauthorityKeyIdentifier  = keyid:always\nbasicConstraints        = critical,CA:false\nextendedKeyUsage        = clientAuth\nkeyUsage                = critical,digitalSignature\nsubjectKeyIdentifier    = hash\n\n[name_constraints]\npermitted;DNS.0=localhost\npermitted;IP.0=127.0.0.1/255.0.0.0\npermitted;IP.1=::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n\n"
  },
  {
    "path": "test/data/server.cnf",
    "content": "# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html\n[req]\nprompt = no\ndistinguished_name = dn\nreq_extensions = ext\n\n[dn]\nCN = test-server\n\n[ext]\nsubjectAltName = DNS:localhost,IP:127.0.0.1,IP:::1\n\n"
  },
  {
    "path": "test/data/sub-ca.cnf",
    "content": "[req]\ndefault_bits            = 4096\nencrypt_key             = yes\ndefault_md              = sha256\nutf8                    = yes\nstring_mask             = utf8only\nprompt                  = no\ndistinguished_name      = sub_ca_dn\nreq_extensions          = sub_ca_ext\n\n[sub_ca_dn]\ncountryName             = \"GB\"\norganizationName        = \"Example\"\ncommonName              = \"Sub CA\"\n\n[sub_ca_ext]\nbasicConstraints        = critical,CA:true\nkeyUsage                = critical,keyCertSign\nsubjectKeyIdentifier    = hash\nnameConstraints         = @name_constraints\n\n[name_constraints]\npermitted;DNS.0=localhost\npermitted;IP.0=127.0.0.1/255.0.0.0\npermitted;IP.1=::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
  },
  {
    "path": "test/data/test_file.txt",
    "content": "test content: hello_äüöp_2585_你好"
  },
  {
    "path": "test/data/test_file_hello_äüöp_2585.txt",
    "content": "test content: hello_äüöp_2585_你好"
  },
  {
    "path": "test/data/test_file_hello_äüöp_2585_你好.txt",
    "content": "test content: hello_äüöp_2585_你好"
  },
  {
    "path": "test/delete_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(DeleteTests, DeleteTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    Response response = cpr::Delete(url);\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, DeleteUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    Response response = cpr::Delete(url);\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, DeleteJsonBodyTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    Response response = cpr::Delete(url, cpr::Body{\"'foo': 'bar'\"}, cpr::Header{{\"Content-Type\", \"application/json\"}});\n    std::string expected_text{\"'foo': 'bar'\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    Session session;\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    Session session;\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteJsonBodyTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Content-Type\", \"application/json\"}});\n    session.SetBody(cpr::Body{\"{'foo': 'bar'}\"});\n    Response response = session.Delete();\n    std::string expected_text{\"{'foo': 'bar'}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteUnallowedAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteUnallowedAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, SessionDeleteUnallowedAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    session.SetUrl(url);\n    Response response = session.Delete();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, AsyncDeleteTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    cpr::AsyncResponse future_response = cpr::DeleteAsync(url);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, AsyncDeleteUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    cpr::AsyncResponse future_response = cpr::DeleteAsync(url);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DeleteTests, AsyncMultipleDeleteTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::DeleteAsync(url));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"Delete success\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(DeleteTests, AsyncMultipleDeleteUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/delete_unallowed.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::DeleteAsync(url));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"Method Not Allowed\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(405, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/download_tests.cpp",
    "content": "#include <cstddef>\n#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/accept_encoding.h\"\n#include \"cpr/cpr.h\"\n\n#include \"cpr/api.h\"\n#include \"cpr/callback.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/session.h\"\n#include \"httpServer.hpp\"\n\n\nstatic cpr::HttpServer* server = new cpr::HttpServer();\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nTEST(DownloadTests, DownloadHeaderGzip) {\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetUrl(url);\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(DownloadTests, DownloadAcceptEncodingGzip) {\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetAcceptEncoding(cpr::AcceptEncoding{cpr::AcceptEncodingMethods::gzip});\n    session.SetUrl(url);\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(DownloadTests, RangeTestWholeFile) {\n    const int64_t download_size = 9;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetRange(cpr::Range{std::nullopt, std::nullopt});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nTEST(DownloadTests, RangeTestLowerLimit) {\n    const int64_t download_size = 8;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetRange(cpr::Range{1, std::nullopt});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(206, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nTEST(DownloadTests, RangeTestUpperLimit) {\n    const int64_t download_size = 6;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetRange(cpr::Range{std::nullopt, download_size - 1});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(206, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nTEST(DownloadTests, RangeTestLowerAndUpperLimit) {\n    const int64_t download_size = 2;\n    const int64_t start_from = 2;\n    const int64_t finish_at = start_from + download_size - 1;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetRange(cpr::Range{start_from, finish_at});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(206, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nTEST(DownloadTests, RangeTestMultipleRangesSet) {\n    const int64_t num_parts = 2;\n    const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 6 /*data*/;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetMultiRange(cpr::MultiRange{cpr::Range{std::nullopt, 3}, cpr::Range{5, 6}});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(206, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nTEST(DownloadTests, RangeTestMultipleRangesOption) {\n    const int64_t num_parts = 3;\n    const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 7 /*data*/;\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetOption(cpr::MultiRange{cpr::Range{std::nullopt, 2}, cpr::Range{4, 5}, cpr::Range{7, 8}});\n    cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(206, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(download_size, response.downloaded_bytes);\n}\n\nbool real_write_data(std::string_view data, intptr_t userdata) {\n    // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast)\n    std::string* dst = reinterpret_cast<std::string*>(userdata);\n    *dst += data;\n    return true;\n}\n\nTEST(DownloadTests, GetDownloadFileLength) {\n    cpr::Url url{server->GetBaseUrl() + \"/get_download_file_length.html\"};\n    cpr::Session session;\n    session.SetUrl(url);\n    auto len = session.GetDownloadFileLength();\n    EXPECT_EQ(len, -1);\n\n    std::string strFileData;\n    cpr::Response response = session.Download(cpr::WriteCallback{real_write_data, (intptr_t) &strFileData});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n    EXPECT_EQ(strFileData, \"this is a file content.\");\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/encoded_auth_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\nusing namespace cpr;\n\nTEST(EncodedAuthenticationTests, UnicodeEncoderTest) {\n    std::string user = \"一二三\";\n    std::string pass = \"Hello World!\";\n    EncodedAuthentication pa{user, pass};\n    EXPECT_EQ(pa.GetUsername(), \"%E4%B8%80%E4%BA%8C%E4%B8%89\");\n    EXPECT_EQ(pa.GetPassword(), \"Hello%20World%21\");\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/error_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <chrono>\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include <curl/curl.h>\n\n#include \"httpServer.hpp\"\n#include \"httpsServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(ErrorTests, UnsupportedProtocolFailure) {\n    Url url{\"urk://wat.is.this\"};\n    Response response = cpr::Get(url);\n    EXPECT_EQ(0, response.status_code);\n    EXPECT_EQ(ErrorCode::UNSUPPORTED_PROTOCOL, response.error.code);\n}\n\nTEST(ErrorTests, InvalidURLFailure) {\n    Url url{\"???\"};\n    Response response = cpr::Get(url);\n    EXPECT_EQ(0, response.status_code);\n    EXPECT_EQ(ErrorCode::URL_MALFORMAT, response.error.code);\n}\n\nTEST(ErrorTests, TimeoutFailure) {\n    Url url{server->GetBaseUrl() + \"/timeout.html\"};\n    Response response = cpr::Get(url, cpr::Timeout{1});\n    EXPECT_EQ(0, response.status_code);\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(ErrorTests, ChronoTimeoutFailure) {\n    Url url{server->GetBaseUrl() + \"/timeout.html\"};\n    Response response = cpr::Get(url, cpr::Timeout{std::chrono::milliseconds{1}});\n    EXPECT_EQ(0, response.status_code);\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(ErrorTests, ConnectTimeoutFailure) {\n    Url url{\"http://localhost:67\"};\n    Response response = cpr::Get(url, cpr::ConnectTimeout{1});\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT:\n    EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(ErrorTests, ChronoConnectTimeoutFailure) {\n    Url url{\"http://localhost:67\"};\n    Response response = cpr::Get(url, cpr::ConnectTimeout{std::chrono::milliseconds{1}});\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT:\n    EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(ErrorTests, LowSpeedTimeFailure) {\n    Url url{server->GetBaseUrl() + \"/low_speed.html\"};\n    Response response = cpr::Get(url, cpr::LowSpeed{1000, std::chrono::seconds(1)});\n    // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(ErrorTests, LowSpeedBytesFailure) {\n    Url url{server->GetBaseUrl() + \"/low_speed_bytes.html\"};\n    Response response = cpr::Get(url, cpr::LowSpeed{1000, std::chrono::seconds(1)});\n    // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(ErrorTests, ProxyFailure) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, cpr::Proxies{{\"http\", \"http://bad_host.libcpr.orgoooo\"}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT.\n    EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_PROXY || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(ErrorTests, BoolFalseTest) {\n    Error error;\n    EXPECT_FALSE(error);\n}\n\nTEST(ErrorTests, BoolTrueTest) {\n    Error error;\n    error.code = ErrorCode::UNSUPPORTED_PROTOCOL;\n    EXPECT_TRUE(error);\n}\n\nTEST(ErrorTests, StringReprTest) {\n    Error error;\n    error.code = ErrorCode::UNSUPPORTED_PROTOCOL;\n    EXPECT_EQ(std::to_string(error.code), \"UNSUPPORTED_PROTOCOL\");\n}\n\nTEST(ErrorTests, StringReprUnknownTest) {\n    Error error;\n    error.code = ErrorCode::UNKNOWN_ERROR;\n    EXPECT_EQ(std::to_string(error.code), \"UNKNOWN_ERROR\");\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/file_upload_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <fstream>\n#include <optional>\n#include <string>\n\n#include <cpr/cpr.h>\n#include <cpr/filesystem.h>\n\n#include \"cpr/api.h\"\n#include \"cpr/file.h\"\n#include \"cpr/multipart.h\"\n#include \"httpServer.hpp\"\n\nnamespace {\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp)\ncpr::HttpServer* server = new cpr::HttpServer();\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nstd::optional<cpr::fs::path> baseDirPath{std::nullopt};\n} // namespace\n\ncpr::fs::path GetBasePath(const std::string& execPath) {\n    return cpr::fs::path(cpr::fs::path{execPath}.parent_path().string() + \"/\").make_preferred();\n}\n\n\nTEST(FileUploadTests, AsciiFileName) {\n    // Ensure 'baseDirPath' has been set\n    EXPECT_NE(baseDirPath, std::nullopt);\n\n    // NOLINTNEXTLINE(bugprone-unchecked-optional-access)\n    cpr::fs::path filePath = *baseDirPath / \"test_file.txt\";\n\n    cpr::Multipart mp{{cpr::Part(\"file_name\", cpr::File(filePath.string()))}};\n    cpr::Url url{server->GetBaseUrl() + \"/post_file_upload.html\"};\n    cpr::Response response = cpr::Post(url, mp);\n\n    // Expected file content\n    std::ifstream ifs(filePath.string());\n    std::string expected_text = \"{\\n  \\\"file_name\\\": \\\"\" + filePath.filename().string() + \"=\" + std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())) + \"\\\"\\n}\";\n\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(cpr::Url{server->GetBaseUrl() + \"/post_file_upload.html\"}, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(FileUploadTests, NonAsciiFileName) {\n    // Ensure 'baseDirPath' has been set\n    EXPECT_NE(baseDirPath, std::nullopt);\n\n    // NOLINTNEXTLINE(bugprone-unchecked-optional-access)\n    cpr::fs::path filePath = *baseDirPath / \"test_file_hello_äüöp_2585.txt\";\n\n    cpr::Multipart mp{{cpr::Part(\"file_name\", cpr::File(filePath.string()))}};\n    cpr::Url url{server->GetBaseUrl() + \"/post_file_upload.html\"};\n    cpr::Response response = cpr::Post(url, mp);\n\n    // Expected file content\n    std::ifstream ifs(filePath.string());\n    std::string expected_text = \"{\\n  \\\"file_name\\\": \\\"\" + filePath.filename().string() + \"=\" + std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())) + \"\\\"\\n}\";\n\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(cpr::Url{server->GetBaseUrl() + \"/post_file_upload.html\"}, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(FileUploadTests, ChineseFileName) {\n    // Ensure 'baseDirPath' has been set\n    EXPECT_NE(baseDirPath, std::nullopt);\n\n    // NOLINTNEXTLINE(bugprone-unchecked-optional-access)\n    cpr::fs::path filePath = *baseDirPath / \"test_file_hello_äüöp_2585.txt\";\n\n    cpr::Multipart mp{{cpr::Part(\"file_name\", cpr::File(filePath.string()))}};\n    cpr::Url url{server->GetBaseUrl() + \"/post_file_upload.html\"};\n    cpr::Response response = cpr::Post(url, mp);\n\n    // Expected file content\n    std::ifstream ifs(filePath.string());\n    std::string expected_text = \"{\\n  \\\"file_name\\\": \\\"\" + filePath.filename().string() + \"=\" + std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())) + \"\\\"\\n}\";\n\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(cpr::Url{server->GetBaseUrl() + \"/post_file_upload.html\"}, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    baseDirPath = std::make_optional<cpr::fs::path>(cpr::fs::path{GetBasePath(argv[0]).string() + \"data/\"});\n\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/get_tests.cpp",
    "content": "#include <chrono>\n#include <cpr/error.h>\n#include <gtest/gtest.h>\n\n#include <memory>\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include \"cpr/cprtypes.h\"\n#include \"cpr/redirect.h\"\n#include \"cpr/session.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(BasicTests, HelloWorldTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, HelloWorldStringViewUrlTest) {\n    Url url{static_cast<std::string_view>(server->GetBaseUrl() + \"/hello.html\")};\n    Response response = cpr::Get(url);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, HelloWorldNoInterfaceTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Interface iface{\"\"}; // Do not specify any specific interface\n    Response response = cpr::Get(url, iface);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, HelloWorldNoInterfaceStringViewTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Interface iface{std::string_view{}}; // Do not specify any specific interface\n    Response response = cpr::Get(url, iface);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, TimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, Timeout{0L});\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, BasicJsonTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Response response = cpr::Get(url);\n    std::string expected_text{\n            \"[\\n\"\n            \"  {\\n\"\n            \"    \\\"first_key\\\": \\\"first_value\\\",\\n\"\n            \"    \\\"second_key\\\": \\\"second_value\\\"\\n\"\n            \"  }\\n\"\n            \"]\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, ResourceNotFoundTest) {\n    Url url{server->GetBaseUrl() + \"/error.html\"};\n    Response response = cpr::Get(url);\n    std::string expected_text{\"Not Found\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(404, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, BadHostTest) {\n    Url url{\"http://bad_host/\"};\n    Response response = cpr::Get(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT.\n    EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(CookiesTests, BasicCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/basic_cookies.html\"};\n    Response response = cpr::Get(url);\n    cpr::Cookies res_cookies{response.cookies};\n    std::string expected_text{\"Basic Cookies\"};\n    cpr::Cookies expectedCookies{\n            {\"SID\", \"31d4d96e407aad42\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"en-US\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {\n        EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());\n        EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());\n        EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());\n        EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());\n        EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());\n        EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());\n        EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());\n    }\n}\n\nTEST(CookiesTests, EmptyCookieTest) {\n    Url url{server->GetBaseUrl() + \"/empty_cookies.html\"};\n    Response response = cpr::Get(url);\n    cpr::Cookies res_cookies{response.cookies};\n    std::string expected_text{\"Empty Cookies\"};\n    cpr::Cookies expectedCookies{\n            {\"SID\", \"\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(expected_text, response.text);\n    for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {\n        EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());\n        EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());\n        EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());\n        EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());\n        EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());\n        EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());\n        EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());\n    }\n}\n\nTEST(CookiesTests, ClientSetCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/cookies_reflect.html\"};\n    Cookies cookies{\n            {\"SID\", \"31d4d96e407aad42\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"en-US\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    Response response = cpr::Get(url, cookies);\n    std::string expected_text{\"SID=31d4d96e407aad42; lang=en-US;\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CookiesTests, UnencodedCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/cookies_reflect.html\"};\n    Cookies cookies{\n            {\"SID\", \"31d4d  %$  96e407aad42\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"en-US\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    cookies.encode = false;\n    Response response = cpr::Get(url, cookies);\n    std::string expected_text{\"SID=31d4d  %$  96e407aad42; lang=en-US;\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterTests, SingleParameterTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Parameters parameters{{\"key\", \"value\"}};\n    Response response = cpr::Get(url, parameters);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key=value\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterTests, SingleParameterOnlyKeyTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Parameters parameters{{\"key\", \"\"}};\n    Response response = cpr::Get(url, parameters);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n}\n\nTEST(ParameterTests, MultipleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, Parameters{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}});\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key=value&hello=world&test=case\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterTests, MultipleDynamicParametersTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Parameters parameters{{\"key\", \"value\"}};\n    parameters.Add({\"hello\", \"world\"});\n    parameters.Add({\"test\", \"case\"});\n    Response response = cpr::Get(url, parameters);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key=value&hello=world&test=case\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationTests, BasicAuthenticationSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationTests, BasicBearerSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/bearer_token.html\"};\n#if CPR_LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0\n    Response response = cpr::Get(url, Bearer{\"the_token\"});\n#else\n    Response response = cpr::Get(url, Header{{\"Authorization\", \"Bearer the_token\"}});\n#endif\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationTests, BasicDigestSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/digest_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::DIGEST});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAthenticationParameterTests, BasicAuthenticationSuccessSingleParameterTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key=value&hello=world&test=case\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessSingleParameterReverseTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?key=value&hello=world&test=case\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n    EXPECT_EQ(std::string{\"case\"}, response.header[\"test\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderReverseTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n    EXPECT_EQ(std::string{\"case\"}, response.header[\"test\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationTests, BasicAuthenticationNullFailureTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url);\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationTests, BasicAuthenticationFailureTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureSingleParameterTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"hello\", \"world\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureMultipleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"key\", \"value\"}, {\"hello\", \"world\"}, {\"test\", \"case\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?key=value&hello=world&test=case\"}, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderJsonTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Response response = cpr::Get(url, Header{{\"content-type\", \"application/json\"}});\n    std::string expected_text{\n            \"[\\n\"\n            \"  {\\n\"\n            \"    \\\"first_key\\\": \\\"first_value\\\",\\n\"\n            \"    \\\"second_key\\\": \\\"second_value\\\"\\n\"\n            \"  }\\n\"\n            \"]\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectNoneTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url);\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectUpdateHeaderAddSessionTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetHeader(Header{{\"Header1\", \"Value1\"}});\n    session.SetUrl(url);\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"Value1\"}, response.header[\"Header1\"]);\n    EXPECT_EQ(std::string{}, response.header[\"Header2\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n\n    session.UpdateHeader(Header{{\"Header2\", \"Value2\"}});\n    response = session.Get();\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"Value1\"}, response.header[\"Header1\"]);\n    EXPECT_EQ(std::string{\"Value2\"}, response.header[\"Header2\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\n/**\n * Test case for #532\n * https://github.com/whoshuu/cpr/issues/532\n **/\nTEST(HeaderTests, SessionHeaderReflectTest) {\n    std::unique_ptr<cpr::Session> session(new cpr::Session());\n    session->SetUrl({server->GetBaseUrl() + \"/header_reflect.html\"});\n    session->SetBody(\"Some Body to post\");\n    session->SetHeader({{\"Content-Type\", \"application/json\"}});\n    cpr::Response response = session->Post();\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(std::string{\"Header reflect POST\"}, response.text);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"Content-Type\"]);\n}\n\nTEST(HeaderTests, HeaderReflectUpdateHeaderUpdateSessionTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetHeader(Header{{\"Header1\", \"Value1\"}});\n    session.SetUrl(url);\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"Value1\"}, response.header[\"Header1\"]);\n    EXPECT_EQ(std::string{}, response.header[\"Header2\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n\n    session.UpdateHeader(Header{{\"Header1\", \"Value2\"}});\n    response = session.Get();\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"Value2\"}, response.header[\"Header1\"]);\n    EXPECT_EQ(std::string{}, response.header[\"Header2\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectEmptyTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectSingleTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectMultipleTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}, {\"key\", \"value\"}, {\"test\", \"case\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n    EXPECT_EQ(std::string{\"case\"}, response.header[\"test\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, HeaderReflectCaseInsensitiveTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"HeLlO\", \"wOrLd\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"HELLO\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hElLo\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeaderTests, SetEmptyHeaderTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectNoneParametersTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectEmptyParametersTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{}, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectSingleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectMultipleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}, {\"key\", \"value\"}, {\"test\", \"case\"}}, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n    EXPECT_EQ(std::string{\"case\"}, response.header[\"test\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Header{{\"HeLlO\", \"wOrLd\"}}, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"HELLO\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hElLo\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectEmptyParametersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}}, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectSingleParametersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectMultipleParametersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}}, Header{{\"hello\", \"world\"}, {\"key\", \"value\"}, {\"test\", \"case\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n    EXPECT_EQ(std::string{\"case\"}, response.header[\"test\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersReverseTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}, {\"three\", \"four\"}, {\"five\", \"six\"}}, Header{{\"HeLlO\", \"wOrLd\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two&three=four&five=six\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hello\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"HELLO\"]);\n    EXPECT_EQ(std::string{\"wOrLd\"}, response.header[\"hElLo\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{}, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderABTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{}, Header{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderACTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}}, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderADTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}}, Header{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{}, Header{{\"hello\", \"world\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}}, Header{{\"hello\", \"world\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBBTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBCTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Header{}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBDTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Header{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCBTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCCTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCDTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Parameters{{\"one\", \"two\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{}, Parameters{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDBTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{}, Parameters{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDCTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{}, Parameters{{\"one\", \"two\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDDTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{}, Parameters{{\"one\", \"two\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}}, Parameters{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}}, Parameters{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}}, Parameters{{\"one\", \"two\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}}, Parameters{{\"one\", \"two\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEBTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderECTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEDTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{}, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Parameters{}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Parameters{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Header{{\"hello\", \"world\"}}, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFATest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFBTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFCTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFDTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFETest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFFTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFGTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}});\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFHTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Get(url, Parameters{{\"one\", \"two\"}}, Authentication{\"user\", \"bad_password\", AuthMode::BASIC}, Header{{\"hello\", \"world\"}});\n    EXPECT_EQ(\"Unauthorized\", response.text);\n    EXPECT_EQ(Url{url + \"?one=two\"}, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(GetRedirectTests, RedirectTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Response response = cpr::Get(url, Redirect(false));\n    std::string expected_text{\"Moved Temporarily\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(302, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(GetRedirectTests, ZeroMaxRedirectsSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, Redirect(0L));\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(GetRedirectTests, ZeroMaxRedirectsFailureTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Response response = cpr::Get(url, Redirect(0L));\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(301, response.status_code);\n    EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);\n}\n\nTEST(GetRedirectTests, BasicAuthenticationRedirectSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Response response = cpr::Get(url, Authentication{\"user\", \"password\", AuthMode::BASIC}, Header{{\"RedirectLocation\", \"basic_auth.html\"}}, Redirect(true, true));\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    std::string resultUrl = \"http://user:password@127.0.0.1:\" + std::to_string(server->GetPort()) + \"/basic_auth.html\";\n    EXPECT_EQ(response.url, resultUrl);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, RequestBodyTest) {\n    Url url{server->GetBaseUrl() + \"/body_get.html\"};\n    Body body{\"message=abc123\"};\n    Response response = cpr::Get(url, body);\n    std::string expected_text{\"abc123\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BasicTests, RequestBodyStringViewTest) {\n    Url url{server->GetBaseUrl() + \"/body_get.html\"};\n    Body body{static_cast<std::string_view>(\"message=abc123\")};\n    Response response = cpr::Get(url, body);\n    std::string expected_text{\"abc123\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(LimitRateTests, HelloWorldTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, LimitRate(1024, 1024));\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/head_tests.cpp",
    "content": "#include <chrono>\n#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(HeadTests, BasicHeadTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, ComplexHeadTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, ResourceNotFoundHeadTest) {\n    Url url{server->GetBaseUrl() + \"/error.html\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(404, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, BadHostHeadTest) {\n    Url url{\"http://bad_host/\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT.\n    EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(HeadTests, CookieHeadTest) {\n    Url url{server->GetBaseUrl() + \"/basic_cookies.html\"};\n    Response response = cpr::Head(url);\n    cpr::Cookies expectedCookies{\n            {\"SID\", \"31d4d96e407aad42\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"en-US\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    cpr::Cookies res_cookies{response.cookies};\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {\n        EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());\n        EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());\n        EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());\n        EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());\n        EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());\n        EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());\n        EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());\n    }\n}\n\nTEST(HeadTests, ParameterHeadTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Parameters parameters{{\"key\", \"value\"}};\n    Response response = cpr::Head(url, parameters);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(Url{url + \"?key=value\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, AuthenticationSuccessHeadTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Head(url, Authentication{\"user\", \"password\", AuthMode::BASIC});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, AuthenticationNullFailureHeadTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, AuthenticationFailureHeadTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Response response = cpr::Head(url, Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(\"text/plain\", response.header[\"content-type\"]);\n    EXPECT_EQ(401, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, BearerSuccessHeadTest) {\n    Url url{server->GetBaseUrl() + \"/bearer_token.html\"};\n#if CPR_LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0\n    Response response = cpr::Get(url, Bearer{\"the_token\"});\n#else\n    Response response = cpr::Get(url, Header{{\"Authorization\", \"Bearer the_token\"}});\n#endif\n    EXPECT_EQ(std::string{\"Header reflect GET\"}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, DigestSuccessHeadTest) {\n    Url url{server->GetBaseUrl() + \"/digest_auth.html\"};\n    Response response = cpr::Head(url, Authentication{\"user\", \"password\", AuthMode::DIGEST});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, HeaderReflectNoneHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Head(url);\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, HeaderReflectEmptyHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Head(url, Header{});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, HeaderReflectHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Head(url, Header{{\"hello\", \"world\"}});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, SetEmptyHeaderHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Head(url, Header{{\"hello\", \"\"}});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(std::string{}, response.header[\"hello\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, RedirectHeadTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Response response = cpr::Head(url, Redirect(false));\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(302, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, ZeroMaxRedirectsHeadTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Head(url, Redirect(0L));\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(HeadTests, BasicHeadAsyncTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::HeadAsync(url));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        EXPECT_EQ(std::string{}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/httpServer.cpp",
    "content": "#include \"httpServer.hpp\"\n#include <array>\n#include <chrono>\n#include <ctime>\n#include <iomanip>\n#include <sstream>\n#include <string>\n#include <system_error>\n#include <thread>\n\nnamespace cpr {\n\nstd::string HttpServer::GetBaseUrl() {\n    return \"http://127.0.0.1:\" + std::to_string(GetPort());\n}\n\nuint16_t HttpServer::GetPort() {\n    // Unassigned port number in the ephemeral range\n    return 61936;\n}\n\nmg_connection* HttpServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) {\n    // Based on: https://mongoose.ws/tutorials/http-server/\n    mg_mgr_init(mgr);\n    std::string port = std::to_string(GetPort());\n    mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this);\n    if (!c) {\n        throw std::system_error(errno, std::system_category(), \"Failed to listen at port \" + port);\n    }\n    return c;\n}\n\nvoid HttpServer::acceptConnection(mg_connection* /* conn */) {}\n\nvoid HttpServer::OnRequestHello(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"OPTIONS\"}) {\n        OnRequestOptions(conn, msg);\n    } else {\n        std::string response{\"Hello world!\"};\n        std::string headers = \"Content-Type: text/html\\r\\n\";\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestRoot(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"OPTIONS\"}) {\n        OnRequestOptions(conn, msg);\n    } else {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestNotFound(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"OPTIONS\"}) {\n        OnRequestOptions(conn, msg);\n    } else {\n        std::string errorMessage{\"Not Found\"};\n        SendError(conn, 404, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestOptions(mg_connection* conn, mg_http_message* /*msg*/) {\n    std::string headers =\n            \"Content-Type: text/plain\\r\\n\"\n            \"Access-Control-Allow-Origin: *\\r\\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS\\r\\n\"\n            \"Access-Control-Max-Age: 3600\\r\\n\";\n\n    std::string response;\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestTimeout(mg_connection* conn, mg_http_message* msg) {\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    OnRequestHello(conn, msg);\n}\n\nvoid HttpServer::OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg) {\n    std::this_thread::sleep_for(std::chrono::seconds(2));\n    OnRequestHello(conn, msg);\n}\n\n// Send the header, then send \"Hello world!\" every 100ms\n// For this, we use a mongoose timer\nvoid HttpServer::OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* /* msg */, TimerArg* timer_arg) {\n    std::string response{\"Hello world!\"};\n    mg_printf(conn, \"HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\nContent-Length: %d\\r\\n\\r\\n\", response.length() * 20);\n    timer_arg->connection_id = conn->id;\n    mg_timer_init(\n            &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT,\n            // The following lambda function gets executed each time the timer is called.\n            // It sends \"Hello world!\" to the client each 100ms at most 20 times.\n            [](void* arg) {\n                TimerArg* timer_arg = static_cast<TimerArg*>(arg);\n                if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) {\n                    std::string response{\"Hello world!\"};\n                    mg_send(timer_arg->connection, response.c_str(), response.length());\n                    ++timer_arg->counter;\n                } else {\n                    timer_arg->counter = 20; // Make sure that this timer is never called again\n                }\n            },\n            timer_arg);\n}\n\n// Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed\n// The nested call to mg_mgr_poll can lead to problems otherwise\nvoid HttpServer::OnRequestLowSpeed(mg_connection* conn, mg_http_message* /*msg*/, mg_mgr* mgr) {\n    std::string response{\"Hello world!\"};\n    mg_printf(conn, \"HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\nContent-Length: %d\\r\\n\\r\\n\", response.length());\n    mg_timer_add(\n            mgr, 2000, MG_TIMER_ONCE,\n            [](void* connection) {\n                std::string response{\"Hello world!\"};\n                mg_send(static_cast<mg_connection*>(connection), response.c_str(), response.length());\n            },\n            conn);\n}\n\n// Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed\n// The nested call to mg_mgr_poll can lead to problems otherwise\nvoid HttpServer::OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* /*msg*/, TimerArg* timer_arg) {\n    std::string response{'a'};\n    mg_printf(conn, \"HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\nContent-Length: %d\\r\\n\\r\\n\", response.length() * 20);\n\n    mg_timer_init(\n            &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT,\n            // The following lambda function gets executed each time the timer is called.\n            // It first waits for 2 seconds, then sends \"a\" to the client each 100ms at most 20 times.\n            [](void* arg) {\n                TimerArg* timer_arg = static_cast<TimerArg*>(arg);\n                if (timer_arg->counter == 0) {\n                    std::this_thread::sleep_for(std::chrono::seconds(2));\n                }\n                if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) {\n                    std::string response{'a'};\n                    mg_send(timer_arg->connection, response.c_str(), response.length());\n                    ++timer_arg->counter;\n                } else {\n                    timer_arg->counter = 20; // Make sure that this timer is never called again\n                }\n            },\n            timer_arg);\n}\n\nstd::string HttpServer::GetCookieExpiresIn100HoursString() {\n    static const std::chrono::system_clock::time_point expires = GetCookieExpiresIn100HoursTimePoint();\n    const std::time_t timeT = std::chrono::system_clock::to_time_t(expires);\n    const std::tm* utcTimeT = std::gmtime(&timeT); // NOLINT (concurrency-mt-unsafe) not relevant here\n\n    std::stringstream ss;\n    ss << std::put_time(utcTimeT, \"%a, %d %b %Y %T GMT\");\n\n    return ss.str();\n}\n\nstd::chrono::system_clock::time_point HttpServer::GetCookieExpiresIn100HoursTimePoint() {\n    // Cookie timepoints have a maximum resolution of seconds so floor it to that.\n    static const std::chrono::system_clock::time_point expires = std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now() + std::chrono::hours(100));\n    return expires;\n}\n\nvoid HttpServer::OnRequestBasicCookies(mg_connection* conn, mg_http_message* /*msg*/) {\n    const std::string expires = GetCookieExpiresIn100HoursString();\n\n    std::string cookie1{\"SID=31d4d96e407aad42; Expires=\" + expires + \"; Secure\"};\n    std::string cookie2{\"lang=en-US; Expires=\" + expires + \"; Secure\"};\n    std::string headers =\n            \"Content-Type: text/html\\r\\n\"\n            \"Set-Cookie: \" +\n            cookie1 +\n            \"\\r\\n\"\n            \"Set-Cookie: \" +\n            cookie2 + \"\\r\\n\";\n    std::string response{\"Basic Cookies\"};\n\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestEmptyCookies(mg_connection* conn, mg_http_message* /*msg*/) {\n    const std::string expires = GetCookieExpiresIn100HoursString();\n\n    std::string cookie1{\"SID=; Expires=\" + expires + \"; Secure\"};\n    std::string cookie2{\"lang=; Expires=\" + expires + \"; Secure\"};\n    std::string headers =\n            \"Content-Type: text/html\\r\\n\"\n            \"Set-Cookie: \" +\n            cookie1 +\n            \"\\r\\n\"\n            \"Set-Cookie: \" +\n            cookie2 + \"\\r\\n\";\n    std::string response{\"Empty Cookies\"};\n\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg) {\n    mg_str* request_cookies{nullptr};\n    if ((request_cookies = mg_http_get_header(msg, \"Cookie\")) == nullptr) {\n        std::string errorMessage{\"Cookie not found\"};\n        SendError(conn, 400, errorMessage);\n        return;\n    }\n    std::string cookie_str{request_cookies->ptr, request_cookies->len};\n    std::string headers = \"Content-Type: text/html\\r\\n\";\n    mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str());\n}\n\nvoid HttpServer::OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg) {\n    const std::string expires = GetCookieExpiresIn100HoursString();\n\n    mg_str* request_cookies{nullptr};\n    std::string cookie_str;\n    if ((request_cookies = mg_http_get_header(msg, \"Cookie\")) != nullptr) {\n        cookie_str = std::string{request_cookies->ptr, request_cookies->len};\n    }\n\n    if (cookie_str.find(\"SID=31d4d96e407aad42\") == std::string::npos) {\n        std::string cookie1{\"SID=31d4d96e407aad42; Expires=\" + expires + \"; Secure\"};\n        std::string cookie2{\"lang=en-US; Expires=\" + expires + \"; Secure\"};\n        std::string headers =\n                \"Content-Type: text/html\\r\\n\"\n                \"Location: http://127.0.0.1:61936/redirection_with_changing_cookies.html\\r\\n\"\n                \"Set-Cookie: \" +\n                cookie1 +\n                \"\\r\\n\"\n                \"Set-Cookie: \" +\n                cookie2 + \"\\r\\n\";\n\n        mg_http_reply(conn, 302, headers.c_str(), \"\");\n    } else {\n        cookie_str = \"Received cookies are: \" + cookie_str;\n        std::string headers = \"Content-Type: text/html\\r\\n\";\n        mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg) {\n    mg_str* requested_auth;\n    std::string auth{\"Basic\"};\n    if ((requested_auth = mg_http_get_header(msg, \"Authorization\")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) {\n        std::string errorMessage{\"Unauthorized\"};\n        SendError(conn, 401, errorMessage);\n        return;\n    }\n    std::string auth_string{requested_auth->ptr, requested_auth->len};\n    size_t basic_token = auth_string.find(' ') + 1;\n    auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token);\n    auth_string = Base64Decode(auth_string);\n    size_t colon = auth_string.find(':');\n    std::string username = auth_string.substr(0, colon);\n    std::string password = auth_string.substr(colon + 1, auth_string.length() - colon - 1);\n    if (username == \"user\" && password == \"password\") {\n        OnRequestHeaderReflect(conn, msg);\n    } else {\n        std::string errorMessage{\"Unauthorized\"};\n        SendError(conn, 401, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg) {\n    mg_str* requested_auth;\n    std::string auth{\"Bearer\"};\n    if ((requested_auth = mg_http_get_header(msg, \"Authorization\")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) {\n        std::string errorMessage{\"Unauthorized\"};\n        SendError(conn, 401, errorMessage);\n        return;\n    }\n    std::string auth_string{requested_auth->ptr, requested_auth->len};\n    size_t basic_token = auth_string.find(' ') + 1;\n    auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token);\n    if (auth_string == \"the_token\") {\n        OnRequestHeaderReflect(conn, msg);\n    } else {\n        std::string errorMessage{\"Unauthorized\"};\n        SendError(conn, 401, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestBasicJson(mg_connection* conn, mg_http_message* /*msg*/) {\n    std::string response =\n            \"[\\n\"\n            \"  {\\n\"\n            \"    \\\"first_key\\\": \\\"first_value\\\",\\n\"\n            \"    \\\"second_key\\\": \\\"second_value\\\"\\n\"\n            \"  }\\n\"\n            \"]\";\n    std::string headers = \"Content-Type: application/json\\r\\n\";\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg) {\n    if (std::string_view{msg->method.ptr, msg->method.len} == \"GET\") {\n        if (msg->body.len > 0) {\n            std::string errorMessage{\"Bad Request: GET shouldn't contain a body.\"};\n            SendError(conn, 400, errorMessage);\n            return;\n        } else if (msg->chunk.len > 0) {\n            std::string errorMessage{\"Bad Request: GET shouldn't contain a body.\"};\n            SendError(conn, 400, errorMessage);\n            return;\n        }\n    }\n\n    std::string response = \"Header reflect \" + std::string{msg->method.ptr, msg->method.len};\n    std::string headers;\n    bool hasContentTypeHeader = false;\n    for (const mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"Content-Type\"} == name) {\n            hasContentTypeHeader = true;\n        }\n\n        if (std::string{\"Host\"} != name && std::string{\"Accept\"} != name && std::string{\"Content-Length\"} != name) {\n            if (header.value.ptr) {\n                headers.append(name + \": \" + std::string(header.value.ptr, header.value.len) + \"\\r\\n\");\n            }\n        }\n    }\n\n    if (!hasContentTypeHeader) {\n        headers.append(\"Content-Type: text/html\\r\\n\");\n    }\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg) {\n    // Get the requested target location:\n    std::string location;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"RedirectLocation\"} == name) {\n            location = std::string(header.value.ptr, header.value.len);\n            break;\n        }\n    }\n\n    // Check if the request contains a valid location, else default to 'hello.html':\n    if (location.empty()) {\n        location = \"hello.html\";\n    }\n    std::string headers = \"Location: \" + location + \"\\r\\n\";\n    std::string response = \"Moved Temporarily\";\n    mg_http_reply(conn, 302, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg) {\n    // Get the requested target location:\n    std::string location;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"RedirectLocation\"} == name) {\n            location = std::string(header.value.ptr, header.value.len);\n            break;\n        }\n    }\n\n    // Check if the request contains a valid location, else default to 'hello.html':\n    if (location.empty()) {\n        location = \"hello.html\";\n    }\n    std::string headers = \"Location: \" + location + \"\\r\\n\";\n    std::string response = \"Moved Permanently\";\n\n    mg_http_reply(conn, 301, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg) {\n    // Get the requested target location:\n    std::string location;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"RedirectLocation\"} == name) {\n            location = std::string(header.value.ptr, header.value.len);\n            break;\n        }\n    }\n\n    if (location.empty()) {\n        std::string errorMessage{\"Redirect location missing\"};\n        SendError(conn, 405, errorMessage);\n        return;\n    }\n\n    std::string headers = \"Location: \" + location + \"\\r\\n\";\n    std::string response = \"Moved Permanently\";\n\n    mg_http_reply(conn, 301, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestTwoRedirects(mg_connection* conn, mg_http_message* /*msg*/) {\n    std::string response = \"Moved Permanently\";\n    std::string headers = \"Location: permanent_redirect.html\\r\\n\";\n    mg_http_reply(conn, 301, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestUrlPost(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} != std::string{\"POST\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n        return;\n    }\n\n    std::string headers = \"Content-Type: application/json\\r\\n\";\n\n    std::array<char, 100> x{0};\n    std::array<char, 100> y{0};\n    mg_http_get_var(&(msg->body), \"x\", x.data(), x.size());\n    mg_http_get_var(&(msg->body), \"y\", y.data(), y.size());\n    std::string x_string{x.data()};\n    std::string y_string{y.data()};\n    std::string response;\n    if (y_string.empty()) {\n        response = std::string{\n                \"{\\n\"\n                \"  \\\"x\\\": \" +\n                x_string +\n                \"\\n\"\n                \"}\"};\n    } else {\n        response = std::string{\n                \"{\\n\"\n                \"  \\\"x\\\": \" +\n                x_string +\n                \",\\n\"\n                \"  \\\"y\\\": \" +\n                y_string +\n                \",\\n\"\n                \"  \\\"sum\\\": \" +\n                std::to_string(atoi(x.data()) + atoi(y.data())) +\n                \"\\n\"\n                \"}\"};\n    }\n    mg_http_reply(conn, 201, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestBodyGet(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} != std::string{\"GET\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n        return;\n    }\n    std::array<char, 100> message{};\n    mg_http_get_var(&(msg->body), \"message\", message.data(), message.size());\n    if (msg->body.len <= 0) {\n        std::string errorMessage{\"No Content\"};\n        SendError(conn, 405, errorMessage);\n        return;\n    }\n    std::string response = message.data();\n    std::string headers = \"Content-Type: text/html\\r\\n\";\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestJsonPost(mg_connection* conn, mg_http_message* msg) {\n    mg_str* content_type{nullptr};\n    if ((content_type = mg_http_get_header(msg, \"Content-Type\")) == nullptr || std::string{content_type->ptr, content_type->len} != \"application/json\") {\n        std::string errorMessage{\"Unsupported Media Type\"};\n        SendError(conn, 415, errorMessage);\n        return;\n    }\n\n    std::string headers = \"Content-Type: application/json\\r\\n\";\n    mg_http_reply(conn, 201, headers.c_str(), msg->body.ptr);\n}\n\nvoid HttpServer::OnRequestFormPost(mg_connection* conn, mg_http_message* msg) {\n    size_t pos{0};\n    mg_http_part part{};\n\n    std::string headers = \"Content-Type: application/json\\r\\n\";\n    std::string response{};\n    response += \"{\\n\";\n    while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) {\n        response += \"  \\\"\" + std::string(part.name.ptr, part.name.len) + \"\\\": \\\"\";\n        if (!std::string(part.filename.ptr, part.filename.len).empty()) {\n            response += std::string(part.filename.ptr, part.filename.len) + \"=\";\n        }\n        response += std::string(part.body.ptr, part.body.len) + \"\\\",\\n\";\n    }\n    response.erase(response.find_last_not_of(\",\\n\") + 1);\n    response += \"\\n}\";\n\n    mg_http_reply(conn, 201, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg) {\n    size_t pos{0};\n    mg_http_part part{};\n\n    std::string headers = \"Content-Type: application/json\\r\\n\";\n    std::string response{};\n    response += \"{\\n\";\n    while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) {\n        response += \"  \\\"\" + std::string(part.name.ptr, part.name.len) + \"\\\": \\\"\";\n        if (!std::string(part.filename.ptr, part.filename.len).empty()) {\n            response += std::string(part.filename.ptr, part.filename.len) + \"=\";\n        }\n        response += std::string(part.body.ptr, part.body.len) + \"\\\",\\n\";\n    }\n    response.erase(response.find_last_not_of(\",\\n\") + 1);\n    response += \"\\n}\";\n\n    mg_http_reply(conn, 201, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestDelete(mg_connection* conn, mg_http_message* msg) {\n    bool has_json_header = false;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name = std::string(header.name.ptr, header.name.len);\n        std::string value = std::string(header.value.ptr, header.value.len);\n        if (std::string{\"Content-Type\"} == name && std::string{\"application/json\"} == value) {\n            has_json_header = true;\n            break;\n        }\n    }\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"DELETE\"}) {\n        std::string headers;\n        std::string response = \"Patch success\";\n        if (!has_json_header) {\n            headers = \"Content-Type: text/html\\r\\n\";\n            response = \"Delete success\";\n        } else {\n            headers = \"Content-Type: application/json\\r\\n\";\n            response = std::string{msg->body.ptr, msg->body.len};\n        }\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    } else {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"DELETE\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    } else {\n        std::string headers = \"Content-Type: text/html\\r\\n\";\n        std::string response = \"Delete success\";\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestPut(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"PUT\"}) {\n        std::array<char, 100> x{0};\n        std::array<char, 100> y{0};\n        mg_http_get_var(&(msg->body), \"x\", x.data(), x.size());\n        mg_http_get_var(&(msg->body), \"y\", y.data(), y.size());\n        std::string x_string{x.data()};\n        std::string y_string{y.data()};\n        std::string headers = \"Content-Type: application/json\\r\\n\";\n        std::string response;\n        if (y_string.empty()) {\n            response = std::string{\n                    \"{\\n\"\n                    \"  \\\"x\\\": \" +\n                    x_string +\n                    \"\\n\"\n                    \"}\"};\n        } else {\n            response = std::string{\n                    \"{\\n\"\n                    \"  \\\"x\\\": \" +\n                    x_string +\n                    \",\\n\"\n                    \"  \\\"y\\\": \" +\n                    y_string +\n                    \",\\n\"\n                    \"  \\\"sum\\\": \" +\n                    std::to_string(atoi(x.data()) + atoi(y.data())) +\n                    \"\\n\"\n                    \"}\"};\n        }\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    } else {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestPostReflect(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} != std::string{\"POST\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    }\n\n    std::string response = std::string{msg->body.ptr, msg->body.len};\n    std::string headers;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n\n        std::string name{header.name.ptr, header.name.len};\n        if (std::string{\"Host\"} != name && std::string{\"Accept\"} != name) {\n            if (header.value.ptr) {\n                headers.append(name + \": \" + std::string(header.value.ptr, header.value.len) + \"\\r\\n\");\n            }\n        }\n    }\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"PUT\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    } else {\n        std::string headers = \"Content-Type: text/html\\r\\n\";\n        std::string response = \"Delete success\";\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestPatch(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"PATCH\"}) {\n        std::array<char, 100> x{0};\n        std::array<char, 100> y{0};\n        mg_http_get_var(&(msg->body), \"x\", x.data(), x.size());\n        mg_http_get_var(&(msg->body), \"y\", y.data(), y.size());\n        std::string x_string{x.data()};\n        std::string y_string{y.data()};\n        std::string headers = \"Content-Type: application/json\\r\\n\";\n        std::string response;\n        if (y_string.empty()) {\n            response = std::string{\n                    \"{\\n\"\n                    \"  \\\"x\\\": \" +\n                    x_string +\n                    \"\\n\"\n                    \"}\"};\n        } else {\n            response = std::string{\n                    \"{\\n\"\n                    \"  \\\"x\\\": \" +\n                    x_string +\n                    \",\\n\"\n                    \"  \\\"y\\\": \" +\n                    y_string +\n                    \",\\n\"\n                    \"  \\\"sum\\\": \" +\n                    std::to_string(atoi(x.data()) + atoi(y.data())) +\n                    \"\\n\"\n                    \"}\"};\n        }\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    } else {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    }\n}\n\nvoid HttpServer::OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"PATCH\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    } else {\n        std::string headers = \"Content-Type: text/html\\r\\n\";\n        std::string response = \"Delete success\";\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg) {\n    if (std::string{msg->method.ptr, msg->method.len} == std::string{\"DOWNLOAD\"}) {\n        std::string errorMessage{\"Method Not Allowed\"};\n        SendError(conn, 405, errorMessage);\n    } else {\n        std::string encoding;\n        std::string range;\n        std::vector<std::pair<int64_t, int64_t>> ranges;\n\n        for (mg_http_header& header : msg->headers) {\n            if (!header.name.ptr) {\n                continue;\n            }\n\n            std::string name = std::string(header.name.ptr, header.name.len);\n            if (std::string{\"Accept-Encoding\"} == name) {\n                encoding = std::string(header.value.ptr, header.value.len);\n            } else if (std::string{\"Range\"} == name) {\n                range = std::string(header.value.ptr, header.value.len);\n            }\n        }\n        if (encoding.find(\"gzip\") == std::string::npos) {\n            std::string errorMessage{\"Invalid encoding: \" + encoding};\n            SendError(conn, 405, errorMessage);\n            return;\n        }\n        if (!range.empty()) {\n            std::string::size_type eq_pos = range.find('=');\n            if (eq_pos == std::string::npos) {\n                std::string errorMessage{\"Invalid range header: \" + range};\n                SendError(conn, 405, errorMessage);\n                return;\n            }\n\n            int64_t current_start_index = eq_pos + 1;\n            int64_t current_end_index;\n            std::string::size_type range_len = range.length();\n            std::string::size_type com_pos;\n            std::string::size_type sep_pos;\n            bool more_ranges_exists;\n\n            do {\n                com_pos = range.find(',', current_start_index);\n                if (com_pos < range_len) {\n                    current_end_index = com_pos - 1;\n                } else {\n                    current_end_index = range_len - 1;\n                }\n\n                std::pair<int64_t, int64_t> current_range{0, -1};\n\n                sep_pos = range.find('-', current_start_index);\n                if (sep_pos == std::string::npos) {\n                    std::string errorMessage{\"Invalid range format \" + range.substr(current_start_index, current_end_index)};\n                    SendError(conn, 405, errorMessage);\n                    return;\n                }\n                if (sep_pos == eq_pos + 1) {\n                    std::string errorMessage{\"Suffix ranage not supported: \" + range.substr(current_start_index, current_end_index)};\n                    SendError(conn, 405, errorMessage);\n                    return;\n                }\n\n                current_range.first = std::strtoll(range.substr(current_start_index, sep_pos - 1).c_str(), nullptr, 10);\n                if (current_range.first == LLONG_MAX || current_range.first == LLONG_MIN) {\n                    std::string errorMessage{\"Start range is invalid number: \" + range.substr(current_start_index, current_end_index)};\n                    SendError(conn, 405, errorMessage);\n                    return;\n                }\n\n                std::string er_str = range.substr(sep_pos + 1, current_end_index);\n                if (!er_str.empty()) {\n                    current_range.second = std::strtoll(er_str.c_str(), nullptr, 10);\n                    if (current_range.second == 0 || current_range.second == LLONG_MAX || current_range.second == LLONG_MIN) {\n                        std::string errorMessage{\"End range is invalid number: \" + range.substr(current_start_index, current_end_index)};\n                        SendError(conn, 405, errorMessage);\n                        return;\n                    }\n                }\n\n                ranges.push_back(current_range);\n\n                if (current_end_index >= static_cast<int64_t>(range.length() - 1)) {\n                    more_ranges_exists = false;\n                } else {\n                    // Multiple ranges are separated by ', '\n                    more_ranges_exists = true;\n                    current_start_index = current_end_index + 3;\n                }\n            } while (more_ranges_exists);\n        }\n\n        std::string response = \"Download!\";\n        int status_code = 200;\n        std::string headers;\n\n        if (!ranges.empty()) {\n            // Create response parts\n            std::vector<std::string> responses;\n            for (std::pair<int64_t, int64_t> local_range : ranges) {\n                if (local_range.first >= 0) {\n                    if (local_range.first >= (int64_t) response.length()) {\n                        responses.push_back(\"\");\n                    } else if (local_range.second == -1 || local_range.second >= (int64_t) response.length()) {\n                        responses.push_back(response.substr(local_range.first));\n                    } else {\n                        responses.push_back(response.substr(local_range.first, local_range.second - local_range.first + 1));\n                    }\n                }\n            }\n\n            if (responses.size() > 1) {\n                // Create mime multipart response\n                std::string boundary = \"3d6b6a416f9b5\";\n                status_code = 206;\n                response.clear();\n\n                for (size_t i{0}; i < responses.size(); ++i) {\n                    response += \"--\" + boundary + \"\\n\";\n                    response += \"Content-Range: bytes \" + std::to_string(ranges.at(i).first) + \"-\";\n                    if (ranges.at(i).second > 0) {\n                        response += std::to_string(ranges.at(i).second);\n                    } else {\n                        response += std::to_string(responses.at(i).length());\n                    }\n                    response += \"/\" + std::to_string(responses.at(i).length()) + \"\\n\\n\";\n                    response += responses.at(i) + \"\\n\";\n                }\n                response += \"--\" + boundary + \"--\";\n            } else {\n                if (ranges.at(0).second == -1 || ranges.at(0).second >= (int64_t) response.length()) {\n                    status_code = ranges.at(0).first > 0 ? 206 : 200;\n                } else {\n                    status_code = 206;\n                }\n                response = responses.at(0);\n\n                if (status_code == 206) {\n                    headers = \"Content-Range: bytes \" + std::to_string(ranges.at(0).first) + \"-\";\n                    if (ranges.at(0).second > 0) {\n                        headers += std::to_string(ranges.at(0).second);\n                    } else {\n                        headers += std::to_string(response.length());\n                    }\n                    headers += \"/\" + std::to_string(response.length());\n                }\n            }\n        }\n        if (!headers.empty()) {\n            headers += \"\\r\\n\";\n        }\n\n        mg_http_reply(conn, status_code, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg) {\n    std::string response;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"Accept-Encoding\"} == name) {\n            response = std::string(header.value.ptr, header.value.len);\n        }\n    }\n    std::string headers = \"Content-Type: text/html\\r\\n\";\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg) {\n    std::string response;\n    for (mg_http_header& header : msg->headers) {\n        if (!header.name.ptr) {\n            continue;\n        }\n        std::string name = std::string(header.name.ptr, header.name.len);\n        if (std::string{\"Expect\"} == name) {\n            response = std::string(header.value.ptr, header.value.len);\n        }\n    }\n    std::string headers = \"Content-Type: text/html\\r\\n\";\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nvoid HttpServer::OnRequestGetDownloadFileLength(mg_connection* conn, mg_http_message* msg) {\n    auto method = std::string{msg->method.ptr, msg->method.len};\n    if (method == std::string{\"HEAD\"}) {\n        mg_http_reply(conn, 405, nullptr, \"\");\n    } else {\n        std::string response(\"this is a file content.\");\n        std::string headers = \"Content-Type: text/plain\\r\\n\";\n        mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n    }\n}\n\nvoid HttpServer::OnRequest(mg_connection* conn, mg_http_message* msg) {\n    std::string uri = std::string(msg->uri.ptr, msg->uri.len);\n\n    if (uri == \"/\") {\n        OnRequestRoot(conn, msg);\n    } else if (uri == \"/hello.html\") {\n        OnRequestHello(conn, msg);\n    } else if (uri == \"/timeout.html\") {\n        OnRequestTimeout(conn, msg);\n    } else if (uri == \"/long_timeout.html\") {\n        OnRequestLongTimeout(conn, msg);\n    } else if (uri == \"/low_speed_timeout.html\") {\n        timer_args.emplace_back(std::make_unique<TimerArg>(&mgr, conn, mg_timer{}));\n        OnRequestLowSpeedTimeout(conn, msg, timer_args.back().get());\n    } else if (uri == \"/low_speed.html\") {\n        OnRequestLowSpeed(conn, msg, &mgr);\n    } else if (uri == \"/low_speed_bytes.html\") {\n        timer_args.emplace_back(std::make_unique<TimerArg>(&mgr, conn, mg_timer{}));\n        OnRequestLowSpeedBytes(conn, msg, timer_args.back().get());\n    } else if (uri == \"/basic_cookies.html\") {\n        OnRequestBasicCookies(conn, msg);\n    } else if (uri == \"/empty_cookies.html\") {\n        OnRequestEmptyCookies(conn, msg);\n    } else if (uri == \"/cookies_reflect.html\") {\n        OnRequestCookiesReflect(conn, msg);\n    } else if (uri == \"/redirection_with_changing_cookies.html\") {\n        OnRequestRedirectionWithChangingCookies(conn, msg);\n    } else if (uri == \"/basic_auth.html\") {\n        OnRequestBasicAuth(conn, msg);\n    } else if (uri == \"/bearer_token.html\") {\n        OnRequestBearerAuth(conn, msg);\n    } else if (uri == \"/digest_auth.html\") {\n        OnRequestHeaderReflect(conn, msg);\n    } else if (uri == \"/basic.json\") {\n        OnRequestBasicJson(conn, msg);\n    } else if (uri == \"/header_reflect.html\") {\n        OnRequestHeaderReflect(conn, msg);\n    } else if (uri == \"/temporary_redirect.html\") {\n        OnRequestTempRedirect(conn, msg);\n    } else if (uri == \"/permanent_redirect.html\") {\n        OnRequestPermRedirect(conn, msg);\n    } else if (uri == \"/resolve_permanent_redirect.html\") {\n        OnRequestResolvePermRedirect(conn, msg);\n    } else if (uri == \"/two_redirects.html\") {\n        OnRequestTwoRedirects(conn, msg);\n    } else if (uri == \"/url_post.html\") {\n        OnRequestUrlPost(conn, msg);\n    } else if (uri == \"/body_get.html\") {\n        OnRequestBodyGet(conn, msg);\n    } else if (uri == \"/json_post.html\") {\n        OnRequestJsonPost(conn, msg);\n    } else if (uri == \"/form_post.html\") {\n        OnRequestFormPost(conn, msg);\n    } else if (uri == \"/post_file_upload.html\") {\n        OnRequestFileUploadPost(conn, msg);\n    } else if (uri == \"/post_reflect.html\") {\n        OnRequestPostReflect(conn, msg);\n    } else if (uri == \"/delete.html\") {\n        OnRequestDelete(conn, msg);\n    } else if (uri == \"/delete_unallowed.html\") {\n        OnRequestDeleteNotAllowed(conn, msg);\n    } else if (uri == \"/put.html\") {\n        OnRequestPut(conn, msg);\n    } else if (uri == \"/put_unallowed.html\") {\n        OnRequestPutNotAllowed(conn, msg);\n    } else if (uri == \"/patch.html\") {\n        OnRequestPatch(conn, msg);\n    } else if (uri == \"/patch_unallowed.html\") {\n        OnRequestPatchNotAllowed(conn, msg);\n    } else if (uri == \"/download_gzip.html\") {\n        OnRequestDownloadGzip(conn, msg);\n    } else if (uri == \"/local_port.html\") {\n        OnRequestLocalPort(conn, msg);\n    } else if (uri == \"/check_accept_encoding.html\") {\n        OnRequestCheckAcceptEncoding(conn, msg);\n    } else if (uri == \"/check_expect_100_continue.html\") {\n        OnRequestCheckExpect100Continue(conn, msg);\n    } else if (uri == \"/get_download_file_length.html\") {\n        OnRequestGetDownloadFileLength(conn, msg);\n    } else {\n        OnRequestNotFound(conn, msg);\n    }\n}\n\nvoid HttpServer::OnRequestLocalPort(mg_connection* conn, mg_http_message* /*msg*/) {\n    // send source port number as response for checking SetLocalPort/SetLocalPortRange\n    std::string headers = \"Content-Type: text/plain\\r\\n\";\n    // Convert from big endian to little endian\n    std::string response = std::to_string(AbstractServer::GetRemotePort(conn));\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "test/httpServer.hpp",
    "content": "#ifndef CPR_TEST_HTTP_SERVER_H\n#define CPR_TEST_HTTP_SERVER_H\n\n#include <string>\n\n#include \"abstractServer.hpp\"\n#include \"mongoose.h\"\n\nnamespace cpr {\nclass HttpServer : public AbstractServer {\n  public:\n    ~HttpServer() override = default;\n\n    std::string GetBaseUrl() override;\n    uint16_t GetPort() override;\n\n    void OnRequest(mg_connection* conn, mg_http_message* msg) override;\n\n    /**\n     * Returns the current date and time + 100 hours from when this function was invoked for the first time.\n     **/\n    static std::chrono::system_clock::time_point GetCookieExpiresIn100HoursTimePoint();\n\n    /**\n     * Returns the current date and time + 100 hours from when this function (or better GetCookieExpiresIn100HoursTimePoint()) was invoked for the first time as cookies expires string.\n     * For example: Wed, 30 Sep 2093 03:18:00 GMT\n     **/\n    static std::string GetCookieExpiresIn100HoursString();\n\n  private:\n    static void OnRequestHello(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestRoot(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestOptions(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestTimeout(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* msg, TimerArg* arg);\n    static void OnRequestLowSpeed(mg_connection* conn, mg_http_message* msg, mg_mgr* mgr);\n    static void OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* msg, TimerArg* arg);\n    static void OnRequestBasicCookies(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestEmptyCookies(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestBasicJson(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestTwoRedirects(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestUrlPost(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPostReflect(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestBodyGet(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestJsonPost(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestFormPost(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestDelete(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPut(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPatch(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestLocalPort(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestGetDownloadFileLength(mg_connection* conn, mg_http_message* msg);\n\n  protected:\n    mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override;\n    void acceptConnection(mg_connection* conn) override;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "test/httpsServer.cpp",
    "content": "#include \"httpsServer.hpp\"\n#include <system_error>\n\nnamespace cpr {\nHttpsServer::HttpsServer(fs::path&& baseDirPath, fs::path&& sslCertFileName, fs::path&& sslKeyFileName) : baseDirPath(baseDirPath.make_preferred().string()), sslCertFileName(sslCertFileName.make_preferred().string()), sslKeyFileName(sslKeyFileName.make_preferred().string()) {\n    // See https://mongoose.ws/tutorials/tls/\n    memset(static_cast<void*>(&tlsOpts), 0, sizeof(tlsOpts));\n    tlsOpts.cert = this->sslCertFileName.c_str();\n    tlsOpts.certkey = this->sslKeyFileName.c_str();\n}\n\nstd::string HttpsServer::GetBaseUrl() {\n    return \"https://127.0.0.1:\" + std::to_string(GetPort());\n}\n\nuint16_t HttpsServer::GetPort() {\n    // Unassigned port in the ephemeral range\n    return 61937;\n}\n\nmg_connection* HttpsServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) {\n    mg_mgr_init(mgr);\n\n    std::string port = std::to_string(GetPort());\n    mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this);\n    return c;\n}\n\nvoid HttpsServer::acceptConnection(mg_connection* conn) {\n    // See https://mongoose.ws/tutorials/tls/\n    mg_tls_init(conn, &tlsOpts);\n}\n\nvoid HttpsServer::OnRequest(mg_connection* conn, mg_http_message* msg) {\n    std::string uri = std::string(msg->uri.ptr, msg->uri.len);\n    if (uri == \"/hello.html\") {\n        OnRequestHello(conn, msg);\n    } else {\n        OnRequestNotFound(conn, msg);\n    }\n}\n\nvoid HttpsServer::OnRequestNotFound(mg_connection* conn, mg_http_message* /*msg*/) {\n    mg_http_reply(conn, 404, nullptr, \"Not Found\");\n}\n\nvoid HttpsServer::OnRequestHello(mg_connection* conn, mg_http_message* /*msg*/) {\n    std::string response{\"Hello world!\"};\n    std::string headers{\"Content-Type: text/html\\r\\n\"};\n    mg_http_reply(conn, 200, headers.c_str(), response.c_str());\n}\n\nconst std::string& HttpsServer::getBaseDirPath() const {\n    return baseDirPath;\n}\n\nconst std::string& HttpsServer::getSslCertFileName() const {\n    return sslCertFileName;\n}\n\nconst std::string& HttpsServer::getSslKeyFileName() const {\n    return sslKeyFileName;\n}\n\n} // namespace cpr\n"
  },
  {
    "path": "test/httpsServer.hpp",
    "content": "#ifndef CPR_TEST_HTTPS_SERVER_H\n#define CPR_TEST_HTTPS_SERVER_H\n\n#include <memory>\n#include <string>\n\n#include \"abstractServer.hpp\"\n#include \"cpr/cpr.h\"\n#include \"cpr/filesystem.h\"\n#include \"mongoose.h\"\n\nnamespace cpr {\nclass HttpsServer : public AbstractServer {\n  private:\n    // We don't use fs::path here, as this leads to problems using windows\n    const std::string baseDirPath;\n    const std::string sslCertFileName;\n    const std::string sslKeyFileName;\n    struct mg_tls_opts tlsOpts;\n\n  public:\n    explicit HttpsServer(fs::path&& baseDirPath, fs::path&& sslCertFileName, fs::path&& sslKeyFileName);\n    ~HttpsServer() override = default;\n\n    std::string GetBaseUrl() override;\n    uint16_t GetPort() override;\n\n    void OnRequest(mg_connection* conn, mg_http_message* msg) override;\n    static void OnRequestHello(mg_connection* conn, mg_http_message* msg);\n    static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg);\n\n    const std::string& getBaseDirPath() const;\n    const std::string& getSslCertFileName() const;\n    const std::string& getSslKeyFileName() const;\n\n  protected:\n    mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override;\n    void acceptConnection(mg_connection* conn) override;\n};\n} // namespace cpr\n\n#endif\n"
  },
  {
    "path": "test/interceptor_multi_tests.cpp",
    "content": "#include <gtest/gtest.h>\n#include <iostream>\n#include <memory>\n\n#include \"cpr/cpr.h\"\n#include \"cpr/interceptor.h\"\n#include \"cpr/response.h\"\n#include \"cpr/session.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nclass HiddenHelloWorldRedirectInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        std::shared_ptr<Session> session = multi.GetSessions().front().first;\n\n        // Save original url\n        Url old_url = session->GetFullRequestUrl();\n\n        // Rewrite the url\n        Url url{server->GetBaseUrl() + \"/hello.html\"};\n        session->SetUrl(url);\n\n        // Proceed the chain\n        std::vector<Response> response = proceed(multi);\n        EXPECT_FALSE(response.empty());\n\n        // Restore the url again\n        response.front().url = old_url;\n        return response;\n    }\n};\n\nclass ChangeStatusCodeInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        std::shared_ptr<Session> session = multi.GetSessions().front().first;\n\n        // Proceed the chain\n        std::vector<Response> response = proceed(multi);\n        EXPECT_FALSE(response.empty());\n\n        // Change the status code\n        response.front().status_code = 12345;\n        return response;\n    }\n};\n\nclass SetBasicAuthInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        std::shared_ptr<Session> session = multi.GetSessions().front().first;\n\n        // Set authentication\n        session->SetAuth(Authentication{\"user\", \"password\", AuthMode::BASIC});\n\n        // Proceed the chain\n        return proceed(multi);\n    }\n};\n\nclass SetUnsupportedProtocolErrorInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        std::shared_ptr<Session> session = multi.GetSessions().front().first;\n\n        // Proceed the chain\n        std::vector<Response> response = proceed(multi);\n        EXPECT_FALSE(response.empty());\n\n        // Set unsupported protocol error\n        response.front().error = Error{CURLE_UNSUPPORTED_PROTOCOL, \"SetErrorInterceptorMulti\"};\n\n        // Return response\n        return response;\n    }\n};\n\nclass ChangeRequestMethodToGetInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::GET_REQUEST;\n\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToPostInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::POST_REQUEST;\n        multi.GetSessions().front().first->SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToPutInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::PUT_REQUEST;\n        multi.GetSessions().front().first->SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToDeleteInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::DELETE_REQUEST;\n        return proceed(multi);\n    }\n};\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nclass ChangeRequestMethodToDownloadCallbackInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::DOWNLOAD_REQUEST;\n        PrepareDownloadSession(multi, 0, WriteCallback{write_data, 0});\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToHeadInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::HEAD_REQUEST;\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToOptionsInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::OPTIONS_REQUEST;\n        return proceed(multi);\n    }\n};\n\nclass ChangeRequestMethodToPatchInterceptorMulti : public InterceptorMulti {\n  public:\n    std::vector<Response> intercept(MultiPerform& multi) override {\n        EXPECT_FALSE(multi.GetSessions().empty());\n        multi.GetSessions().front().second = MultiPerform::HttpMethod::PATCH_REQUEST;\n        multi.GetSessions().front().first->SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(multi);\n    }\n};\n\nTEST(InterceptorMultiTest, HiddenUrlRewriteInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeStatusCodeInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    long expected_status_code{12345};\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(expected_status_code, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, DownloadChangeStatusCodeInterceptorMultiTest) {\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptorMulti>());\n    std::vector<Response> response = multi.Download(cpr::WriteCallback{write_data, 0});\n    EXPECT_EQ(response.size(), 1);\n\n    long expected_status_code{12345};\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(expected_status_code, response.front().status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, SetBasicAuthInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<SetBasicAuthInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, SetUnsupportedProtocolErrorInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_error_message{\"SetErrorInterceptorMulti\"};\n    ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(expected_error_message, response.front().error.message);\n    EXPECT_EQ(expected_error_code, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToGetInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToGetInterceptorMulti>());\n    std::vector<Response> response = multi.Head();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToPostInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToPostInterceptorMulti>());\n    std::vector<Response> response = multi.Head();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(201, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToPutInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToPutInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToPatchInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToPatchInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToOptionsInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToOptionsInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.front().header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToHeadInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToHeadInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    EXPECT_EQ(std::string{}, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToDownloadCallbackInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session->SetTimeout(Timeout{2000});\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToDownloadCallbackInterceptorMulti>());\n    std::vector<Response> response = multi.Put();\n    EXPECT_EQ(response.size(), 1);\n\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToDownloadCallbackInterceptorMultiMixTest) {\n    Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    std::shared_ptr<Session> session1 = std::make_shared<Session>();\n    session1->SetUrl(url);\n    session1->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session1->SetTimeout(Timeout{2000});\n\n    std::shared_ptr<Session> session2 = std::make_shared<Session>();\n    session2->SetUrl(url);\n    session2->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session2->SetTimeout(Timeout{2000});\n\n    MultiPerform multi;\n    multi.AddSession(session1);\n    multi.AddSession(session2);\n    // Changes only one of two sessions to download, so it is expected to throw an exception here since we can not mix them.\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToDownloadCallbackInterceptorMulti>());\n    EXPECT_THROW(multi.Put(), std::invalid_argument);\n}\n\nTEST(InterceptorMultiTest, ChangeRequestMethodToDeleteInterceptorMultiTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<ChangeRequestMethodToDeleteInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.front().header[\"content-type\"]);\n    EXPECT_EQ(200, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, TwoInterceptorMultisTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptorMulti>());\n    multi.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Hello world!\"};\n    long expected_status_code{12345};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(expected_status_code, response.front().status_code);\n    EXPECT_EQ(ErrorCode::OK, response.front().error.code);\n}\n\nTEST(InterceptorMultiTest, ThreeInterceptorMultisTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multi;\n    multi.AddSession(session);\n    multi.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptorMulti>());\n    multi.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptorMulti>());\n    multi.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptorMulti>());\n    std::vector<Response> response = multi.Get();\n    EXPECT_EQ(response.size(), 1);\n\n    std::string expected_text{\"Hello world!\"};\n    long expected_status_code{12345};\n    std::string expected_error_message{\"SetErrorInterceptorMulti\"};\n    ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};\n    EXPECT_EQ(expected_text, response.front().text);\n    EXPECT_EQ(url, response.front().url);\n    EXPECT_EQ(expected_status_code, response.front().status_code);\n    EXPECT_EQ(expected_error_message, response.front().error.message);\n    EXPECT_EQ(expected_error_code, response.front().error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}"
  },
  {
    "path": "test/interceptor_tests.cpp",
    "content": "#include <gtest/gtest.h>\n#include <iostream>\n\n#include \"cpr/cpr.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nclass HiddenHelloWorldRedirectInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        // Save original url\n        Url old_url = session.GetFullRequestUrl();\n\n        // Rewrite the url\n        Url url{server->GetBaseUrl() + \"/hello.html\"};\n        session.SetUrl(url);\n\n        // Proceed the chain\n        Response response = proceed(session);\n\n        // Restore the url again\n        response.url = old_url;\n        return response;\n    }\n};\n\nclass ChangeStatusCodeInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        // Proceed the chain\n        Response response = proceed(session);\n\n        // Change the status code\n        response.status_code = 12345;\n        return response;\n    }\n};\n\nclass SetBasicAuthInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        // Set authentication\n        session.SetAuth(Authentication{\"user\", \"password\", AuthMode::BASIC});\n\n        // Proceed the chain\n        return proceed(session);\n    }\n};\n\nclass SetUnsupportedProtocolErrorInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        // Proceed the chain\n        Response response = proceed(session);\n\n        // Set unsupported protocol error\n        response.error = Error{CURLE_UNSUPPORTED_PROTOCOL, \"SetErrorInterceptor\"};\n\n        // Return response\n        return response;\n    }\n};\n\nclass ChangeRequestMethodToGetInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        return proceed(session, Interceptor::ProceedHttpMethod::GET_REQUEST);\n    }\n};\n\nclass ChangeRequestMethodToPostInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        session.SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(session, Interceptor::ProceedHttpMethod::POST_REQUEST);\n    }\n};\n\nclass ChangeRequestMethodToPutInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        session.SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(session, Interceptor::ProceedHttpMethod::PUT_REQUEST);\n    }\n};\n\nclass ChangeRequestMethodToDeleteInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        return proceed(session, Interceptor::ProceedHttpMethod::DELETE_REQUEST);\n    }\n};\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nclass ChangeRequestMethodToDownloadCallbackInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        return proceed(session, Interceptor::ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST, WriteCallback{write_data, 0});\n    }\n};\n\nclass ChangeRequestMethodToHeadInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        return proceed(session, Interceptor::ProceedHttpMethod::HEAD_REQUEST);\n    }\n};\n\nclass ChangeRequestMethodToOptionsInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        return proceed(session, Interceptor::ProceedHttpMethod::OPTIONS_REQUEST);\n    }\n};\n\nclass ChangeRequestMethodToPatchInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        session.SetOption(Payload{{\"x\", \"5\"}});\n        return proceed(session, Interceptor::ProceedHttpMethod::PATCH_REQUEST);\n    }\n};\n\nclass RetryInterceptor : public Interceptor {\n  public:\n    Response intercept(Session& session) override {\n        // Proceed the chain\n        Response response = proceed(session);\n\n        // retried request\n        response = proceed(session);\n\n        return response;\n    }\n};\n\nTEST(InterceptorTest, HiddenUrlRewriteInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeStatusCodeInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());\n    Response response = session.Get();\n\n    long expected_status_code{12345};\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n\n    // second request\n    response = session.Get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, RetryInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<RetryInterceptor>());\n    session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());\n    Response response = session.Get();\n\n    long expected_status_code{12345};\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n\n    // second request\n    response = session.Get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, DownloadChangeStatusCodeInterceptorTest) {\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    cpr::Session session;\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());\n    Response response = session.Download(cpr::WriteCallback{write_data, 0});\n\n    long expected_status_code{12345};\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, SetBasicAuthInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<SetBasicAuthInterceptor>());\n\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, SetUnsupportedProtocolErrorInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptor>());\n\n    Response response = session.Get();\n    std::string expected_error_message{\"SetErrorInterceptor\"};\n    ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(expected_error_message, response.error.message);\n    EXPECT_EQ(expected_error_code, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToGetInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToGetInterceptor>());\n    Response response = session.Head();\n\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToPostInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToPostInterceptor>());\n    Response response = session.Head();\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToPutInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToPutInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToPatchInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToPatchInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToOptionsInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToOptionsInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToHeadInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToHeadInterceptor>());\n    Response response = session.Get();\n\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToDownloadCallbackInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    session.SetTimeout(Timeout{2000});\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToDownloadCallbackInterceptor>());\n    Response response = session.Put();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ChangeRequestMethodToDeleteInterceptorTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<ChangeRequestMethodToDeleteInterceptor>());\n    Response response = session.Get();\n\n\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, TwoInterceptorsTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());\n    session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\"Hello world!\"};\n    long expected_status_code{12345};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(InterceptorTest, ThreeInterceptorsTest) {\n    Url url{server->GetBaseUrl() + \"/basic.json\"};\n    Session session;\n    session.SetUrl(url);\n    session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());\n    session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());\n    session.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptor>());\n    Response response = session.Get();\n\n    std::string expected_text{\"Hello world!\"};\n    long expected_status_code{12345};\n    std::string expected_error_message{\"SetErrorInterceptor\"};\n    ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(expected_status_code, response.status_code);\n    EXPECT_EQ(expected_error_message, response.error.message);\n    EXPECT_EQ(expected_error_code, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/multiasync_tests.cpp",
    "content": "#include <functional>\n#include <string>\n#include <vector>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n#include \"multiasync_tests.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\n// A cancellable AsyncResponse\nusing AsyncResponseC = AsyncWrapper<Response, true>;\n\n/** This property is tested at compile-time, so if compilation succeeds, it has already been verified. It is, however, useful to structure it as a test for semantic purposes.\n */\nTEST(AsyncWrapperTests, TestConstructorDeductions) {\n    auto wrapper_non_cancellable{AsyncWrapper{std::future<std::string>{}}};\n    auto wrapper_cancellable{AsyncWrapper{std::future<std::string>{}, std::make_shared<std::atomic_bool>(false)}};\n\n    static_assert(std::is_same_v<AsyncWrapper<std::string, false>, decltype(wrapper_non_cancellable)>);\n    static_assert(std::is_same_v<AsyncWrapper<std::string, true>, decltype(wrapper_cancellable)>);\n    SUCCEED();\n}\n\n/** These tests aim to set a point of reference for AsyncWrapper behavior.\n * Those functions that replicate std::future member functions should behave in a way that is in all ways compatible.\n * Others should behave as expected by the below test set.\n */\nTEST(AsyncWrapperNonCancellableTests, TestGetNoError) {\n    const Url hello_url{server->GetBaseUrl() + \"/hello.html\"};\n    const std::string expected_hello{\"Hello world!\"};\n    const Response resp{GetAsync(hello_url).get()};\n    EXPECT_EQ(expected_hello, resp.text);\n}\n\nTEST(AsyncWrapperNonCancellableTests, TestExceptionsNoSharedState) {\n    const std::chrono::duration five_secs{std::chrono::seconds(1)};\n    const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs};\n\n    // We create an AsyncWrapper for a future without a shared state (default-initialized)\n    AsyncWrapper test_wrapper{std::future<std::string>{}};\n    ASSERT_FALSE(test_wrapper.valid());\n\n    // Trying to get or wait for a future that doesn't have a shared state should result to an exception\n    // It should be noted that there is a divergence from std::future behavior here: calling wait* on the original std::future is undefined behavior, according to cppreference.com . We find it preferrable to throw an exception.\n    EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception);\n    EXPECT_THROW(test_wrapper.wait(), std::exception);\n    EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception);\n    EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception);\n}\n\nTEST(AsyncWrapperCancellableTests, TestExceptionsNoSharedState) {\n    const std::chrono::duration five_secs{std::chrono::seconds(5)};\n    const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs};\n\n    AsyncWrapper test_wrapper{std::future<std::string>{}, std::make_shared<std::atomic_bool>(false)};\n\n    static_assert(std::is_same<AsyncWrapper<std::string, true>, decltype(test_wrapper)>::value);\n\n    ASSERT_FALSE(test_wrapper.valid());\n    ASSERT_FALSE(test_wrapper.IsCancelled());\n\n    EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception);\n    EXPECT_THROW(test_wrapper.wait(), std::exception);\n    EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception);\n    EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception);\n}\n\nTEST(AsyncWrapperCancellableTests, TestExceptionsCancelledRequest) {\n    const Url call_url{server->GetBaseUrl() + \"/low_speed_bytes.html\"};\n    const std::chrono::duration five_secs{std::chrono::seconds(5)};\n    const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs};\n\n    AsyncResponseC test_wrapper{std::move(MultiGetAsync(std::tuple{call_url}).at(0))};\n    EXPECT_EQ(CancellationResult::success, test_wrapper.Cancel());\n    EXPECT_EQ(CancellationResult::invalid_operation, test_wrapper.Cancel());\n    ASSERT_TRUE(test_wrapper.IsCancelled());\n\n    EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception);\n    EXPECT_THROW(test_wrapper.wait(), std::exception);\n    EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception);\n    EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception);\n}\n\nTEST(AsyncWrapperCancellableTests, TestWaitFor) {\n    constexpr std::chrono::duration wait_for_time{std::chrono::milliseconds(100)};\n    constexpr std::chrono::duration teardown_time{std::chrono::milliseconds(10)};\n\n    const Url call_url{server->GetBaseUrl() + \"/low_speed_bytes.html\"};\n\n    AsyncResponseC test_wrapper{std::move(MultiGetAsync(std::tuple{call_url}).at(0))};\n\n    EXPECT_EQ(std::future_status::timeout, test_wrapper.wait_for(wait_for_time));\n\n    ASSERT_TRUE(test_wrapper.valid());\n    ASSERT_FALSE(test_wrapper.IsCancelled());\n\n    EXPECT_EQ(CancellationResult::success, test_wrapper.Cancel());\n\n    std::this_thread::sleep_for(teardown_time);\n}\n/** The group MultiAsyncBasicTests executes multiple tests from the test sources associated with every Http action in parallel.\n * These tests are reproductions of tests from the appropriate test suites, but they guarantee that the multiasync function template produces correctly working instantiations for every Http action.\n */\nTEST(MultiAsyncBasicTests, MultiAsyncGetTest) {\n    const Url hello_url{server->GetBaseUrl() + \"/hello.html\"};\n    const std::string expected_hello{\"Hello world!\"};\n    std::vector<AsyncResponseC> resps{MultiGetAsync(std::tuple{hello_url}, std::tuple{hello_url}, std::tuple{hello_url})};\n\n    for (AsyncResponseC& resp : resps) {\n        EXPECT_EQ(expected_hello, resp.get().text);\n    }\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncDeleteTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url delete_allowed{server_base + \"/delete.html\"};\n    const Url delete_unallowed{server_base + \"/delete_unallowed.html\"};\n    const std::tuple del_json_params{delete_allowed, Body{\"'foo':'bar'\"}, Header{{\"Content-Type\", \"application/json\"}}};\n    const std::string expected_text_success{\"Delete success\"};\n    const std::string expected_text_fail{\"Method Not Allowed\"};\n    const std::string expected_text_json{\"'foo':'bar'\"};\n\n    std::vector<AsyncResponseC> resps{MultiDeleteAsync(std::tuple{delete_allowed}, std::tuple{delete_unallowed}, del_json_params)};\n\n    Response del_success{resps.at(0).get()};\n    Response del_fail{resps.at(1).get()};\n    Response del_json{resps.at(2).get()};\n\n    EXPECT_EQ(expected_text_success, del_success.text);\n    EXPECT_EQ(delete_allowed, del_success.url);\n    EXPECT_EQ(std::string{\"text/html\"}, del_success.header[\"content-type\"]);\n    EXPECT_EQ(200, del_success.status_code);\n    EXPECT_EQ(ErrorCode::OK, del_success.error.code);\n\n    EXPECT_EQ(expected_text_fail, del_fail.text);\n    EXPECT_EQ(delete_unallowed, del_fail.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, del_fail.header[\"content-type\"]);\n    EXPECT_EQ(405, del_fail.status_code);\n    EXPECT_EQ(ErrorCode::OK, del_fail.error.code);\n\n    EXPECT_EQ(expected_text_json, del_json.text);\n    EXPECT_EQ(delete_allowed, del_json.url);\n    EXPECT_EQ(std::string{\"application/json\"}, del_json.header[\"content-type\"]);\n    EXPECT_EQ(200, del_json.status_code);\n    EXPECT_EQ(ErrorCode::OK, del_json.error.code);\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncHeadTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url hello_url{server_base + \"/hello.html\"};\n    const Url json_url{server_base + \"/basic.json\"};\n    const Url notfound_url{server_base + \"/error.html\"};\n    const Url digest_url{server_base + \"/digest_auth.html\"};\n    const Authentication digest_auth{\"user\", \"password\", AuthMode::DIGEST};\n\n    std::vector<AsyncResponseC> resps{MultiHeadAsync(std::tuple{hello_url}, std::tuple{json_url}, std::tuple{notfound_url}, std::tuple{digest_url, digest_auth})};\n    Response hello_resp{resps.at(0).get()};\n    Response json_resp{resps.at(1).get()};\n    Response notfound_resp{resps.at(2).get()};\n    Response digest_resp{resps.at(3).get()};\n\n    EXPECT_EQ(std::string{}, hello_resp.text);\n    EXPECT_EQ(hello_url, hello_resp.url);\n    EXPECT_EQ(std::string{\"text/html\"}, hello_resp.header[\"content-type\"]);\n    EXPECT_EQ(200, hello_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, hello_resp.error.code);\n\n    EXPECT_EQ(std::string{}, json_resp.text);\n    EXPECT_EQ(json_url, json_resp.url);\n    EXPECT_EQ(std::string{\"application/json\"}, json_resp.header[\"content-type\"]);\n    EXPECT_EQ(200, json_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, json_resp.error.code);\n\n    EXPECT_EQ(std::string{}, notfound_resp.text);\n    EXPECT_EQ(notfound_url, notfound_resp.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, notfound_resp.header[\"content-type\"]);\n    EXPECT_EQ(404, notfound_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, notfound_resp.error.code);\n\n    EXPECT_EQ(std::string{}, digest_resp.text);\n    EXPECT_EQ(digest_url, digest_resp.url);\n    EXPECT_EQ(std::string{\"text/html\"}, digest_resp.header[\"content-type\"]);\n    EXPECT_EQ(200, digest_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, digest_resp.error.code);\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncOptionsTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url root_url{server_base + \"/\"};\n    const Url hello_url{server_base + \"/hello.html\"};\n\n    std::vector<AsyncResponseC> resps{MultiOptionsAsync(std::tuple{root_url}, std::tuple{hello_url})};\n\n    Response root_resp{resps.at(0).get()};\n    Response hello_resp{resps.at(1).get()};\n\n    EXPECT_EQ(std::string{}, root_resp.text);\n    EXPECT_EQ(root_url, root_resp.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, root_resp.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, root_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, root_resp.error.code);\n\n    EXPECT_EQ(std::string{}, hello_resp.text);\n    EXPECT_EQ(hello_url, hello_resp.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, hello_resp.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, hello_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, hello_resp.error.code);\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncPatchTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url patch_url{server_base + \"/patch.html\"};\n    const Url patch_not_allowed_url{server_base + \"/patch_unallowed.html\"};\n    const Payload pl{{\"x\", \"10\"}, {\"y\", \"1\"}};\n    const std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 10,\\n\"\n            \"  \\\"y\\\": 1,\\n\"\n            \"  \\\"sum\\\": 11\\n\"\n            \"}\"};\n    const std::string notallowed_text{\"Method Not Allowed\"};\n    std::vector<AsyncResponseC> resps{MultiPatchAsync(std::tuple{patch_url, pl}, std::tuple{patch_not_allowed_url, pl})};\n    const Response success{resps.at(0).get()};\n    const Response fail{resps.at(1).get()};\n    EXPECT_EQ(expected_text, success.text);\n    EXPECT_EQ(200, success.status_code);\n    EXPECT_EQ(patch_url, success.url);\n\n    EXPECT_EQ(notallowed_text, fail.text);\n    EXPECT_EQ(405, fail.status_code);\n    EXPECT_EQ(ErrorCode::OK, fail.error.code);\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncPostTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url post_url{server_base + \"/url_post.html\"};\n    const Url form_post_url{server_base + \"/form_post.html\"};\n\n    const Payload post_data{{\"x\", \"5\"}, {\"y\", \"15\"}};\n    const Multipart form_data{{\"x\", 5}};\n\n    const std::string post_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5,\\n\"\n            \"  \\\"y\\\": 15,\\n\"\n            \"  \\\"sum\\\": 20\\n\"\n            \"}\"};\n    const std::string form_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n\n    std::vector<AsyncResponseC> resps{MultiPostAsync(std::tuple{post_url, post_data}, std::tuple{form_post_url, form_data})};\n\n    Response post_resp{resps.at(0).get()};\n    Response form_resp{resps.at(1).get()};\n\n    EXPECT_EQ(post_text, post_resp.text);\n    EXPECT_EQ(post_url, post_resp.url);\n    EXPECT_EQ(std::string{\"application/json\"}, post_resp.header[\"content-type\"]);\n    EXPECT_EQ(201, post_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, post_resp.error.code);\n\n    EXPECT_EQ(form_text, form_resp.text);\n    EXPECT_EQ(form_post_url, form_resp.url);\n    EXPECT_EQ(std::string{\"application/json\"}, form_resp.header[\"content-type\"]);\n    EXPECT_EQ(201, form_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, form_resp.error.code);\n}\n\nTEST(MultiAsyncBasicTests, MultiAsyncPutTest) {\n    const std::string server_base{server->GetBaseUrl()};\n    const Url put_url{server_base + \"/put.html\"};\n    const Url put_failure_url{server_base + \"/put_unallowed.html\"};\n    const Payload pl{{\"x\", \"7\"}};\n    const std::string success_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 7\\n\"\n            \"}\"};\n    const std::string failure_text{\"Method Not Allowed\"};\n\n    std::vector<AsyncResponseC> resps{MultiPutAsync(std::tuple{put_url, pl}, std::tuple{put_failure_url, pl})};\n    Response success_resp{resps.at(0).get()};\n    Response failure_resp{resps.at(1).get()};\n\n    EXPECT_EQ(success_text, success_resp.text);\n    EXPECT_EQ(put_url, success_resp.url);\n    EXPECT_EQ(std::string{\"application/json\"}, success_resp.header[\"content-type\"]);\n    EXPECT_EQ(200, success_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, success_resp.error.code);\n\n    EXPECT_EQ(failure_text, failure_resp.text);\n    EXPECT_EQ(put_failure_url, failure_resp.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, failure_resp.header[\"content-type\"]);\n    EXPECT_EQ(405, failure_resp.status_code);\n    EXPECT_EQ(ErrorCode::OK, failure_resp.error.code);\n}\n\nstatic TestSynchronizationEnv* synchro_env = new TestSynchronizationEnv();\n\n/**\n * We test that cancellation on queue, works, ie libcurl does not get engaged at all\n * To do this, we plant an observer function in the progress call sequence, which\n * will set an atomic boolean to true. The objective is to verify that within 500ms,\n * the function is never called.\n */\nTEST(MultiAsyncCancelTests, CancellationOnQueue) {\n    synchro_env->Reset();\n    const Url hello_url{server->GetBaseUrl() + \"/hello.html\"};\n    const std::function<bool(cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t)> observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool {\n        synchro_env->fn_called.store(true);\n        return true;\n    }};\n\n    GlobalThreadPool::GetInstance()->Pause();\n    std::vector<AsyncResponseC> resps{MultiGetAsync(std::tuple{hello_url, ProgressCallback{observer_fn}})};\n    EXPECT_EQ(CancellationResult::success, resps.at(0).Cancel());\n    GlobalThreadPool::GetInstance()->Resume();\n    const bool was_called{synchro_env->fn_called};\n    EXPECT_EQ(false, was_called);\n}\n\n/**\n * We test that cancellation works as intended while the request is being processed by the server.\n * To achieve this we use a condition variable to ensure that the observer function, wrapped in a\n * cpr::ProgressCallback, is called at least once, and then no further calls are made for half a\n * second after cancellation.\n *\n * The usage of the condition variable and mutex to synchronize this procedure is analogous to the section \"Example\" in https://en.cppreference.com/w/cpp/thread/condition_variable\n * We use the condition variable in our synchronization environment to ensure that the transfer has\n * started at the time of cancellation, ie the observer function has been called at least once.\n */\nTEST(MultiAsyncCancelTests, TestCancellationInTransit) {\n    const Url call_url{server->GetBaseUrl() + \"/low_speed_bytes.html\"};\n    synchro_env->Reset();\n\n    // 1. Thread running the test acquires the condition variable's mutex\n    std::unique_lock setup_lock{synchro_env->test_cv_mutex};\n    const std::function<bool(cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t)> observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool {\n        if (synchro_env->counter == 0) {\n            // 3. in Threadpool, the cv mutex is obtained by the worker thread\n            const std::unique_lock l{synchro_env->test_cv_mutex};\n            synchro_env->counter++;\n            // 4. the cv is notified\n            synchro_env->test_cv.notify_all();\n        } else {\n            synchro_env->counter++;\n        }\n        return true;\n    }};\n    std::vector<AsyncResponseC> res{cpr::MultiGetAsync(std::tuple{call_url, cpr::ProgressCallback{observer_fn}})};\n    // 2. cv mutex is released, thread waits for notification on cv\n    // see https://en.cppreference.com/w/cpp/thread/condition_variable/wait\n    synchro_env->test_cv.wait(setup_lock);\n    // 5. execution continues after notification\n    const size_t init_calls{synchro_env->counter};\n    EXPECT_LT(0, init_calls);\n    EXPECT_EQ(cpr::CancellationResult::success, res.at(0).Cancel());\n    const size_t calls{synchro_env->counter};\n    std::this_thread::sleep_for(std::chrono::milliseconds{101});\n    const size_t calls_post{synchro_env->counter};\n    EXPECT_LT(calls_post, calls + 2);\n}\n\n/** Checks that the request is cancelled when the corresponding AsyncResponseC is desturcted\n */\nTEST(MultiAsyncCancelTests, TestCancellationOnResponseWrapperDestruction) {\n    const Url call_url{server->GetBaseUrl() + \"/hello.html\"};\n    synchro_env->Reset();\n    std::unique_lock setup_lock{synchro_env->test_cv_mutex};\n    const std::function<bool(cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t)> observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool {\n        const std::unique_lock l{synchro_env->test_cv_mutex};\n        synchro_env->counter++;\n        synchro_env->test_cv.notify_all();\n        return true;\n    }};\n\n    // We construct a Request that will not terminate, wait until it is being processed by a thread, and destruct the AsyncResponseC\n    {\n        AsyncResponseC resp{std::move(MultiGetAsync(std::tuple{call_url, ProgressCallback{observer_fn}}).at(0))};\n        synchro_env->test_cv.wait(setup_lock);\n        const size_t init_calls{synchro_env->counter};\n        EXPECT_LT(0, init_calls);\n    }\n\n    const size_t calls{synchro_env->counter};\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    const size_t post_calls{synchro_env->counter};\n    EXPECT_EQ(calls, post_calls);\n}\n\n/**\n * This test checks if the interval of calls to the progress function is\n * acceptable during a low-speed transaction. The server's low_speed_bytes\n * uri sends 1 Byte/second, and we aim to evaluate that 15 calls to the\n * progress function happen within 5 seconds. This would indicate that\n * the user can realistically expect to have their request cancelled within\n * ~1s on a bad case (low network speed).\n * INFO this test is not, strictly speaking, deterministic. It depends at the\n * least on scheduler behavior. We have tried, however, to set a boundary that\n * is permissive enough to ensure consistency.\n */\n\nTEST(MultiAsyncCancelTests, TestIntervalOfProgressCallsLowSpeed) {\n    const Url call_url{server->GetBaseUrl() + \"/low_speed_bytes.html\"};\n\n    synchro_env->Reset();\n    size_t N{15};\n    // This variable will be used to cancel the transaction at the point of the Nth call.\n    const std::chrono::time_point start{std::chrono::steady_clock::now()};\n\n    const std::function<bool(cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t)> observer_fn{[N](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool {\n        const size_t current_iteration{++(synchro_env->counter)};\n        return current_iteration <= N;\n    }};\n    const ProgressCallback pcall{observer_fn};\n\n    std::vector<AsyncResponseC> resp{MultiGetAsync(std::tuple{call_url, pcall})};\n    resp.at(0).wait();\n\n    const std::chrono::duration elapsed_time{std::chrono::steady_clock::now() - start};\n    EXPECT_GT(std::chrono::seconds(N), elapsed_time);\n    std::this_thread::sleep_for(std::chrono::milliseconds{101});\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    ::testing::AddGlobalTestEnvironment(synchro_env);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/multiasync_tests.hpp",
    "content": "#pragma once\n\n#include <atomic>\n#include <condition_variable>\n#include <gtest/gtest.h>\n\nclass TestSynchronizationEnv : public testing::Environment {\n  public:\n    std::atomic_size_t counter{0};\n    std::atomic_bool fn_called{false};\n    std::condition_variable test_cv{};\n    std::mutex test_cv_mutex{};\n\n    void Reset() {\n        counter = 0;\n        fn_called = false;\n    }\n};\n"
  },
  {
    "path": "test/multiperform_tests.cpp",
    "content": "#include <cstdint>\n#include <gtest/gtest.h>\n\n#include <memory>\n#include <string>\n\n#include \"cpr/api.h\"\n#include \"cpr/cpr.h\"\n#include <curl/curl.h>\n\n#include \"httpServer.hpp\"\n#include \"httpsServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddSingleSessionTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n\n    EXPECT_EQ(2, session.use_count());\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddMultipleSessionsTest) {\n    MultiPerform multiperform;\n    for (int i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        multiperform.AddSession(session);\n        EXPECT_EQ(2, session.use_count());\n    }\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddMultipleNonDownloadSessionsTest) {\n    MultiPerform multiperform;\n    for (int i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST);\n        EXPECT_EQ(2, session.use_count());\n    }\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddMultipleDownloadSessionsTest) {\n    MultiPerform multiperform;\n    for (int i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);\n        EXPECT_EQ(2, session.use_count());\n    }\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsTest) {\n    std::shared_ptr<Session> session_1 = std::make_shared<Session>();\n    std::shared_ptr<Session> session_2 = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session_1, MultiPerform::HttpMethod::GET_REQUEST);\n    EXPECT_EQ(2, session_1.use_count());\n    EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::DOWNLOAD_REQUEST), std::invalid_argument);\n}\n\nTEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsReversTest) {\n    std::shared_ptr<Session> session_1 = std::make_shared<Session>();\n    std::shared_ptr<Session> session_2 = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session_1, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);\n    EXPECT_EQ(2, session_1.use_count());\n    EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::GET_REQUEST), std::invalid_argument);\n}\n\nTEST(MultiperformRemoveSessionTests, MultiperformRemoveSingleSessionTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    EXPECT_EQ(2, session.use_count());\n    multiperform.RemoveSession(session);\n    EXPECT_EQ(1, session.use_count());\n}\n\nTEST(MultiperformRemoveSessionTests, MultiperformRemoveMultipleSessionsTest) {\n    MultiPerform multiperform;\n    for (int i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        multiperform.AddSession(session);\n        EXPECT_EQ(2, session.use_count());\n        multiperform.RemoveSession(session);\n        EXPECT_EQ(1, session.use_count());\n    }\n}\n\nTEST(MultiperformRemoveSessionTests, MultiperformRemoveNonExistingSessionEmptyTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    MultiPerform multiperform;\n    EXPECT_THROW(multiperform.RemoveSession(session), std::invalid_argument);\n}\n\nTEST(MultiperformRemoveSessionTests, MultiperformRemoveNonExistingSessionTest) {\n    MultiPerform multiperform;\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    multiperform.AddSession(session);\n\n    std::shared_ptr<Session> session2 = std::make_shared<Session>();\n    EXPECT_THROW(multiperform.RemoveSession(session2), std::invalid_argument);\n}\n\nTEST(MultiperformGetTests, MultiperformSingleSessionGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Get();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformGetTests, MultiperformTwoSessionsGetTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/hello.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/error.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        multiperform.AddSession(sessions.at(i));\n    }\n\n    std::vector<Response> responses = multiperform.Get();\n\n    EXPECT_EQ(responses.size(), sessions.size());\n    std::vector<std::string> expected_texts;\n    expected_texts.emplace_back(\"Hello world!\");\n    expected_texts.emplace_back(\"Not Found\");\n\n    std::vector<std::string> expected_content_types;\n    expected_content_types.emplace_back(\"text/html\");\n    expected_content_types.emplace_back(\"text/plain\");\n\n    std::vector<uint16_t> expected_status_codes;\n    expected_status_codes.push_back(200);\n    expected_status_codes.push_back(404);\n\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(expected_texts.at(i), responses.at(i).text);\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(expected_content_types.at(i), responses.at(i).header[\"content-type\"]);\n        EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformGetTests, MultiperformRemoveSessionGetTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/hello.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/hello.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        multiperform.AddSession(sessions.at(i));\n    }\n\n    multiperform.RemoveSession(sessions.at(0));\n\n    std::vector<Response> responses = multiperform.Get();\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(urls.at(0), responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformGetTests, MultiperformSingleSessionMultiGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Get();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n\n    responses = multiperform.Get();\n    EXPECT_EQ(responses.size(), 1);\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformGetTests, MultiperformAssignAfterUseTest) {\n    MultiPerform multiperform;\n    {\n        Url url{server->GetBaseUrl() + \"/hello.html\"};\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        multiperform.AddSession(session);\n        std::vector<Response> responses = multiperform.Get();\n\n        EXPECT_EQ(responses.size(), 1);\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, responses.at(0).text);\n        EXPECT_EQ(url, responses.at(0).url);\n        EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n        EXPECT_EQ(200, responses.at(0).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n    }\n\n    {\n        Url url{server->GetBaseUrl() + \"/hello.html\"};\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        multiperform = MultiPerform();\n        multiperform.AddSession(session);\n        std::vector<Response> responses = multiperform.Get();\n\n        EXPECT_EQ(responses.size(), 1);\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, responses.at(0).text);\n        EXPECT_EQ(url, responses.at(0).url);\n        EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n        EXPECT_EQ(200, responses.at(0).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n    }\n}\n\n#ifndef __APPLE__\n/**\n * This test case is currently disabled for macOS/Apple systems since it fails in an nondeterministic manner.\n * It is probably caused by a bug inside curl_multi_perform on macOS.\n * Needs further investigation.\n * Issue: https://github.com/libcpr/cpr/issues/841\n **/\nTEST(MultiperformGetTests, MultiperformTenSessionsGetTest) {\n    const size_t sessionCount = 10;\n\n    MultiPerform multiperform;\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    for (size_t i = 0; i < sessionCount; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        multiperform.AddSession(session);\n    }\n\n    std::vector<Response> responses = multiperform.Get();\n\n    EXPECT_EQ(responses.size(), sessionCount);\n    for (Response& response : responses) {\n        EXPECT_EQ(std::string{\"Hello world!\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n#endif\n\nTEST(MultiperformDeleteTests, MultiperformSingleSessionDeleteTest) {\n    Url url{server->GetBaseUrl() + \"/delete.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Delete();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadTest) {\n    Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0});\n\n    EXPECT_EQ(responses.size(), 1);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadNonMatchingArgumentsNumberTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    EXPECT_THROW(std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument);\n}\n\nTEST(MultiperformDownloadTests, MultiperformTwoSessionsDownloadTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/download_gzip.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/download_gzip.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        sessions.at(i)->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n\n        multiperform.AddSession(sessions.at(i));\n    }\n\n    std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0});\n\n    EXPECT_EQ(responses.size(), sessions.size());\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(200, responses.at(i).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformPutTests, MultiperformSingleSessionPutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload(Payload{{\"x\", \"5\"}});\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Put();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformHeadTests, MultiperformSingleSessionHeadTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Head();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text;\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformOptionsTests, MultiperformSingleSessionOptionsTest) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Options();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text;\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, responses.at(0).header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformPatchTests, MultiperformSingleSessionPatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload(Payload{{\"x\", \"5\"}});\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Patch();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformPostTests, MultiperformSingleSessionPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload(Payload{{\"x\", \"5\"}});\n    MultiPerform multiperform;\n    multiperform.AddSession(session);\n    std::vector<Response> responses = multiperform.Post();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(201, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformPerformTests, MultiperformSingleGetPerformTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    MultiPerform multiperform;\n    multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST);\n    std::vector<Response> responses = multiperform.Perform();\n\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformPerformTests, MultiperformTwoGetPerformTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/hello.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/error.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::GET_REQUEST);\n    }\n\n    std::vector<Response> responses = multiperform.Perform();\n\n    EXPECT_EQ(responses.size(), sessions.size());\n    std::vector<std::string> expected_texts;\n    expected_texts.emplace_back(\"Hello world!\");\n    expected_texts.emplace_back(\"Not Found\");\n\n    std::vector<std::string> expected_content_types;\n    expected_content_types.emplace_back(\"text/html\");\n    expected_content_types.emplace_back(\"text/plain\");\n\n    std::vector<uint16_t> expected_status_codes;\n    expected_status_codes.push_back(200);\n    expected_status_codes.push_back(404);\n\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(expected_texts.at(i), responses.at(i).text);\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(expected_content_types.at(i), responses.at(i).header[\"content-type\"]);\n        EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformPerformTests, MultiperformMixedMethodsPerformTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/hello.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/delete.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/error.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/url_post.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n    std::vector<MultiPerform::HttpMethod> methods;\n    methods.push_back(MultiPerform::HttpMethod::GET_REQUEST);\n    methods.push_back(MultiPerform::HttpMethod::DELETE_REQUEST);\n    methods.push_back(MultiPerform::HttpMethod::GET_REQUEST);\n    methods.push_back(MultiPerform::HttpMethod::POST_REQUEST);\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        if (methods.at(i) == MultiPerform::HttpMethod::POST_REQUEST) {\n            sessions.at(i)->SetPayload(Payload{{\"x\", \"5\"}});\n        }\n        multiperform.AddSession(sessions.at(i), methods.at(i));\n    }\n\n    std::vector<Response> responses = multiperform.Perform();\n\n    EXPECT_EQ(responses.size(), sessions.size());\n\n    std::vector<std::string> expected_texts;\n    expected_texts.emplace_back(\"Hello world!\");\n    expected_texts.emplace_back(\"Delete success\");\n    expected_texts.emplace_back(\"Not Found\");\n    expected_texts.emplace_back(\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\");\n\n    std::vector<std::string> expected_content_types;\n    expected_content_types.emplace_back(\"text/html\");\n    expected_content_types.emplace_back(\"text/html\");\n    expected_content_types.emplace_back(\"text/plain\");\n    expected_content_types.emplace_back(\"application/json\");\n\n    std::vector<uint16_t> expected_status_codes;\n    expected_status_codes.push_back(200);\n    expected_status_codes.push_back(200);\n    expected_status_codes.push_back(404);\n    expected_status_codes.push_back(201);\n\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(expected_texts.at(i), responses.at(i).text);\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(expected_content_types.at(i), responses.at(i).header[\"content-type\"]);\n        EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformPerformDownloadTests, MultiperformSinglePerformDownloadTest) {\n    Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    MultiPerform multiperform;\n    multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);\n    std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0});\n\n    EXPECT_EQ(responses.size(), 1);\n    EXPECT_EQ(url, responses.at(0).url);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformDownloadTests, MultiperformSinglePerformDownloadNonMatchingArgumentsNumberTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    MultiPerform multiperform;\n    multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);\n    EXPECT_THROW(std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument);\n}\n\nTEST(MultiperformPerformDownloadTests, MultiperformTwoPerformDownloadTest) {\n    MultiPerform multiperform;\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/download_gzip.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/download_gzip.html\"});\n\n    std::vector<std::shared_ptr<Session>> sessions;\n    sessions.push_back(std::make_shared<Session>());\n    sessions.push_back(std::make_shared<Session>());\n\n\n    for (size_t i = 0; i < sessions.size(); ++i) {\n        sessions.at(i)->SetUrl(urls.at(i));\n        sessions.at(i)->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n\n        multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::DOWNLOAD_REQUEST);\n    }\n\n    std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0});\n\n    EXPECT_EQ(responses.size(), sessions.size());\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(200, responses.at(i).status_code);\n        EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformAPITests, MultiperformApiSingleGetTest) {\n    std::vector<Response> responses = MultiGet(std::tuple<Url>{Url{server->GetBaseUrl() + \"/hello.html\"}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiTwoGetsTest) {\n    std::vector<Response> responses = MultiGet(std::tuple<Url, Timeout>{Url{server->GetBaseUrl() + \"/long_timeout.html\"}, Timeout{1000}}, std::tuple<Url>{Url{server->GetBaseUrl() + \"/error.html\"}});\n\n    EXPECT_EQ(responses.size(), 2);\n    std::vector<Url> urls;\n    urls.push_back({server->GetBaseUrl() + \"/long_timeout.html\"});\n    urls.push_back({server->GetBaseUrl() + \"/error.html\"});\n\n    std::vector<std::string> expected_texts;\n    expected_texts.emplace_back(\"\");\n    expected_texts.emplace_back(\"Not Found\");\n\n    std::vector<std::string> expected_content_types;\n    expected_content_types.emplace_back(\"\");\n    expected_content_types.emplace_back(\"text/plain\");\n\n    std::vector<uint16_t> expected_status_codes;\n    expected_status_codes.push_back(0);\n    expected_status_codes.push_back(404);\n\n    std::vector<ErrorCode> expected_error_codes;\n    expected_error_codes.push_back(ErrorCode::OPERATION_TIMEDOUT);\n    expected_error_codes.push_back(ErrorCode::OK);\n\n    for (size_t i = 0; i < responses.size(); ++i) {\n        EXPECT_EQ(expected_texts.at(i), responses.at(i).text);\n        EXPECT_EQ(urls.at(i), responses.at(i).url);\n        EXPECT_EQ(expected_content_types.at(i), responses.at(i).header[\"content-type\"]);\n        EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);\n        EXPECT_EQ(expected_error_codes.at(i), responses.at(i).error.code);\n    }\n}\n\nTEST(MultiperformAPITests, MultiperformApiSingleDeleteTest) {\n    std::vector<Response> responses = MultiDelete(std::tuple<Url>{Url{server->GetBaseUrl() + \"/delete.html\"}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\"Delete success\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/delete.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiSinglePutTest) {\n    std::vector<Response> responses = MultiPut(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + \"/put.html\"}, Payload{{\"x\", \"5\"}}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/put.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiSingleHeadTest) {\n    std::vector<Response> responses = MultiHead(std::tuple<Url>{Url{server->GetBaseUrl() + \"/hello.html\"}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text;\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"text/html\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiSingleOptionsTest) {\n    std::vector<Response> responses = MultiOptions(std::tuple<Url>{Url{server->GetBaseUrl() + \"/\"}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text;\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, responses.at(0).header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiSinglePatchTest) {\n    std::vector<Response> responses = MultiPatch(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + \"/patch.html\"}, Payload{{\"x\", \"5\"}}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/patch.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(200, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nTEST(MultiperformAPITests, MultiperformApiSinglePostTest) {\n    std::vector<Response> responses = MultiPost(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + \"/url_post.html\"}, Payload{{\"x\", \"5\"}}});\n    EXPECT_EQ(responses.size(), 1);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, responses.at(0).text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/url_post.html\"}, responses.at(0).url);\n    EXPECT_EQ(std::string{\"application/json\"}, responses.at(0).header[\"content-type\"]);\n    EXPECT_EQ(201, responses.at(0).status_code);\n    EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/options_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(OptionsTests, BaseUrlTest) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    Response response = cpr::Options(url);\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(OptionsTests, SpecificUrlTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Options(url);\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(OptionsTests, AsyncBaseUrlTest) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::OptionsAsync(url));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(OptionsTests, AsyncSpecificUrlTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::OptionsAsync(url));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/patch_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(PatchTests, PatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Response response = cpr::Patch(url, payload);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, PatchUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Response response = cpr::Patch(url, payload);\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchUnallowedAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchUnallowedAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, SessionPatchUnallowedAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Patch();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, AsyncPatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, AsyncPatchUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PatchTests, AsyncMultiplePatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::PatchAsync(url, payload));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"x\\\": 5\\n\"\n                \"}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(PatchTests, AsyncMultiplePatchUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/patch_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::PatchAsync(url, payload));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"Method Not Allowed\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(405, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/post_tests.cpp",
    "content": "#include <chrono>\n#include <cstddef>\n#include <gtest/gtest.h>\n\n#include <array>\n#include <cstdio>\n#include <fstream>\n#include <string>\n\n#include \"cpr/cookies.h\"\n#include \"cpr/cpr.h\"\n#include \"cpr/multipart.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(UrlEncodedPostTests, UrlPostSingleTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostAddPayloadPair) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"1\"}};\n    payload.Add({\"y\", \"2\"});\n    Response response = cpr::Post(url, payload);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 1,\\n\"\n            \"  \\\"y\\\": 2,\\n\"\n            \"  \\\"sum\\\": 3\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostPayloadIteratorTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::vector<Pair> payloadData;\n    payloadData.emplace_back(\"x\", \"1\");\n    payloadData.emplace_back(\"y\", \"2\");\n    Response response = cpr::Post(url, Payload(payloadData.begin(), payloadData.end()));\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 1,\\n\"\n            \"  \\\"y\\\": 2,\\n\"\n            \"  \\\"sum\\\": 3\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostEncodeTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"hello world!!~\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": hello world!!~\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostEncodeNoCopyTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"hello world!!~\"}};\n    // payload lives through the lifetime of Post, so it doesn't need to be copied\n    Response response = cpr::Post(url, payload);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": hello world!!~\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostManyTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}, {\"y\", \"13\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5,\\n\"\n            \"  \\\"y\\\": 13,\\n\"\n            \"  \\\"sum\\\": 18\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostBadHostTest) {\n    Url url{\"http://bad_host/\"};\n    Response response = cpr::Post(url, Payload{{\"hello\", \"world\"}});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT.\n    EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(UrlEncodedPostTests, FormPostSingleTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", 5}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileTest) {\n    std::string filename{\"test_file\"};\n    std::string content{\"hello world\"};\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << content;\n    test_file.close();\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", File{filename}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            content +\n            \"\\\"\\n\"\n            \"}\"};\n    std::remove(filename.c_str());\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostMultipleFilesTestLvalue) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    std::string filename1{\"file1\"};\n    std::string content1{\"apple\"};\n    std::ofstream file1;\n    file1.open(filename1);\n    file1 << content1;\n    file1.close();\n    std::string filename2{\"file2\"};\n    std::string content2{\"banana\"};\n    std::ofstream file2;\n    file2.open(filename2);\n    file2 << content2;\n    file2.close();\n    File singleFile{\"file1\"};\n    File singleFileWithOverridenFilename{\"file1\", \"applefile\"};\n    Files multipleFiles{\"file1\", \"file2\"};\n    Files multipleFilesWithOverridenFilename{\n            File{\"file1\", \"applefile\"},\n            File{\"file2\", \"bananafile\"},\n    };\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", singleFile}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"file1=\" +\n                content1 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"singleFile\", singleFileWithOverridenFilename}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"singleFile\\\": \\\"applefile=\" +\n                content1 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", multipleFiles}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"file1=\" +\n                content1 +\n                \"\\\",\\n\"\n                \"  \\\"files\\\": \\\"file2=\" +\n                content2 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", multipleFilesWithOverridenFilename}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"applefile=\" +\n                content1 +\n                \"\\\",\\n\"\n                \"  \\\"files\\\": \\\"bananafile=\" +\n                content2 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    std::remove(filename1.c_str());\n    std::remove(filename2.c_str());\n}\n\nTEST(UrlEncodedPostTests, FormPostMultipleFilesTestRvalue) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    std::string filename1{\"file1\"};\n    std::string content1{\"apple\"};\n    std::ofstream file1;\n    file1.open(filename1);\n    file1 << content1;\n    file1.close();\n    std::string filename2{\"file2\"};\n    std::string content2{\"banana\"};\n    std::ofstream file2;\n    file2.open(filename2);\n    file2 << content2;\n    file2.close();\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", File{\"file1\"}}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"file1=\" +\n                content1 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", File{\"file1\", \"applefile\"}}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"applefile=\" +\n                content1 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", Files{\"file1\", \"file2\"}}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"file1=\" +\n                content1 +\n                \"\\\",\\n\"\n                \"  \\\"files\\\": \\\"file2=\" +\n                content2 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = cpr::Post(url, Multipart{{\"files\", Files{\n                                                                       File{\"file1\", \"applefile\"},\n                                                                       File{\"file2\", \"bananafile\"},\n                                                               }}});\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"files\\\": \\\"applefile=\" +\n                content1 +\n                \"\\\",\\n\"\n                \"  \\\"files\\\": \\\"bananafile=\" +\n                content2 + \"\\\"\\n}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    std::remove(filename1.c_str());\n    std::remove(filename2.c_str());\n}\n\nTEST(UrlEncodedPostTests, FormPostFileTestWithOverridenFilename) {\n    std::string filename{\"test_file\"};\n    std::string overided_filename{\"overided_filename\"};\n    std::string content{\"hello world\"};\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << content;\n    test_file.close();\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n\n    Response response = cpr::Post(url, Multipart{{\"x\", File{filename, overided_filename}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"\" +\n            overided_filename + \"=\" + content +\n            \"\\\"\\n\"\n            \"}\"};\n    std::remove(filename.c_str());\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileNoCopyTest) {\n    std::string filename{\"./test_file\"};\n    std::string content{\"hello world\"};\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << content;\n    test_file.close();\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Multipart multipart{{\"x\", File{filename}}};\n    Response response = cpr::Post(url, multipart);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            content +\n            \"\\\"\\n\"\n            \"}\"};\n    std::remove(filename.c_str());\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileNoCopyTestWithOverridenFilename) {\n    std::string filename{\"test_file\"};\n    std::string overriden_filename{\"overided_filename\"};\n    std::string content{\"hello world\"};\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << content;\n    test_file.close();\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Multipart multipart{{\"x\", File{filename, overriden_filename}}};\n    Response response = cpr::Post(url, multipart);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"\" +\n            overriden_filename + \"=\" + content +\n            \"\\\"\\n\"\n            \"}\"};\n    std::remove(filename.c_str());\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, TimeoutPostTest) {\n    Url url{server->GetBaseUrl() + \"/json_post.html\"};\n    std::string body{R\"({\"RegisterObject\": {\"DeviceID\": \"65010000005030000001\"}})\"};\n    cpr::Response response = cpr::Post(url, cpr::Header{{\"Content-Type\", \"application/json\"}}, cpr::Body{body}, cpr::ConnectTimeout{3000}, cpr::Timeout{3000});\n    std::string expected_text{R\"({\"RegisterObject\": {\"DeviceID\": \"65010000005030000001\"}})\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferTest) {\n    std::string content{\"hello world\"};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{content.begin(), content.end(), \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            content +\n            \"\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferNoCopyTest) {\n    std::string content{\"hello world\"};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Multipart multipart{{\"x\", Buffer{content.begin(), content.end(), \"test_file\"}}};\n    Response response = cpr::Post(url, multipart);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            content +\n            \"\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferPointerTest) {\n    const char* content = \"hello world\";\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{content, 11 + content, \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            std::string(content) +\n            \"\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferArrayTest) {\n    const char content[] = \"hello world\";\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    // We subtract 1 from std::end() because we don't want to include the terminating null\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{std::begin(content), std::end(content) - 1, \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=\" +\n            std::string(content) +\n            \"\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferVectorTest) {\n    std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{content.begin(), content.end(), \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=hello world\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostFileBufferStdArrayTest) {\n    std::array<unsigned char, 11> content{{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{content.begin(), content.end(), \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=hello world\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostBufferRvalueTest) {\n    std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", Buffer{content.begin(), content.end(), \"test_file\"}}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=hello world\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, ReflectPostBufferLvalueTest) {\n    std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Buffer buff{content.begin(), content.end(), \"test_file\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", buff}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"test_file=hello world\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostManyTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", 5}, {\"y\", 13}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\",\\n\"\n            \"  \\\"y\\\": \\\"13\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostManyNoCopyTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Multipart multipart{{\"x\", 5}, {\"y\", 13}};\n    Response response = cpr::Post(url, multipart);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\",\\n\"\n            \"  \\\"y\\\": \\\"13\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostContentTypeTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url, Multipart{{\"x\", 5, \"application/number\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, FormPostContentTypeLValueTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Multipart multipart{{\"x\", 5, \"application/number\"}};\n    Response response = cpr::Post(url, multipart);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, UrlPostAsyncSingleTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::PostAsync(url, payload));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"x\\\": 5\\n\"\n                \"}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(UrlEncodedPostTests, UrlReflectTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}});\n    std::string expected_text{\"Header reflect POST\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UrlEncodedPostTests, PostWithNoBodyTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Response response = cpr::Post(url);\n    std::string expected_text{\"{\\n}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nstatic std::string getTimestamp() {\n    const std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();\n    const time_t timeT = std::chrono::system_clock::to_time_t(tp);\n    // NOLINTNEXTLINE(concurrency-mt-unsafe)\n    struct tm* tm = gmtime(&timeT);\n\n    std::string buf;\n    buf.resize(EXPIRES_STRING_SIZE);\n\n    const size_t s = strftime(buf.data(), buf.size(), \"%a, %d %b %Y %H:%M:%S GMT\", tm);\n    EXPECT_GT(s, 0);\n    buf.resize(s);\n    return buf;\n}\n\nTEST(UrlEncodedPostTests, PostReflectTest) {\n    std::string uri = server->GetBaseUrl() + \"/post_reflect.html\";\n    std::string body = R\"({\"property1\": \"value1\"})\";\n    std::string contentType = \"application/json\";\n    std::string signature = \"x-ms-date: something\";\n    std::string logType = \"LoggingTest\";\n    std::string date = getTimestamp();\n    Response response = cpr::Post(cpr::Url(uri), cpr::Header{{\"content-type\", contentType}, {\"Authorization\", signature}, {\"log-type\", logType}, {\"x-ms-date\", date}, {\"content-length\", std::to_string(body.length())}}, cpr::Body(body));\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(body, response.text);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(signature, response.header[\"Authorization\"]);\n    EXPECT_EQ(logType, response.header[\"log-type\"]);\n    EXPECT_EQ(date, response.header[\"x-ms-date\"]);\n    EXPECT_EQ(std::to_string(body.length()), response.header[\"content-length\"]);\n}\n\nTEST(UrlEncodedPostTests, PostReflectPayloadTest) {\n    std::string uri = server->GetBaseUrl() + \"/header_reflect.html\";\n    cpr::Payload payload = cpr::Payload{{\"email\", \"\"}, {\"password\", \"\"}, {\"devicetoken\", \"\"}};\n    cpr::Response response = cpr::Post(cpr::Url(uri), cpr::Timeout{10000}, payload);\n\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(200, response.status_code);\n}\n\nTEST(UrlEncodedPostTests, InjectMultipleHeadersTest) {\n    std::string uri = server->GetBaseUrl() + \"/post_reflect.html\";\n    std::string key_1 = \"key_1\";\n    std::string val_1 = \"value_1\";\n    std::string key_2 = \"key_2\";\n    std::string val_2 = \"value_2\";\n    cpr::Response response = cpr::Post(cpr::Url{uri}, cpr::Header{{key_1, val_1}}, cpr::Header{{key_2, val_2}});\n\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(val_1, response.header[key_1]);\n    EXPECT_EQ(val_2, response.header[key_2]);\n}\n\nTEST(UrlEncodedPostTests, PostBodyWithFile) {\n    std::string filename{\"test_file\"};\n    std::string expected_text(R\"({\"property1\": \"value1\"})\");\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << expected_text;\n    test_file.close();\n    Url url{server->GetBaseUrl() + \"/post_reflect.html\"};\n    cpr::Response response = Post(url, cpr::Header({{\"Content-Type\", \"application/octet-stream\"}}), cpr::Body(File(\"test_file\")));\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(std::string{\"application/octet-stream\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n}\n\nTEST(UrlEncodedPostTests, PostBodyWithBuffer) {\n    Url url{server->GetBaseUrl() + \"/post_reflect.html\"};\n    std::string expected_text(R\"({\"property1\": \"value1\"})\");\n    cpr::Response response = Post(url, cpr::Header({{\"Content-Type\", \"application/octet-stream\"}}), cpr::Body(Buffer{expected_text.begin(), expected_text.end(), \"test_file\"}));\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/octet-stream\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, TempRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}}, Redirect(PostRedirectFlags::POST_ALL));\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, TempRedirectNoneTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}}, Redirect(PostRedirectFlags::NONE));\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, PermRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}}, Redirect(PostRedirectFlags::POST_ALL));\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, PermRedirectNoneTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}}, Redirect(PostRedirectFlags::NONE));\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, TempRedirectDefaultTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    // Default PostRedirectFlags is NONE, so POST should not be preserved on redirect\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}});\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PostRedirectTests, PermRedirectDefaultTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    // Default PostRedirectFlags is NONE, so POST should not be preserved on redirect\n    Response response = cpr::Post(url, Payload{{\"x\", \"5\"}}, Header{{\"RedirectLocation\", \"url_post.html\"}});\n    EXPECT_EQ(response.url, server->GetBaseUrl() + \"/url_post.html\");\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/prepare_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n#include <vector>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(PrepareTests, GetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.PrepareGet();\n    CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n    Response response = session.Complete(curl_result);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PrepareTests, OptionsTests) {\n    Url url{server->GetBaseUrl() + \"/\"};\n    Session session;\n    session.SetUrl(url);\n    session.PrepareOptions();\n    CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n    Response response = session.Complete(curl_result);\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"GET, POST, PUT, DELETE, PATCH, OPTIONS\"}, response.header[\"Access-Control-Allow-Methods\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PrepareTests, PatchTest) {\n    Url url{server->GetBaseUrl() + \"/patch.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    session.PreparePatch();\n    CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n    Response response = session.Complete(curl_result);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Url urlPost{server->GetBaseUrl() + \"/post_reflect.html\"};\n    Url urlPut{server->GetBaseUrl() + \"/put.html\"};\n    Session session;\n    for (size_t i = 0; i < 3; ++i) {\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            session.PrepareDelete();\n            CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n            Response response = session.Complete(curl_result);\n            std::string expected_text{\"Header reflect DELETE\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.SetUrl(urlPost);\n            std::string expectedBody = \"a1b2c3Post\";\n            session.SetBody(expectedBody);\n            session.PreparePost();\n            CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n            Response response = session.Complete(curl_result);\n            EXPECT_EQ(expectedBody, response.text);\n            EXPECT_EQ(urlPost, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            session.PrepareGet();\n            CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n            Response response = session.Complete(curl_result);\n            std::string expected_text{\"Header reflect GET\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.SetUrl(urlPut);\n            session.SetPayload({{\"x\", \"5\"}});\n            session.PreparePut();\n            CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n            Response response = session.Complete(curl_result);\n            std::string expected_text{\n                    \"{\\n\"\n                    \"  \\\"x\\\": 5\\n\"\n                    \"}\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(urlPut, response.url);\n            EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            session.PrepareHead();\n            CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);\n            Response response = session.Complete(curl_result);\n            std::string expected_text{\"Header reflect HEAD\"};\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n    }\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/proxy_auth_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include \"httpServer.hpp\"\n\n// TODO: This requires a local proxy server (squid). This should be replaced with a source\n// code implementation.\n\n#define HTTP_PROXY \"127.0.0.1:3128\"\n#define HTTPS_PROXY \"127.0.0.1:3128\"\n#define PROXY_USER \"u$er\"\n#define PROXY_PASS \"p@ss\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(ProxyAuthTests, SetProxyCredentials) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetProxies(Proxies{{\"http\", HTTP_PROXY}, {\"https\", HTTPS_PROXY}});\n    session.SetProxyAuth({{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}, {\"https\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});\n    session.PrepareGet();\n    EXPECT_TRUE(true);\n}\n\n// TODO: These should be fixed after a source code implementation of a proxy\n#if defined(false)\nTEST(ProxyAuthTests, SingleProxyTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, Proxies{{\"http\", HTTP_PROXY}}, ProxyAuthentication{{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyAuthTests, MultipleProxyHttpTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Response response = cpr::Get(url, Proxies{{\"https\", HTTPS_PROXY}, {\"http\", HTTP_PROXY}}, ProxyAuthentication{{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}, {\"https\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyAuthTests, CopyProxyTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Proxies proxies{{\"http\", HTTP_PROXY}};\n    ProxyAuthentication proxy_auth{{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}};\n    Response response = cpr::Get(url, proxies, proxy_auth);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyAuthTests, ProxySessionTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetProxies(Proxies{{\"http\", HTTP_PROXY}});\n    session.SetProxyAuth(ProxyAuthentication{{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyAuthTests, ReferenceProxySessionTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Proxies proxies{{\"http\", HTTP_PROXY}};\n    ProxyAuthentication proxy_auth{{\"http\", EncodedAuthentication{PROXY_USER, PROXY_PASS}}};\n    Session session;\n    session.SetUrl(url);\n    session.SetProxies(proxies);\n    session.SetProxyAuth(proxy_auth);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n#endif\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/proxy_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <chrono>\n#include <sstream>\n#include <stdlib.h>\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n// TODO: This uses public servers for proxies and endpoints. This should be replaced with a source\n// code implementation inside server.cpp\n\n// NOTES:\n// * For no-proxy testing need to run the tests with direct connection to the internet\n// * List of free proxies for testing can be found at https://proxy-list.org/english/index.php\n//   Example: #define HTTP_PROXY \"http://162.223.90.130:80\"\n#define HTTP_PROXY \"51.159.4.98:80\"\n#define HTTPS_PROXY \"51.104.53.182:8000\"\n\n\nusing namespace cpr;\n\nTEST(ProxyTests, SingleProxyTest) {\n    Url url{\"http://www.httpbin.org/get\"};\n    Response response = cpr::Get(url, Proxies{{\"http\", HTTP_PROXY}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyTests, MultipleProxyHttpTest) {\n    Url url{\"http://www.httpbin.org/get\"};\n    Response response = cpr::Get(url, Proxies{{\"http\", HTTP_PROXY}, {\"https\", HTTPS_PROXY}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\n// TODO: These should be fixed after a source code implementation of an HTTPS proxy\n#if defined(false)\nTEST(ProxyTests, ProxyHttpsTest) {\n    Url url{\"https://www.httpbin.org/get\"};\n    Response response = cpr::Get(url, Proxies{{\"https\", HTTPS_PROXY}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyTests, MultipleProxyHttpsTest) {\n    Url url{\"https://www.httpbin.org/get\"};\n    Response response = cpr::Get(url, Proxies{{\"http\", HTTP_PROXY}, {\"https\", HTTPS_PROXY}});\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n#endif\n\nTEST(ProxyTests, CopyProxyTest) {\n    Url url{\"http://www.httpbin.org/get\"};\n    Proxies proxies{{\"http\", HTTP_PROXY}};\n    Response response = cpr::Get(url, proxies);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyTests, ProxySessionTest) {\n    Url url{\"http://www.httpbin.org/get\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetProxies(Proxies{{\"http\", HTTP_PROXY}});\n    Response response = session.Get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyTests, ReferenceProxySessionTest) {\n    Url url{\"http://www.httpbin.org/get\"};\n    Proxies proxies{{\"http\", HTTP_PROXY}};\n    Session session;\n    session.SetUrl(url);\n    session.SetProxies(proxies);\n    session.SetTimeout(std::chrono::seconds(10));\n    Response response = session.Get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ProxyTests, NoProxyTest) {\n    setenv(\"NO_PROXY\", \"httpbin.org\", 1);\n    try {\n        Url url{\"http://www.httpbin.org/get\"};\n        Proxies proxies{{\"http\", HTTP_PROXY}, {\"no_proxy\", \"\"}};\n        Session session;\n        session.SetUrl(url);\n        session.SetProxies(proxies);\n        session.SetTimeout(std::chrono::seconds(10));\n        Response response = session.Get();\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n\n        // check that access was performed through the proxy\n        std::string proxy_ip = HTTP_PROXY;\n        if (proxy_ip[0] == 'h') {\n            // drop protocol:\n            proxy_ip = proxy_ip.substr(proxy_ip.find(':') + 3);\n        }\n        // drop port:\n        proxy_ip = proxy_ip.substr(0, proxy_ip.find(':'));\n\n        // find \"origin\": \"ip\" in response:\n        bool found = false;\n        std::istringstream body(response.text);\n        std::string line;\n        while (std::getline(body, line)) {\n            // example: \"origin\": \"123.456.789.123\"\n            if (line.find(\"\\\"origin\\\":\") != std::string::npos) {\n                found = line.find(proxy_ip) != std::string::npos;\n                break;\n            }\n        }\n        EXPECT_TRUE(found);\n    } catch (...) {\n        unsetenv(\"NO_PROXY\");\n        throw;\n    }\n    unsetenv(\"NO_PROXY\");\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/put_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(PutTests, PutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Response response = cpr::Put(url, payload);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, PutUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Response response = cpr::Put(url, payload);\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutUnallowedAfterGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n    }\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutUnallowedAfterHeadTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/get.html\"};\n        session.SetUrl(url);\n        Response response = session.Head();\n    }\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, SessionPutUnallowedAfterPostTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/url_post.html\"};\n        Payload payload{{\"x\", \"5\"}};\n        session.SetUrl(url);\n        Response response = session.Post();\n    }\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    session.SetUrl(url);\n    session.SetPayload(payload);\n    Response response = session.Put();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, AsyncPutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    cpr::AsyncResponse future_response = cpr::PutAsync(url, payload);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, AsyncPutUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    cpr::AsyncResponse future_response = cpr::PutAsync(url, payload);\n    cpr::Response response = future_response.get();\n    std::string expected_text{\"Method Not Allowed\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(405, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PutTests, AsyncMultiplePutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::PutAsync(url, payload));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\n                \"{\\n\"\n                \"  \\\"x\\\": 5\\n\"\n                \"}\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(PutTests, AsyncMultiplePutUnallowedTest) {\n    Url url{server->GetBaseUrl() + \"/put_unallowed.html\"};\n    Payload payload{{\"x\", \"5\"}};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        responses.emplace_back(cpr::PutAsync(url, payload));\n    }\n    for (cpr::AsyncResponse& future_response : responses) {\n        cpr::Response response = future_response.get();\n        std::string expected_text{\"Method Not Allowed\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(405, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/raw_body_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <cstdio>\n#include <fstream>\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include \"cpr/multipart.h\"\n\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(BodyPostTests, DefaultUrlEncodedPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Body{\"x=5\"});\n    std::string expected_text = \"{\\n  \\\"x\\\": 5\\n}\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, TextUrlEncodedPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Body{\"x=hello world!!~\"});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": hello world!!~\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, TextUrlEncodedNoCopyPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Body body{\"x=hello world!!~\"};\n    // body lives through the lifetime of Post, so it doesn't need to be copied\n    Response response = cpr::Post(url, body);\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": hello world!!~\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, UrlEncodedManyPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Body{\"x=5&y=13\"});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5,\\n\"\n            \"  \\\"y\\\": 13,\\n\"\n            \"  \\\"sum\\\": 18\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, CustomHeaderNumberPostTest) {\n    Url url{server->GetBaseUrl() + \"/json_post.html\"};\n    Response response = cpr::Post(url, Body{\"{\\\"x\\\":5}\"}, Header{{\"Content-Type\", \"application/json\"}});\n    std::string expected_text{\"{\\\"x\\\":5}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, CustomHeaderTextPostTest) {\n    Url url{server->GetBaseUrl() + \"/json_post.html\"};\n    Response response = cpr::Post(url, Body{\"{\\\"x\\\":\\\"hello world!!~\\\"}\"}, Header{{\"Content-Type\", \"application/json\"}});\n    std::string expected_text{\"{\\\"x\\\":\\\"hello world!!~\\\"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, CustomWrongHeaderPostTest) {\n    Url url{server->GetBaseUrl() + \"/json_post.html\"};\n    Response response = cpr::Post(url, Body{\"{\\\"x\\\":5}\"}, Header{{\"Content-Type\", \"text/plain\"}});\n    std::string expected_text{\"Unsupported Media Type\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(415, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, UrlPostBadHostTest) {\n    Url url{\"http://bad_host/\"};\n    Response response = cpr::Post(url, Body{\"hello=world\"});\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(0, response.status_code);\n    // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT.\n    EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT);\n}\n\nTEST(BodyPostTests, StringMoveBodyTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, Body{std::string{\"x=5\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyPostTests, BodyViewTest) {\n    const Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Response response = cpr::Post(url, BodyView{\"x=5\"});\n    const std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/resolve_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(ResolveTests, HelloWorldTest) {\n    Url url{\"http://www.example.com:\" + std::to_string(server->GetPort()) + \"/hello.html\"};\n    Resolve resolve{\"www.example.com\", \"127.0.0.1\", {server->GetPort()}};\n    Response response = cpr::Get(url, resolve);\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ResolveTests, RedirectMultiple) {\n    Url url1{\"http://www.example0.com:\" + std::to_string(server->GetPort()) + \"/resolve_permanent_redirect.html\"};\n    Url url2{\"http://www.example1.com:\" + std::to_string(server->GetPort()) + \"/hello.html\"};\n    Resolve resolve1{\"www.example0.com\", \"127.0.0.1\", {server->GetPort()}};\n    Resolve resolve2{\"www.example1.com\", \"127.0.0.1\", {server->GetPort()}};\n\n    Response response = cpr::Get(url1, std::vector<Resolve>{resolve1, resolve2}, Header{{\"RedirectLocation\", url2.str()}});\n\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url2, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/session_tests.cpp",
    "content": "#include <algorithm>\n#include <chrono>\n#include <cstdint>\n#include <cstdlib>\n#include <gtest/gtest.h>\n\n#include <stdexcept>\n#include <string>\n\n#include \"cpr/cpr.h\"\n#include <curl/curl.h>\n#include <vector>\n\n#include \"cpr/accept_encoding.h\"\n#include \"httpServer.hpp\"\n#include \"testUtils.hpp\"\n\nusing namespace cpr;\nusing namespace std::chrono_literals;\n\nstatic HttpServer* server = new HttpServer();\nstd::chrono::milliseconds sleep_time{50};\nstd::chrono::seconds zero{0};\n\nbool write_data(std::string_view /*data*/, intptr_t /*userdata*/) {\n    return true;\n}\n\nTEST(SessionGetTests, GetMultipleTimes) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    std::string expected_text{\"Hello world!\"};\n\n    for (size_t i = 0; i < 100; i++) {\n        Response response = session.Get();\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(SessionPostTests, PostMultipleTimes) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload({{\"x\", \"5\"}});\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n\n    for (size_t i = 0; i < 100; i++) {\n        Response response = session.Post();\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(201, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(RedirectTests, TemporaryDefaultRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(RedirectTests, NoTemporaryRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/temporary_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(false));\n    Response response = session.Get();\n    std::string expected_text{\"Moved Temporarily\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(302, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(RedirectTests, PermanentDefaultRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(RedirectTests, NoPermanentRedirectTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(false));\n    Response response = session.Get();\n    std::string expected_text{\"Moved Permanently\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(301, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MaxRedirectsTests, ZeroMaxRedirectsSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(0L));\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MaxRedirectsTests, ZeroMaxRedirectsFailureTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(0L));\n    Response response = session.Get();\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(301, response.status_code);\n    EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);\n}\n\nTEST(MaxRedirectsTests, OneMaxRedirectsSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/permanent_redirect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(1L));\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MaxRedirectsTests, OneMaxRedirectsFailureTest) {\n    Url url{server->GetBaseUrl() + \"/two_redirects.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(1L));\n    Response response = session.Get();\n    EXPECT_EQ(std::string{}, response.text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/permanent_redirect.html\"}, response.url);\n    EXPECT_EQ(std::string{}, response.header[\"content-type\"]);\n    EXPECT_EQ(301, response.status_code);\n    EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);\n}\n\nTEST(MaxRedirectsTests, TwoMaxRedirectsSuccessTest) {\n    Url url{server->GetBaseUrl() + \"/two_redirects.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetRedirect(Redirect(2L));\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{server->GetBaseUrl() + \"/hello.html\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MultipleGetTests, BasicMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    for (size_t i = 0; i < 100; ++i) {\n        Response response = session.Get();\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, UrlChangeMultipleGetTest) {\n    Session session;\n    {\n        Url url{server->GetBaseUrl() + \"/hello.html\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Url url{server->GetBaseUrl() + \"/basic.json\"};\n        session.SetUrl(url);\n        Response response = session.Get();\n        std::string expected_text{\n                \"[\\n\"\n                \"  {\\n\"\n                \"    \\\"first_key\\\": \\\"first_value\\\",\\n\"\n                \"    \\\"second_key\\\": \\\"second_value\\\"\\n\"\n                \"  }\\n\"\n                \"]\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, HeaderMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetHeader(Header{{\"hello\", \"world\"}});\n    for (size_t i = 0; i < 100; ++i) {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, HeaderChangeMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetHeader(Header{{\"hello\", \"world\"}});\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(std::string{\"world\"}, response.header[\"hello\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    session.SetHeader(Header{{\"key\", \"value\"}, {\"lorem\", \"ipsum\"}});\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(std::string{\"value\"}, response.header[\"key\"]);\n        EXPECT_EQ(std::string{\"ipsum\"}, response.header[\"lorem\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    Header& headerMap = session.GetHeader();\n    headerMap.erase(\"key\");\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(std::string{\"ipsum\"}, response.header[\"lorem\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, ParameterMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetParameters({{\"hello\", \"world\"}});\n    for (size_t i = 0; i < 100; ++i) {\n        Response response = session.Get();\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, ParameterChangeMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetParameters({{\"hello\", \"world\"}});\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    session.SetUrl(url);\n    session.SetParameters({{\"key\", \"value\"}});\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Hello world!\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(Url{url + \"?key=value\"}, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, BasicAuthenticationMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAuth(Authentication{\"user\", \"password\", AuthMode::BASIC});\n    for (size_t i = 0; i < 100; ++i) {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(MultipleGetTests, BasicAuthenticationChangeMultipleGetTest) {\n    Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAuth(Authentication{\"user\", \"password\", AuthMode::BASIC});\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    session.SetAuth(Authentication{\"user\", \"bad_password\", AuthMode::BASIC});\n    {\n        Response response = session.Get();\n        EXPECT_EQ(std::string{\"Unauthorized\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(401, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    session.SetAuth(Authentication{\"bad_user\", \"password\", AuthMode::BASIC});\n    {\n        Response response = session.Get();\n        EXPECT_EQ(std::string{\"Unauthorized\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(401, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(ParameterTests, ParameterSingleTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    Parameters parameters{{\"hello\", \"world\"}};\n    session.SetParameters(parameters);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?hello=world\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ParameterTests, ParameterMultipleTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    Parameters parameters{{\"hello\", \"world\"}, {\"key\", \"value\"}};\n    session.SetParameters(parameters);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(Url{url + \"?hello=world&key=value\"}, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(FullRequestUrlTest, GetFullRequestUrlNoParametersTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    std::string expected_text{server->GetBaseUrl() + \"/hello.html\"};\n    EXPECT_EQ(expected_text, session.GetFullRequestUrl());\n}\n\nTEST(FullRequestUrlTest, GetFullRequestUrlOneParameterTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    Parameters parameters{{\"hello\", \"world\"}};\n    session.SetParameters(parameters);\n    std::string expected_text{server->GetBaseUrl() + \"/hello.html\" + \"?hello=world\"};\n    EXPECT_EQ(expected_text, session.GetFullRequestUrl());\n}\n\nTEST(FullRequestUrlTest, GetFullRequestUrlMultipleParametersTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    Parameters parameters{{\"hello\", \"world\"}, {\"key\", \"value\"}};\n    session.SetParameters(parameters);\n    std::string expected_text{server->GetBaseUrl() + \"/hello.html\" + \"?hello=world&key=value\"};\n    EXPECT_EQ(expected_text, session.GetFullRequestUrl());\n}\n\nTEST(TimeoutTests, SetTimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(0L);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(TimeoutTests, SetTimeoutLongTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(10000L);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(TimeoutTests, SetTimeoutLowSpeed) {\n    Url url{server->GetBaseUrl() + \"/low_speed_timeout.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(1000);\n    Response response = session.Get();\n    EXPECT_EQ(url, response.url);\n    // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(TimeoutTests, SetChronoTimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(std::chrono::milliseconds{0});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(TimeoutTests, SetChronoTimeoutLongTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(std::chrono::milliseconds{10000});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(TimeoutTests, SetChronoLiteralTimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(2s);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(TimeoutTests, SetChronoLiteralTimeoutLowSpeed) {\n    Url url{server->GetBaseUrl() + \"/low_speed_timeout.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetTimeout(1000ms);\n    Response response = session.Get();\n    EXPECT_EQ(url, response.url);\n    // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received\n    EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);\n}\n\nTEST(ConnectTimeoutTests, SetConnectTimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetConnectTimeout(0L);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ConnectTimeoutTests, SetConnectTimeoutLongTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetConnectTimeout(10000L);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ConnectTimeoutTests, SetChronoConnectTimeoutTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetConnectTimeout(std::chrono::milliseconds{0});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(ConnectTimeoutTests, SetChronoConnectTimeoutLongTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetConnectTimeout(std::chrono::milliseconds{10000});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(LowSpeedTests, SetLowSpeedTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetLowSpeed({1, std::chrono::seconds(1)});\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PayloadTests, SetPayloadTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetPayload({{\"x\", \"5\"}});\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(PayloadTests, SetPayloadLValueTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    Payload payload{{\"x\", \"5\"}};\n    session.SetPayload(payload);\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MultipartTests, SetMultipartTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetMultipart({{\"x\", \"5\"}});\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MultipartTests, SetMultipartValueTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    Multipart multipart{{\"x\", \"5\"}};\n    session.SetMultipart(multipart);\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(MultipartTests, SetMultipartVectorPartsTest) {\n    Url url{server->GetBaseUrl() + \"/form_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    Multipart multipart{std::vector<Part>{Part{\"x\", \"5\"}}};\n    session.SetMultipart(multipart);\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": \\\"5\\\"\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyTests, SetBodyTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetBody(Body{\"x=5\"});\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyTests, SetBodyValueTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    Body body{\"x=5\"};\n    session.SetBody(body);\n    Response response = session.Post();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(BodyTests, SetBodyViewTest) {\n    const Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetBodyView(BodyView{\"x=5\"});\n    Response response = session.Post();\n    const std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(DigestTests, SetDigestTest) {\n    Url url{server->GetBaseUrl() + \"/digest_auth.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAuth({\"user\", \"password\", AuthMode::DIGEST});\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AnyAuthTests, SetAnyTest) {\n    Url url{server->GetBaseUrl() + \"/digest_auth.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAuth({\"user\", \"password\", AuthMode::ANY});\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AnyAuthTests, SetAnySafeTest) {\n    Authentication auth = {\"user\", \"password\", AuthMode::ANYSAFE};\n    {\n        Url url{server->GetBaseUrl() + \"/digest_auth.html\"};\n        Session session;\n        session.SetUrl(url);\n        session.SetAuth(auth);\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Url url{server->GetBaseUrl() + \"/basic_auth.html\"};\n        Session session;\n        session.SetUrl(url);\n        session.SetAuth(auth);\n        Response response = session.Get();\n        EXPECT_EQ(std::string{\"Unauthorized\"}, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/plain\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(401, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(UserAgentTests, SetUserAgentTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    UserAgent userAgent{\"Test User Agent\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetUserAgent(userAgent);\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(userAgent, response.header[\"User-Agent\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(UserAgentTests, SetUserAgentStringViewTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    UserAgent userAgent{std::string_view{\"Test User Agent\"}};\n    Session session;\n    session.SetUrl(url);\n    session.SetUserAgent(userAgent);\n    Response response = session.Get();\n    std::string expected_text{\"Header reflect GET\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(userAgent, response.header[\"User-Agent\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CookiesTests, BasicCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/basic_cookies.html\"};\n    Session session{};\n    session.SetUrl(url);\n    Response response = session.Get();\n    Cookies res_cookies{response.cookies};\n    std::string expected_text{\"Basic Cookies\"};\n    cpr::Cookies expectedCookies{\n            {\"SID\", \"31d4d96e407aad42\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n            {\"lang\", \"en-US\", \"127.0.0.1\", false, \"/\", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()},\n    };\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    EXPECT_EQ(expected_text, response.text);\n    for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {\n        EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());\n        EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());\n        EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());\n        EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());\n        EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());\n        EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());\n        EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());\n    }\n}\n\nTEST(CookiesTests, ClientSetCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/cookies_reflect.html\"};\n    {\n        Session session{};\n        session.SetUrl(url);\n        session.SetCookies(Cookies{\n                {\"SID\", \"31d4d96e407aad42\"},\n                {\"lang\", \"en-US\"},\n        });\n        Response response = session.Get();\n        std::string expected_text{\"SID=31d4d96e407aad42; lang=en-US;\"};\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n        EXPECT_EQ(expected_text, response.text);\n    }\n    {\n        Session session{};\n        session.SetUrl(url);\n        Cookies cookie{\n                {\"SID\", \"31d4d96e407aad42\"},\n                {\"lang\", \"en-US\"},\n        };\n        session.SetCookies(cookie);\n        Response response = session.Get();\n        std::string expected_text{\"SID=31d4d96e407aad42; lang=en-US;\"};\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n        EXPECT_EQ(expected_text, response.text);\n    }\n}\n\nTEST(CookiesTests, RedirectionWithChangingCookiesTest) {\n    Url url{server->GetBaseUrl() + \"/redirection_with_changing_cookies.html\"};\n    {\n        Session session{};\n        session.SetUrl(url);\n        session.SetCookies(Cookies{\n                {\"SID\", \"31d4d96e407aad42\"},\n                {\"lang\", \"en-US\"},\n        });\n        session.SetRedirect(Redirect(0L));\n        Response response = session.Get();\n        std::string expected_text{\"Received cookies are: SID=31d4d96e407aad42; lang=en-US;\"};\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n        EXPECT_EQ(expected_text, response.text);\n    }\n    {\n        Session session{};\n        session.SetUrl(url);\n        session.SetRedirect(Redirect(1L));\n        Response response = session.Get();\n        std::string expected_text{\"Received cookies are: lang=en-US; SID=31d4d96e407aad42\"};\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n        EXPECT_EQ(expected_text, response.text);\n    }\n    {\n        Session session{};\n        session.SetUrl(url);\n        session.SetCookies(Cookies{\n                {\"SID\", \"empty_sid\"},\n        });\n        session.SetRedirect(Redirect(1L));\n        Response response = session.Get();\n        std::string expected_text{\"Received cookies are: lang=en-US; SID=31d4d96e407aad42; SID=empty_sid;\"};\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n        EXPECT_EQ(expected_text, response.text);\n    }\n}\n\nTEST(DifferentMethodTests, GetPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(DifferentMethodTests, PostGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(DifferentMethodTests, GetPostGetTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(DifferentMethodTests, PostGetPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(DifferentMethodTests, MultipleGetPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    for (size_t i = 0; i < 100; ++i) {\n        {\n            Response response = session.Get();\n            std::string expected_text{\"Header reflect GET\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            Response response = session.Post();\n            std::string expected_text{\"Header reflect POST\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n    }\n}\n\nTEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Url urlPost{server->GetBaseUrl() + \"/post_reflect.html\"};\n    Url urlPut{server->GetBaseUrl() + \"/put.html\"};\n    Url urlMultipartPost{server->GetBaseUrl() + \"/post_file_upload.html\"};\n    Session session;\n    for (size_t i = 0; i < 10; ++i) {\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            Response response = session.Get();\n            std::string expected_text{\"Header reflect GET\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(urlMultipartPost);\n            std::string fileContentsBinary{\"this is a binary payload\"};\n            std::string fileExtension = \".myfile\";\n            session.SetMultipart(cpr::Multipart{{\"files\", cpr::Buffer{fileContentsBinary.begin(), fileContentsBinary.end(), \"myfile.jpg\"}}, {\"file_types\", \"[\\\"\" + fileExtension + \"\\\"]\"}});\n            Response response = session.Post();\n            std::string expected_text{\"{\\n  \\\"files\\\": \\\"myfile.jpg=this is a binary payload\\\",\\n  \\\"file_types\\\": \\\"[\\\".myfile\\\"]\\\"\\n}\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(urlMultipartPost, response.url);\n            EXPECT_EQ(201, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            Response response = session.Delete();\n            std::string expected_text{\"Header reflect DELETE\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.SetUrl(urlPost);\n            std::string expectedBody = \"a1b2c3Post\";\n            session.SetBody(expectedBody);\n            Response response = session.Post();\n            EXPECT_EQ(expectedBody, response.text);\n            EXPECT_EQ(urlPost, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            Response response = session.Get();\n            std::string expected_text{\"Header reflect GET\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.SetUrl(urlPut);\n            session.SetPayload({{\"x\", \"5\"}});\n            Response response = session.Put();\n            std::string expected_text{\n                    \"{\\n\"\n                    \"  \\\"x\\\": 5\\n\"\n                    \"}\"};\n            EXPECT_EQ(expected_text, response.text);\n            EXPECT_EQ(urlPut, response.url);\n            EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n        {\n            session.RemoveContent();\n            session.SetUrl(url);\n            Response response = session.Head();\n            std::string expected_text{\"Header reflect HEAD\"};\n            EXPECT_EQ(url, response.url);\n            EXPECT_EQ(200, response.status_code);\n            EXPECT_EQ(ErrorCode::OK, response.error.code);\n        }\n    }\n}\n\nTEST(CurlHolderManipulateTests, CustomOptionTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    curl_easy_setopt(session.GetCurlHolder()->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE);\n    {\n        Response response = session.Get();\n        std::string expected_text{\"Header reflect GET\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n    {\n        Response response = session.Post();\n        std::string expected_text{\"Header reflect POST\"};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(200, response.status_code);\n        EXPECT_EQ(ErrorCode::OK, response.error.code);\n    }\n}\n\nTEST(LocalPortTests, SetLocalPortTest) {\n    Url url{server->GetBaseUrl() + \"/local_port.html\"};\n    Session session;\n    Response response;\n    session.SetUrl(url);\n    uint16_t local_port{0};\n    ASSERT_NO_THROW(local_port = cpr::test::get_free_port());\n    uint16_t local_port_range{7000};\n    session.SetLocalPort(local_port);\n    session.SetLocalPortRange(local_port_range);\n    // expected response: body contains port number in specified range\n    // NOTE: even when trying up to 7000 ports there is the chance that all of them are occupied.\n    // It would be possible to also check here for ErrorCode::UNKNOWN_ERROR but that somehow seems\n    // wrong as then this test would pass in case SetLocalPort does not work at all\n    // or in other words: we have to assume that at least one port in the specified range is free.\n    response = session.Get();\n\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    errno = 0;\n    // NOLINTNEXTLINE(google-runtime-int)\n    unsigned long port_from_response = std::strtoul(response.text.c_str(), nullptr, 10);\n    EXPECT_EQ(errno, 0);\n    EXPECT_GE(port_from_response, local_port);\n    EXPECT_LE(port_from_response, local_port + local_port_range);\n}\n\nTEST(LocalPortTests, SetOptionTest) {\n    Url url{server->GetBaseUrl() + \"/local_port.html\"};\n    Session session;\n    Response response;\n    session.SetUrl(url);\n    uint16_t local_port{0};\n    uint16_t local_port_range{7000};\n    ASSERT_NO_THROW(local_port = cpr::test::get_free_port());\n    session.SetOption(LocalPort(local_port));\n    session.SetOption(LocalPortRange(local_port_range));\n    // expected response: body contains port number in specified range\n    // NOTE: even when trying up to 7000 ports there is the chance that all of them are occupied.\n    // It would be possible to also check here for ErrorCode::UNKNOWN_ERROR but that somehow seems\n    // wrong as then this test would pass in case SetLocalPort does not work at all\n    // or in other words: we have to assume that at least one port in the specified range is free.\n    response = session.Get();\n\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    errno = 0;\n    // NOLINTNEXTLINE(google-runtime-int)\n    unsigned long port_from_response = std::strtoul(response.text.c_str(), nullptr, 10);\n    EXPECT_EQ(errno, 0);\n    EXPECT_GE(port_from_response, local_port);\n    EXPECT_LE(port_from_response, local_port + local_port_range);\n}\n\n// The tests using the port of the server as a source port for curl fail for windows.\n// The reason probably is that Windows allows two sockets to bind to the same port if the full hostname is different.\n// In these tests, mongoose binds to http://127.0.0.1:61936, while libcurl binds to a different hostname, but still port 61936.\n// This seems to be okay for Windows, however, these tests expect an error like on Linux and MacOS\n// We therefore, simply skip the tests if Windows is used\n#ifndef _WIN32\nTEST(LocalPortTests, SetLocalPortTestOccupied) {\n    Url url{server->GetBaseUrl() + \"/local_port.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetLocalPort(server->GetPort());\n    // expected response: request cannot be made as port is already occupied\n    Response response = session.Get();\n    EXPECT_EQ(ErrorCode::INTERFACE_FAILED, response.error.code);\n}\n\nTEST(LocalPortTests, SetOptionTestOccupied) {\n    Url url{server->GetBaseUrl() + \"/local_port.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetOption(LocalPort(server->GetPort()));\n    // expected response: request cannot be made as port is already occupied\n    Response response = session.Get();\n    EXPECT_EQ(ErrorCode::INTERFACE_FAILED, response.error.code);\n}\n#endif // _WIN32\n\nTEST(BasicTests, ReserveResponseString) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetReserveSize(4096);\n    Response response = session.Get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_GE(response.text.capacity(), 4096);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nstd::vector<std::string> Split(const std::string& s) {\n    std::vector<std::string> encodings;\n    std::stringstream ss(s);\n    std::string encoding;\n\n    while (std::getline(ss, encoding, ',')) {\n        encoding.erase(std::remove_if(encoding.begin(), encoding.end(), isspace), encoding.end()); // Trim\n        encodings.push_back(encoding);\n    }\n\n    return encodings;\n}\n\nvoid CompareEncodings(const std::string& response, const std::vector<std::string>& expected) {\n    const std::vector<std::string> responseVec = Split(response);\n\n    EXPECT_EQ(responseVec.size(), expected.size());\n    for (const std::string& encoding : expected) {\n        EXPECT_TRUE(std::find(responseVec.begin(), responseVec.end(), encoding) != responseVec.end());\n    }\n}\n\nTEST(BasicTests, AcceptEncodingTestWithMethodsStringMap) {\n    Url url{server->GetBaseUrl() + \"/check_accept_encoding.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAcceptEncoding({{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}});\n    Response response = session.Get();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    CompareEncodings(response.text, std::vector<std::string>{\"deflate\", \"gzip\", \"zlib\"});\n}\n\nTEST(BasicTests, AcceptEncodingTestWithMethodsStringMapLValue) {\n    Url url{server->GetBaseUrl() + \"/check_accept_encoding.html\"};\n    Session session;\n    session.SetUrl(url);\n    AcceptEncoding accept_encoding{{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}};\n    session.SetAcceptEncoding(accept_encoding);\n    Response response = session.Get();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    CompareEncodings(response.text, std::vector<std::string>{\"deflate\", \"gzip\", \"zlib\"});\n}\n\nTEST(BasicTests, AcceptEncodingTestWithCostomizedString) {\n    Url url{server->GetBaseUrl() + \"/check_accept_encoding.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAcceptEncoding({{\"deflate\", \"gzip\", \"zlib\"}});\n    Response response = session.Get();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    CompareEncodings(response.text, std::vector<std::string>{\"deflate\", \"gzip\", \"zlib\"});\n}\n\nTEST(BasicTests, AcceptEncodingTestWithCostomizedStringLValue) {\n    Url url{server->GetBaseUrl() + \"/check_accept_encoding.html\"};\n    Session session;\n    session.SetUrl(url);\n    AcceptEncoding accept_encoding{{\"deflate\", \"gzip\", \"zlib\"}};\n    session.SetAcceptEncoding(accept_encoding);\n    Response response = session.Get();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    CompareEncodings(response.text, std::vector<std::string>{\"deflate\", \"gzip\", \"zlib\"});\n}\n\nTEST(BasicTests, AcceptEncodingTestDisabled) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAcceptEncoding({AcceptEncodingMethods::disabled});\n    Response response = session.Get();\n\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n    // Ensure no 'Accept-Encoding' header got added\n    EXPECT_TRUE(response.header.find(\"Accept-Encoding\") == response.header.end());\n}\n\nTEST(BasicTests, AcceptEncodingTestDisabledMultipleThrow) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    Session session;\n    session.SetUrl(url);\n    session.SetAcceptEncoding({AcceptEncodingMethods::disabled, AcceptEncodingMethods::deflate});\n    EXPECT_THROW(session.Get(), std::invalid_argument);\n}\n\nTEST(BasicTests, DisableHeaderExpect100ContinueTest) {\n    Url url{server->GetBaseUrl() + \"/check_expect_100_continue.html\"};\n    std::string filename{\"test_file\"};\n    std::string content{std::string(1024 * 1024, 'a')};\n    std::ofstream test_file;\n    test_file.open(filename);\n    test_file << content;\n    test_file.close();\n    Session session{};\n    session.SetUrl(url);\n    session.SetMultipart({{\"file\", File{\"test_file\"}}});\n    Response response = session.Post();\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncGetTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    cpr::AsyncResponse future = session->GetAsync();\n    std::string expected_text{\"Hello world!\"};\n    cpr::Response response = future.get();\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n}\n\nTEST(AsyncRequestsTests, AsyncGetMultipleTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n\n    std::vector<AsyncResponse> responses;\n    std::vector<std::shared_ptr<Session>> sessions;\n    for (size_t i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        sessions.emplace_back(session);\n        responses.emplace_back(session->GetAsync());\n    }\n\n    for (cpr::AsyncResponse& future : responses) {\n        std::string expected_text{\"Hello world!\"};\n        cpr::Response response = future.get();\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n}\n\nTEST(AsyncRequestsTests, AsyncGetMultipleTemporarySessionTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        responses.emplace_back(session->GetAsync());\n    }\n\n    for (cpr::AsyncResponse& future : responses) {\n        std::string expected_text{\"Hello world!\"};\n        cpr::Response response = future.get();\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n    }\n}\n\nTEST(AsyncRequestsTests, AsyncGetMultipleReflectTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::vector<AsyncResponse> responses;\n    for (size_t i = 0; i < 10; ++i) {\n        std::shared_ptr<Session> session = std::make_shared<Session>();\n        session->SetUrl(url);\n        session->SetParameters({{\"key\", std::to_string(i)}});\n        responses.emplace_back(session->GetAsync());\n    }\n    int i = 0;\n    for (cpr::AsyncResponse& future : responses) {\n        cpr::Response response = future.get();\n        std::string expected_text{\"Hello world!\"};\n        Url expected_url{url + \"?key=\" + std::to_string(i)};\n        EXPECT_EQ(expected_text, response.text);\n        EXPECT_EQ(expected_url, response.url);\n        EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n        EXPECT_EQ(200, response.status_code);\n        ++i;\n    }\n}\n\nTEST(AsyncRequestsTests, AsyncWritebackDownloadTest) {\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    cpr::Url url{server->GetBaseUrl() + \"/download_gzip.html\"};\n    session->SetUrl(url);\n    session->SetHeader(cpr::Header{{\"Accept-Encoding\", \"gzip\"}});\n    cpr::AsyncResponse future = session->DownloadAsync(cpr::WriteCallback{write_data, 0});\n    cpr::Response response = future.get();\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncPostTest) {\n    Url url{server->GetBaseUrl() + \"/url_post.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload({{\"x\", \"5\"}});\n    cpr::AsyncResponse future = session->PostAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(201, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncPutTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload({{\"x\", \"5\"}});\n    cpr::AsyncResponse future = session->PutAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncHeadTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    cpr::AsyncResponse future = session->HeadAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncDeleteTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    cpr::AsyncResponse future = session->DeleteAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect DELETE\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncOptionsTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    cpr::AsyncResponse future = session->OptionsAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect OPTIONS\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(AsyncRequestsTests, AsyncPatchTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    cpr::AsyncResponse future = session->PatchAsync();\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect PATCH\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, GetCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->GetCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"Hello world!\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, PostCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->PostCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect POST\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, PutCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/put.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    session->SetPayload({{\"x\", \"5\"}});\n    auto future = session->PutCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\n            \"{\\n\"\n            \"  \\\"x\\\": 5\\n\"\n            \"}\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"application/json\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, HeadCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->HeadCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, DeleteCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->DeleteCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect DELETE\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, OptionsCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->OptionsCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect OPTIONS\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, PatchCallbackTest) {\n    Url url{server->GetBaseUrl() + \"/header_reflect.html\"};\n    std::shared_ptr<Session> session = std::make_shared<Session>();\n    session->SetUrl(url);\n    auto future = session->PatchCallback([](Response r) { return r; });\n    std::this_thread::sleep_for(sleep_time);\n    cpr::Response response = future.get();\n    std::string expected_text{\"Header reflect PATCH\"};\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code);\n}\n\nTEST(CallbackTests, Move) {\n    auto session = Session();\n    session.SetDebugCallback(DebugCallback([](auto, auto, auto) {}));\n\n    auto use = +[](Session& s) {\n        s.SetUrl(server->GetBaseUrl());\n        s.Get();\n    };\n    use(session);\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/singleton_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include \"singleton_tests.hpp\"\n\n// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)\nCPR_SINGLETON_IMPL(TestSingleton)\n\nTEST(SingletonTests, GetInstanceTest) {\n    const TestSingleton* singleton = TestSingleton::GetInstance();\n    EXPECT_NE(singleton, nullptr);\n}\n\nTEST(SingletonTests, ExitInstanceTest) {\n    TestSingleton* singleton = TestSingleton::GetInstance();\n    TestSingleton::ExitInstance();\n    singleton = TestSingleton::GetInstance();\n    EXPECT_EQ(singleton, nullptr);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/singleton_tests.hpp",
    "content": "#pragma once\n\n#include \"cpr/cpr.h\"\n\nclass TestSingleton {\n    CPR_SINGLETON_DECL(TestSingleton)\n  private:\n    TestSingleton() = default;\n};\n"
  },
  {
    "path": "test/sse_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <chrono>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include \"cpr/cpr.h\"\n#include \"cpr/sse.h\"\n#include \"httpServer.hpp\"\n\nusing namespace cpr;\n\nstatic HttpServer* server = new HttpServer();\n\nTEST(SSETests, SSEParserBasicTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"data: Hello World\\n\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Hello World\");\n    EXPECT_EQ(events[0].event, \"message\");\n    EXPECT_FALSE(events[0].id.has_value());\n    EXPECT_FALSE(events[0].retry.has_value());\n}\n\nTEST(SSETests, SSEParserMultilineDataTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"data: First line\\ndata: Second line\\n\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"First line\\nSecond line\");\n    EXPECT_EQ(events[0].event, \"message\");\n}\n\nTEST(SSETests, SSEParserWithEventTypeTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"event: custom\\ndata: Custom event data\\n\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Custom event data\");\n    EXPECT_EQ(events[0].event, \"custom\");\n}\n\nTEST(SSETests, SSEParserWithIdTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"id: 123\\ndata: Event with ID\\n\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Event with ID\");\n    EXPECT_EQ(events[0].event, \"message\");\n    ASSERT_TRUE(events[0].id.has_value());\n    EXPECT_EQ(events[0].id.value(), \"123\");\n}\n\nTEST(SSETests, SSEParserWithRetryTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"retry: 5000\\ndata: Event with retry\\n\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Event with retry\");\n    ASSERT_TRUE(events[0].retry.has_value());\n    EXPECT_EQ(events[0].retry.value(), 5000);\n}\n\nTEST(SSETests, SSEParserCompleteEventTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data =\n            \"id: 42\\n\"\n            \"event: update\\n\"\n            \"retry: 3000\\n\"\n            \"data: Complete event data\\n\"\n            \"\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Complete event data\");\n    EXPECT_EQ(events[0].event, \"update\");\n    ASSERT_TRUE(events[0].id.has_value());\n    EXPECT_EQ(events[0].id.value(), \"42\");\n    ASSERT_TRUE(events[0].retry.has_value());\n    EXPECT_EQ(events[0].retry.value(), 3000);\n}\n\nTEST(SSETests, SSEParserMultipleEventsTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data =\n            \"data: First event\\n\"\n            \"\\n\"\n            \"data: Second event\\n\"\n            \"\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 2);\n    EXPECT_EQ(events[0].data, \"First event\");\n    EXPECT_EQ(events[1].data, \"Second event\");\n}\n\nTEST(SSETests, SSEParserIgnoreCommentsTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data =\n            \": This is a comment\\n\"\n            \"data: Event data\\n\"\n            \": Another comment\\n\"\n            \"\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Event data\");\n}\n\nTEST(SSETests, SSEParserIgnoreEmptyDataTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data =\n            \"event: test\\n\"\n            \"\\n\"; // Event with no data should be ignored\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 0);\n}\n\nTEST(SSETests, SSEParserChunkedDataTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    // Simulate data arriving in chunks\n    parser.parse(\"data: Partial\", [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    EXPECT_EQ(events.size(), 0); // No complete event yet\n\n    parser.parse(\" event\\n\\n\", [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Partial event\");\n}\n\nTEST(SSETests, SSEParserCRLFTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data = \"data: Windows line endings\\r\\n\\r\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Windows line endings\");\n}\n\nTEST(SSETests, SSEParserFieldWithoutColonTest) {\n    ServerSentEventParser parser;\n    std::vector<ServerSentEvent> events;\n\n    std::string sse_data =\n            \"data: Some actual data\\n\"\n            \"data\\n\" // Field without colon should have empty value, appended with newline\n            \"\\n\";\n\n    parser.parse(sse_data, [&events](ServerSentEvent&& event) {\n        events.push_back(std::move(event));\n        return true;\n    });\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Some actual data\\n\");\n}\n\nTEST(SSETests, SSECallbackTest) {\n    ServerSentEventCallback callback(\n            [](ServerSentEvent&& /*event*/, intptr_t userdata) {\n                int* count = reinterpret_cast<int*>(userdata);\n                (*count)++;\n                return true;\n            },\n            0);\n\n    EXPECT_TRUE(callback.callback);\n}\n\nTEST(SSETests, SSECallbackHandleDataTest) {\n    std::vector<ServerSentEvent> events;\n    ServerSentEventCallback callback(\n            [&events](ServerSentEvent&& event, intptr_t /*userdata*/) {\n                events.push_back(std::move(event));\n                return true;\n            },\n            0);\n\n    std::string sse_data = \"data: Test event\\n\\n\";\n    callback.handleData(sse_data);\n\n    ASSERT_EQ(events.size(), 1);\n    EXPECT_EQ(events[0].data, \"Test event\");\n}\n\nTEST(SSETests, SSECallbackAbortTest) {\n    int event_count = 0;\n    ServerSentEventCallback callback(\n            [&event_count](ServerSentEvent&& /*event*/, intptr_t /*userdata*/) {\n                event_count++;\n                // Abort after second event\n                return event_count < 2;\n            },\n            0);\n\n    std::string sse_data =\n            \"data: First\\n\\n\"\n            \"data: Second\\n\\n\"\n            \"data: Third\\n\\n\";\n\n    // First two events should be processed, third should cause abort\n    callback.handleData(sse_data);\n\n    // The callback is called twice (returns true the first time, false the second)\n    // The parser stops after the callback returns false\n    EXPECT_EQ(event_count, 2);\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(server);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/ssl_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <chrono>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include \"cpr/cprtypes.h\"\n#include \"cpr/filesystem.h\"\n#include \"cpr/ssl_options.h\"\n\n#include \"httpsServer.hpp\"\n\n\nusing namespace cpr;\n\nstatic HttpsServer* server;\n\nstatic std::string caCertPath;\nstatic std::string serverPubKeyPath;\nstatic std::string clientKeyPath;\nstatic std::string clientCertPath;\n\nstd::string loadFileContent(const std::string filePath) {\n    std::ifstream file(filePath);\n    std::stringstream buffer;\n    buffer << file.rdbuf();\n    return buffer.str();\n}\n\nTEST(SslTests, HelloWorldTestSimpel) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n\n    SslOptions sslOpts = Ssl(ssl::CaInfo{crtPath + \"ca-bundle.crt\"}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::VerifyPeer{true}, ssl::PinnedPublicKey{keyPath + \"server.pub\"}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n\nTEST(SslTests, HelloWorldTestFull) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n\n    SslOptions sslOpts = Ssl(ssl::TLSv1{}, ssl::ALPN{false},\n#if SUPPORT_NPN\n                             ssl::NPN{false},\n#endif // DEBUG\n                             ssl::CaInfo{crtPath + \"ca-bundle.crt\"}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::PinnedPublicKey{keyPath + \"server.pub\"}, ssl::VerifyPeer{true}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n\nTEST(SslTests, GetCertInfos) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n\n    SslOptions sslOpts = Ssl(ssl::CaInfo{crtPath + \"ca-bundle.crt\"}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::VerifyPeer{true}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::vector<CertInfo> certInfos = response.GetCertInfos();\n\n    std::string expected_text = \"Hello world!\";\n    std::vector<CertInfo> expectedCertInfos{\n            CertInfo{\n                    \"Subject:CN = test-server\",\n                    \"Issuer:C = GB, O = Example, CN = Sub CA\",\n                    \"Version:2\",\n                    \"Serial Number:acbefc2cde5b900b55548396556765d4\",\n                    \"Signature Algorithm:ED25519\",\n                    \"Public Key Algorithm:ED25519\",\n                    \"X509v3 Authority Key Identifier:9B:B1:9B:21:61:DC:66:2B:3A:AD:ED:84:F1:05:B6:CE:99:82:C1:FC\",\n                    \"X509v3 Basic Constraints:CA:FALSE\",\n                    \"X509v3 Extended Key Usage:TLS Web Client Authentication, TLS Web Server Authentication\",\n                    \"X509v3 Key Usage:Digital Signature, Key Encipherment\",\n                    \"X509v3 Subject Key Identifier:66:47:54:F8:25:97:56:9A:52:56:35:B4:A7:52:60:0C:E7:4F:33:09\",\n                    \"X509v3 Subject Alternative Name:DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1\",\n                    \"Start date:May  7 10:18:22 2024 GMT\",\n                    \"Expire date:May  6 10:18:22 2029 GMT\",\n                    \"Signature:6d:63:d9:11:a3:9b:c7:9f:b6:23:12:27:e9:34:e0:a1:a3:20:be:fb:df:80:fe:53:08:9d:8c:e4:82:42:76:c2:55:13:e8:7c:86:83:33:0b:9a:9f:92:2a:3f:de:e9:32:78:c0:b1:bc:3f:42:e9:17:f9:9f:6c:15:35:a3:01:09:\",\n                    R\"(Cert:-----BEGIN CERTIFICATE-----\nMIIBtDCCAWagAwIBAgIRAKy+/CzeW5ALVVSDllVnZdQwBQYDK2VwMDAxCzAJBgNV\nBAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw\nNTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl\ncjAqMAUGAytlcAMhACdLUqJFSyspgGKJiXNlnOLU2dO/TLV+b8aIZNAX7EuVo4Gu\nMIGrMB8GA1UdIwQYMBaAFJuxmyFh3GYrOq3thPEFts6ZgsH8MAwGA1UdEwEB/wQC\nMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIF\noDAdBgNVHQ4EFgQUZkdU+CWXVppSVjW0p1JgDOdPMwkwLAYDVR0RBCUwI4IJbG9j\nYWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMAUGAytlcANBAG1j2RGjm8ef\ntiMSJ+k04KGjIL7734D+UwidjOSCQnbCVRPofIaDMwuan5IqP97pMnjAsbw/QukX\n+Z9sFTWjAQk=\n-----END CERTIFICATE-----\n)\",\n            },\n    };\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n    EXPECT_EQ(1, certInfos.size());\n    for (auto certInfo_it = certInfos.begin(), expectedCertInfo_it = expectedCertInfos.begin(); certInfo_it != certInfos.end() && expectedCertInfo_it != expectedCertInfos.end(); certInfo_it++, expectedCertInfo_it++) {\n        for (auto entry_it = (*certInfo_it).begin(), expectedEntry_it = (*expectedCertInfo_it).begin(); entry_it != (*certInfo_it).end() && expectedEntry_it != (*expectedCertInfo_it).end(); entry_it++, expectedEntry_it++) {\n            std::string search_string = \"Identifier:keyid:\";\n            std::size_t search_index = (*entry_it).find(search_string);\n            if (search_index != std::string::npos) {\n                (*entry_it).replace(search_index, search_string.length(), \"Identifier:\");\n                search_string = \"\\n\";\n                search_index = (*entry_it).find(search_string);\n                if (search_index != std::string::npos) {\n                    (*entry_it).replace(search_index, search_string.length(), \"\");\n                }\n            }\n            EXPECT_EQ(*expectedEntry_it, *entry_it);\n        }\n        std::cout << '\\n';\n    }\n}\n\n#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION\nTEST(SslTests, LoadCertFromBufferTestSimpel) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n    std::string certBuffer = loadFileContent(crtPath + \"ca-bundle.crt\");\n    SslOptions sslOpts = Ssl(ssl::CaBuffer{std::move(certBuffer)}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::VerifyPeer{true}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n#endif\n\n#if SUPPORT_CURLOPT_SSLCERT_BLOB\nTEST(SslTests, LoadCertFromBlobTestSimpel) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n    std::string crtBuffer = loadFileContent(crtPath + \"client.crt\");\n    SslOptions sslOpts = Ssl(ssl::CaInfo{crtPath + \"ca-bundle.crt\"}, ssl::CertBlob{std::move(crtBuffer)}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::VerifyPeer{true}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n#endif\n\n#if SUPPORT_CURLOPT_SSLKEY_BLOB\nTEST(SslTests, LoadKeyFromBlobTestSimpel) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n    std::string keyBuffer = loadFileContent(keyPath + \"client.key\");\n    SslOptions sslOpts = Ssl(ssl::CaInfo{crtPath + \"ca-bundle.crt\"}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyBlob{std::move(keyBuffer)}, ssl::VerifyPeer{true}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n#endif\n\n#if SUPPORT_CURLOPT_CAINFO_BLOB\nTEST(SslTests, CaInfoBlobTestSimpel) {\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    Url url{server->GetBaseUrl() + \"/hello.html\"};\n    std::string baseDirPath{server->getBaseDirPath()};\n    std::string crtPath{baseDirPath + \"certificates/\"};\n    std::string keyPath{baseDirPath + \"keys/\"};\n\n    SslOptions sslOpts = Ssl(ssl::CaInfoBlob{loadFileContent(crtPath + \"ca-bundle.crt\")}, ssl::CertFile{crtPath + \"client.crt\"}, ssl::KeyFile{keyPath + \"client.key\"}, ssl::VerifyPeer{true}, ssl::PinnedPublicKey{keyPath + \"server.pub\"}, ssl::VerifyHost{true}, ssl::VerifyStatus{false});\n    Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});\n    std::string expected_text = \"Hello world!\";\n    EXPECT_EQ(expected_text, response.text);\n    EXPECT_EQ(url, response.url);\n    EXPECT_EQ(std::string{\"text/html\"}, response.header[\"content-type\"]);\n    EXPECT_EQ(200, response.status_code);\n    EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;\n}\n#endif\n\nfs::path GetBasePath(const std::string& execPath) {\n    return fs::path(fs::path{execPath}.parent_path().string() + \"/\").make_preferred();\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    fs::path baseDirPath = fs::path{GetBasePath(argv[0]).string() + \"data/\"};\n    fs::path serverCertPath = fs::path{baseDirPath}.append(\"certificates/server.crt\");\n    fs::path serverKeyPath = fs::path{baseDirPath}.append(\"keys/server.key\");\n    server = new HttpsServer(std::move(baseDirPath), std::move(serverCertPath), std::move(serverKeyPath));\n    ::testing::AddGlobalTestEnvironment(server);\n\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/structures_tests.cpp",
    "content": "#include \"cpr/cprtypes.h\"\n#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/parameters.h\"\n#include \"cpr/payload.h\"\n\nusing namespace cpr;\n\nTEST(PayloadTests, UseStringVariableTest) {\n    std::string value1 = \"hello\";\n    std::string key2 = \"key2\";\n    Payload payload{{\"key1\", value1}, {key2, \"world\"}};\n\n    std::string expected = \"key1=hello&key2=world\";\n    EXPECT_EQ(payload.GetContent(CurlHolder()), expected);\n}\n\nTEST(PayloadTests, DisableEncodingTest) {\n    std::string key1 = \"key1\";\n    std::string key2 = \"key2§$%&/\";\n    std::string value1 = \"hello.,.,\";\n    std::string value2 = \"hello\";\n    Payload payload{{key1, value1}, {key2, value2}};\n    payload.encode = false;\n\n    std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2;\n    EXPECT_EQ(payload.GetContent(CurlHolder()), expected);\n}\n\nTEST(ParametersTests, UseStringVariableTest) {\n    std::string value1 = \"hello\";\n    std::string key2 = \"key2\";\n    Parameters parameters{{\"key1\", value1}, {key2, \"world\"}};\n\n    std::string expected = \"key1=hello&key2=world\";\n    EXPECT_EQ(parameters.GetContent(CurlHolder()), expected);\n}\n\nTEST(ParametersTests, DisableEncodingTest) {\n    std::string key1 = \"key1\";\n    std::string key2 = \"key2§$%&/\";\n    std::string value1 = \"hello.,.,\";\n    std::string value2 = \"hello\";\n    Parameters parameters{{key1, value1}, {key2, value2}};\n    parameters.encode = false;\n\n    std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2;\n    EXPECT_EQ(parameters.GetContent(CurlHolder()), expected);\n}\n\nTEST(ParametersTests, NoCurlHolderTest) {\n    std::string key1 = \"key1\";\n    std::string key2 = \"key2§$%&/\";\n    std::string value1 = \"hello.,.,\";\n    std::string value2 = \"hello\";\n    Parameters parameters{{key1, value1}, {key2, value2}};\n    const std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2;\n    EXPECT_EQ(parameters.GetContent(), expected);\n}\n\nTEST(UrlToAndFromString, UrlTests) {\n    std::string s{\"https://github.com/whoshuu/cpr\"};\n    cpr::Url url = s;\n    EXPECT_EQ(s, url.str());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/testUtils.cpp",
    "content": "#include \"testUtils.hpp\"\n\n#include <cstdint>\n#include <stdexcept>\n\n#ifdef _WIN32\n#define NOMINMAX\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#else\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n\nnamespace cpr::test {\n\nstd::uint16_t get_free_port() {\n#ifdef _WIN32\n    static const WSAInit wsa_guard; // one-time Winsock init\n#endif\n\n    // 1. Create a TCP socket.\n    socket_t sock = ::socket(AF_INET, SOCK_STREAM, 0);\n    if (sock == INVALID_SOCKET_FD) {\n        throw std::runtime_error(\"socket() failed\");\n    }\n\n    // 2. Bind to port 0 so the OS assigns an ephemeral port.\n    sockaddr_in addr{};\n    addr.sin_family = AF_INET;\n    addr.sin_addr.s_addr = htonl(INADDR_ANY);\n    addr.sin_port = htons(0); // 0 ⇒ “pick for me”\n\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    if (::bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != 0) {\n#ifdef _WIN32\n        ::closesocket(sock);\n#else\n        ::close(sock);\n#endif\n        throw std::runtime_error(\"bind() failed\");\n    }\n\n    // 3. Ask what port we actually got.\n    socklen_t len = sizeof(addr);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    if (::getsockname(sock, reinterpret_cast<sockaddr*>(&addr), &len) != 0) {\n#ifdef _WIN32\n        ::closesocket(sock);\n#else\n        ::close(sock);\n#endif\n        throw std::runtime_error(\"getsockname() failed\");\n    }\n\n    std::uint16_t port = ntohs(addr.sin_port);\n\n    // 4. Close the socket ‒ we only needed it to grab the port number.\n#ifdef _WIN32\n    ::closesocket(sock);\n#else\n    ::close(sock);\n#endif\n\n    return port;\n}\n} // namespace cpr::test\n"
  },
  {
    "path": "test/testUtils.hpp",
    "content": "#ifndef CPR_TEST_UTILS_H\n#define CPR_TEST_UTILS_H\n#include <cstdint>\n#include <stdexcept>\n\n#ifdef _WIN32\n#define NOMINMAX\n#include <winsock2.h>\n#pragma comment(lib, \"ws2_32.lib\")\n#endif\n\nnamespace cpr::test {\n\n#ifdef _WIN32\nusing socket_t = SOCKET;\nconstexpr socket_t INVALID_SOCKET_FD = INVALID_SOCKET;\n\nstruct WSAInit {\n    WSAInit() {\n        WSADATA data;\n        if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {\n            throw std::runtime_error(\"WSAStartup failed\");\n        }\n    }\n    ~WSAInit() {\n        WSACleanup();\n    }\n};\n#else\nusing socket_t = int;\nconstexpr socket_t INVALID_SOCKET_FD = -1;\n#endif\n\n/// Return a currently unused TCP port.\n/// \\throws std::runtime_error on failure.\nuint16_t get_free_port();\n} // namespace cpr::test\n#endif"
  },
  {
    "path": "test/testUtils_tests.cpp",
    "content": "#include <cstdint>\n#include <gtest/gtest.h>\n\n#include \"testUtils.hpp\"\n\nTEST(TestUtils, GetUnusedPort) {\n    uint16_t port{0};\n    ASSERT_NO_THROW(port = cpr::test::get_free_port());\n    EXPECT_NE(port, 0);\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/threadpool_tests.cpp",
    "content": "#include <atomic>\n#include <cstddef>\n#include <gtest/gtest.h>\n\n\n#include \"cpr/threadpool.h\"\n\nTEST(ThreadPoolTests, DISABLED_BasicWorkOneThread) {\n    std::atomic_uint32_t invCount{0};\n    uint32_t invCountExpected{100};\n\n    {\n        cpr::ThreadPool tp;\n        tp.SetMinThreadNum(1);\n        tp.SetMaxThreadNum(1);\n        tp.Start(0);\n\n        for (size_t i = 0; i < invCountExpected; ++i) {\n            tp.Submit([&invCount]() -> void { invCount++; });\n        }\n\n        // Wait for the thread pool to finish its work\n        tp.Wait();\n    }\n\n    EXPECT_EQ(invCount, invCountExpected);\n}\n\nTEST(ThreadPoolTests, DISABLED_BasicWorkMultipleThreads) {\n    std::atomic_uint32_t invCount{0};\n    uint32_t invCountExpected{100};\n\n    {\n        cpr::ThreadPool tp;\n        tp.SetMinThreadNum(1);\n        tp.SetMaxThreadNum(10);\n        tp.Start(0);\n\n        for (size_t i = 0; i < invCountExpected; ++i) {\n            tp.Submit([&invCount]() -> void { invCount++; });\n        }\n\n        // Wait for the thread pool to finish its work\n        tp.Wait();\n    }\n\n    EXPECT_EQ(invCount, invCountExpected);\n}\n\nTEST(ThreadPoolTests, DISABLED_PauseResumeSingleThread) {\n    std::atomic_uint32_t invCount{0};\n\n    uint32_t repCount{100};\n    uint32_t invBunchSize{20};\n\n    cpr::ThreadPool tp;\n    tp.SetMinThreadNum(1);\n    tp.SetMaxThreadNum(10);\n    tp.Start(0);\n\n    for (size_t i = 0; i < repCount; ++i) {\n        tp.Pause();\n        EXPECT_EQ(invCount, i * invBunchSize);\n\n        for (size_t e = 0; e < invBunchSize; ++e) {\n            tp.Submit([&invCount]() -> void { invCount++; });\n        }\n        tp.Resume();\n        // Wait for the thread pool to finish its work\n        tp.Wait();\n\n        EXPECT_EQ(invCount, (i + 1) * invBunchSize);\n    }\n}\n\nTEST(ThreadPoolTests, DISABLED_PauseResumeMultipleThreads) {\n    std::atomic_uint32_t invCount{0};\n\n    uint32_t repCount{100};\n    uint32_t invBunchSize{20};\n\n    cpr::ThreadPool tp;\n    tp.SetMinThreadNum(1);\n    tp.SetMaxThreadNum(10);\n    tp.Start(0);\n\n    for (size_t i = 0; i < repCount; ++i) {\n        tp.Pause();\n        EXPECT_EQ(invCount, i * invBunchSize);\n\n        for (size_t e = 0; e < invBunchSize; ++e) {\n            tp.Submit([&invCount]() -> void { invCount++; });\n        }\n        tp.Resume();\n        // Wait for the thread pool to finish its work\n        tp.Wait();\n\n        EXPECT_EQ(invCount, (i + 1) * invBunchSize);\n    }\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/util_tests.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <string>\n\n#include \"cpr/cprtypes.h\"\n#include \"cpr/util.h\"\n\nusing namespace cpr;\n\nTEST(UtilParseCookiesTests, BasicParseTest) {\n    Cookies expectedCookies{{Cookie(\"status\", \"on\", \"127.0.0.1\", false, \"/\", false, std::chrono::system_clock::from_time_t(1656908640)), Cookie(\"name\", \"debug\", \"127.0.0.1\", false, \"/\", false, std::chrono::system_clock::from_time_t(0))}};\n    curl_slist* raw_cookies = new curl_slist{\n            (char*) \"127.0.0.1\\tFALSE\\t/\\tFALSE\\t1656908640\\tstatus\\ton\",\n            new curl_slist{\n                    (char*) \"127.0.0.1\\tFALSE\\t/\\tFALSE\\t0\\tname\\tdebug\",\n                    nullptr,\n            },\n    };\n    Cookies cookies = util::parseCookies(raw_cookies);\n    for (auto cookie = cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {\n        EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());\n        EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());\n        EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());\n        EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());\n        EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());\n        EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());\n        EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());\n    }\n    delete raw_cookies->next;\n    delete raw_cookies;\n}\n\nTEST(UtilParseHeaderTests, BasicParseTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\r\\n\"\n            \"Server: nginx\\r\\n\"\n            \"Date: Sun, 05 Mar 2017 00:34:54 GMT\\r\\n\"\n            \"Content-Type: application/json\\r\\n\"\n            \"Content-Length: 351\\r\\n\"\n            \"Connection: keep-alive\\r\\n\"\n            \"Access-Control-Allow-Origin: *\\r\\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"\\r\\n\"};\n    Header header = util::parseHeader(header_string);\n    EXPECT_EQ(std::string{\"nginx\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"Sun, 05 Mar 2017 00:34:54 GMT\"}, header[\"Date\"]);\n    EXPECT_EQ(std::string{\"application/json\"}, header[\"Content-Type\"]);\n    EXPECT_EQ(std::string{\"351\"}, header[\"Content-Length\"]);\n    EXPECT_EQ(std::string{\"keep-alive\"}, header[\"Connection\"]);\n    EXPECT_EQ(std::string{\"*\"}, header[\"Access-Control-Allow-Origin\"]);\n    EXPECT_EQ(std::string{\"true\"}, header[\"Access-Control-Allow-Credentials\"]);\n}\n\nTEST(UtilParseHeaderTests, NewlineTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\r\\n\"\n            \"Auth:\\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"\\r\\n\"};\n    Header header = util::parseHeader(header_string);\n    EXPECT_EQ(std::string{\"\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"true\"}, header[\"Access-Control-Allow-Credentials\"]);\n}\n\nTEST(UtilParseHeaderTests, SpaceNewlineTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\r\\n\"\n            \"Auth: \\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"\\r\\n\"};\n    Header header = util::parseHeader(header_string);\n    EXPECT_EQ(std::string{\"\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"true\"}, header[\"Access-Control-Allow-Credentials\"]);\n}\n\nTEST(UtilParseHeaderTests, CarriageReturnNewlineTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\n\"\n            \"Auth:\\r\\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"\\r\\n\"};\n    Header header = util::parseHeader(header_string);\n    EXPECT_EQ(std::string{\"\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"true\"}, header[\"Access-Control-Allow-Credentials\"]);\n}\n\nTEST(UtilParseHeaderTests, SpaceCarriageReturnNewlineTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\n\"\n            \"Auth: \\r\\n\"\n            \"Access-Control-Allow-Credentials: true\\r\\n\"\n            \"\\r\\n\"};\n    Header header = util::parseHeader(header_string);\n    EXPECT_EQ(std::string{\"\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"true\"}, header[\"Access-Control-Allow-Credentials\"]);\n}\n\nTEST(UtilParseHeaderTests, BasicStatusLineTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 OK\\r\\n\"\n            \"Server: nginx\\r\\n\"\n            \"Content-Type: application/json\\r\\n\"\n            \"\\r\\n\"};\n    std::string status_line;\n    std::string reason;\n    Header header = util::parseHeader(header_string, &status_line, &reason);\n    EXPECT_EQ(std::string{\"HTTP/1.1 200 OK\"}, status_line);\n    EXPECT_EQ(std::string{\"OK\"}, reason);\n    EXPECT_EQ(std::string{\"nginx\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"application/json\"}, header[\"Content-Type\"]);\n}\n\nTEST(UtilParseHeaderTests, NewlineStatusLineTest) {\n    std::string header_string{\n            \"HTTP/1.1 407 Proxy Authentication Required\\n\"\n            \"Server: nginx\\r\\n\"\n            \"Content-Type: application/json\\r\\n\"\n            \"\\r\\n\"};\n    std::string status_line;\n    std::string reason;\n    Header header = util::parseHeader(header_string, &status_line, &reason);\n    EXPECT_EQ(std::string{\"HTTP/1.1 407 Proxy Authentication Required\"}, status_line);\n    EXPECT_EQ(std::string{\"Proxy Authentication Required\"}, reason);\n    EXPECT_EQ(std::string{\"nginx\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"application/json\"}, header[\"Content-Type\"]);\n}\n\nTEST(UtilParseHeaderTests, NoReasonSpaceTest) {\n    std::string header_string{\n            \"HTTP/1.1 200 \\n\"\n            \"Server: nginx\\r\\n\"\n            \"Content-Type: application/json\\r\\n\"\n            \"\\r\\n\"};\n    std::string status_line;\n    std::string reason;\n    Header header = util::parseHeader(header_string, &status_line, &reason);\n    EXPECT_EQ(std::string{\"HTTP/1.1 200\"}, status_line);\n    EXPECT_EQ(std::string{\"\"}, reason);\n    EXPECT_EQ(std::string{\"nginx\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"application/json\"}, header[\"Content-Type\"]);\n}\n\nTEST(UtilParseHeaderTests, NoReasonTest) {\n    std::string header_string{\n            \"HTTP/1.1 200\\n\"\n            \"Server: nginx\\r\\n\"\n            \"Content-Type: application/json\\r\\n\"\n            \"\\r\\n\"};\n    std::string status_line;\n    std::string reason;\n    Header header = util::parseHeader(header_string, &status_line, &reason);\n    EXPECT_EQ(std::string{\"HTTP/1.1 200\"}, status_line);\n    EXPECT_EQ(std::string{\"\"}, reason);\n    EXPECT_EQ(std::string{\"nginx\"}, header[\"Server\"]);\n    EXPECT_EQ(std::string{\"application/json\"}, header[\"Content-Type\"]);\n}\n\nTEST(UtilUrlEncodeTests, UnicodeEncoderTest) {\n    std::string input = \"一二三\";\n    std::string result{util::urlEncode(input)};\n    std::string expected = \"%E4%B8%80%E4%BA%8C%E4%B8%89\";\n    EXPECT_EQ(result, expected);\n}\n\nTEST(UtilUrlEncodeTests, AsciiEncoderTest) {\n    std::string input = \"Hello World!\";\n    std::string result{util::urlEncode(input)};\n    std::string expected = \"Hello%20World%21\";\n    EXPECT_EQ(result, expected);\n}\n\nTEST(UtilUrlDecodeTests, UnicodeDecoderTest) {\n    std::string input = \"%E4%B8%80%E4%BA%8C%E4%B8%89\";\n    std::string result{util::urlDecode(input)};\n    std::string expected = \"一二三\";\n    EXPECT_EQ(result, expected);\n}\n\nTEST(UtilUrlDecodeTests, AsciiDecoderTest) {\n    std::string input = \"Hello%20World%21\";\n    std::string result{util::urlDecode(input)};\n    std::string expected = \"Hello World!\";\n    EXPECT_EQ(result, expected);\n}\n\nTEST(UtilIsTrueTests, TrueTest) {\n    {\n        std::string input = \"TRUE\";\n        bool output = util::isTrue(input);\n        EXPECT_TRUE(output);\n    }\n    {\n        std::string input = \"True\";\n        bool output = util::isTrue(input);\n        EXPECT_TRUE(output);\n    }\n    {\n        std::string input = \"true\";\n        bool output = util::isTrue(input);\n        EXPECT_TRUE(output);\n    }\n}\n\nTEST(UtilIsTrueTests, FalseTest) {\n    {\n        std::string input = \"FALSE\";\n        bool output = util::isTrue(input);\n        EXPECT_FALSE(output);\n    }\n    {\n        std::string input = \"False\";\n        bool output = util::isTrue(input);\n        EXPECT_FALSE(output);\n    }\n    {\n        std::string input = \"false\";\n        bool output = util::isTrue(input);\n        EXPECT_FALSE(output);\n    }\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/version_tests.cpp",
    "content": "#include \"cpr/cpr.h\"\n#include <cctype>\n#include <cstddef>\n#include <gtest/gtest.h>\n#include <string>\n\n\nTEST(VersionTests, StringVersionExists) {\n#ifndef CPR_VERSION\n    EXPECT_TRUE(false);\n#endif // CPR_VERSION\n}\n\nTEST(VersionTests, StringVersionValid) {\n    EXPECT_TRUE(CPR_VERSION != nullptr);\n    std::string version = CPR_VERSION;\n\n    // Check if the version string is: '\\d+\\.\\d+\\.\\d+'\n    bool digit = true;\n    size_t dotCount = 0;\n    for (size_t i = 0; i < version.size(); i++) {\n        if (i == 0) {\n            EXPECT_TRUE(std::isdigit(version[i]));\n        } else if (digit) {\n            if (version[i] == '.') {\n                digit = false;\n                dotCount++;\n                continue;\n            }\n        }\n        EXPECT_TRUE(std::isdigit(version[i]));\n        digit = true;\n    }\n    EXPECT_EQ(dotCount, 2);\n}\n\nTEST(VersionTests, VersionMajorExists) {\n#ifndef CPR_VERSION_MAJOR\n    EXPECT_TRUE(false);\n#endif // CPR_VERSION_MAJOR\n}\n\nTEST(VersionTests, VersionMinorExists) {\n#ifndef CPR_VERSION_MINOR\n    EXPECT_TRUE(false);\n#endif // CPR_VERSION_MINOR\n}\n\nTEST(VersionTests, VersionPatchExists) {\n#ifndef CPR_VERSION_PATCH\n    EXPECT_TRUE(false);\n#endif // CPR_VERSION_PATCH\n}\n\nTEST(VersionTests, VersionNumExists) {\n#ifndef CPR_VERSION_NUM\n    EXPECT_TRUE(false);\n#endif // CPR_VERSION_NUM\n}\n\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  }
]