pax_global_header00006660000000000000000000000064147565772220014533gustar00rootroot0000000000000052 comment=dd967cb48ea6bcbad9f1da5ada0db8ac0d532c06 cpr-1.11.2/000077500000000000000000000000001475657722200124015ustar00rootroot00000000000000cpr-1.11.2/.clang-format000066400000000000000000000034111475657722200147530ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: Google AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: true AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakTemplateDeclarations: true AlwaysBreakBeforeMultilineStrings: true BreakBeforeBinaryOperators: None BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BinPackParameters: true BinPackArguments: true ColumnLimit: 500 ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 8 DerivePointerAlignment: false ExperimentalAutoDetectBinPacking: false IndentCaseLabels: true IndentWrappedFunctionNames: false IndentFunctionDeclarationAfterType: false MaxEmptyLinesToKeep: 2 KeepEmptyLinesAtTheStartOfBlocks: false NamespaceIndentation: None PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakString: 1000 PenaltyBreakFirstLessLess: 120 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left SpacesBeforeTrailingComments: 1 Cpp11BracedListStyle: true Standard: Auto IndentWidth: 4 TabWidth: 8 UseTab: Never BreakBeforeBraces: Attach SpacesInParentheses: false SpacesInSquareBrackets: false SpacesInAngles: false SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpaceAfterCStyleCast: true SpacesInContainerLiterals: true SpaceBeforeAssignmentOperators: true ContinuationIndentWidth: 8 CommentPragmas: '^ IWYU pragma:' SpaceBeforeParens: ControlStatements ... cpr-1.11.2/.clang-tidy000066400000000000000000000025411475657722200144370ustar00rootroot00000000000000--- Checks: '*, -cppcoreguidelines-pro-type-static-cast-downcast, -fuchsia-default-arguments-calls, -fuchsia-default-arguments, -fuchsia-default-arguments-declarations, -fuchsia-overloaded-operator, -fuchsia-statically-constructed-objects, -hicpp-use-auto, -modernize-use-auto, -modernize-use-trailing-return-type, -readability-implicit-bool-conversion, -readability-const-return-type, -google-runtime-references, -misc-non-private-member-variables-in-classes, -llvm-include-order, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-pro-type-vararg, -hicpp-vararg, -cppcoreguidelines-owning-memory, -llvmlibc-callee-namespace, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -hicpp-no-array-decay, -modernize-pass-by-value, -cppcoreguidelines-pro-bounds-constant-array-index, -hicpp-signed-bitwise, -llvmlibc-implementation-in-namespace, -llvmlibc-restrict-system-libc-headers, -readability-function-cognitive-complexity, -readability-identifier-length, -altera-unroll-loops, -altera-id-dependent-backward-branch, -bugprone-easily-swappable-parameters, -modernize-return-braced-init-list, -cppcoreguidelines-avoid-magic-numbers, -readability-magic-numbers, -cppcoreguidelines-avoid-do-while, -llvmlibc-inline-function-decl, -altera-struct-pack-align, -boost-use-ranges ' WarningsAsErrors: '*' HeaderFilterRegex: 'src\/*.hpp' FormatStyle: file cpr-1.11.2/.github/000077500000000000000000000000001475657722200137415ustar00rootroot00000000000000cpr-1.11.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475657722200161245ustar00rootroot00000000000000cpr-1.11.2/.github/ISSUE_TEMPLATE/bug-report.yml000066400000000000000000000045001475657722200207340ustar00rootroot00000000000000name: "🐛 Bug report" description: Something in cpr is not working as expected? Create a report to help us improve. labels: ["Needs Investigation :mag:", "Bug :bug:"] body: - type: markdown attributes: value: | Provide a general summary of the issue in the Title above. Use Markdown to highlight and format your code! [https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf](https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf) [https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue) ⚠️⚠️ 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. ⚠️⚠️ - type: textarea attributes: label: Description description: A clear and concise description of what the bug is. placeholder: What happened? Also tell us, what did you expect to happen? validations: required: true - type: textarea attributes: label: Example/How to Reproduce description: "Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code to reproduce, if relevant." value: | 1. Create a `cpr::Session` 2. Set option ... 3. Perform the request 4. See error validations: required: true - type: textarea attributes: label: Possible Fix description: A possible fix for your issue. placeholder: Not obligatory, but suggest a fix or reason for the bug. validations: required: false - type: dropdown attributes: label: Where did you get it from? multiple: true options: - GitHub (branch e.g. master) - vcpkg - conan - NuGet - Other (specify in "Additional Context/Your Environment") validations: required: true - type: textarea attributes: label: Additional Context/Your Environment description: Provide some additional context for your issue and your environment your are trying to use cpr in. value: | - OS: - Version: validations: required: true cpr-1.11.2/.github/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000036111475657722200217710ustar00rootroot00000000000000name: "✨ Feature request" description: Suggest an idea for this project. labels: ["Needs Investigation :mag:", "Feature :sparkles:"] body: - type: markdown attributes: value: | Provide a general summary of the feature in the Title above. Use Markdown to highlight and format your code! [https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf](https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf) [https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue) ⚠️⚠️ 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. ⚠️⚠️ - type: markdown attributes: value: | Thanks for suggesting new features or pointing our missing functionality. Please describe your request in detail so we can understand your ideas. Feel free to upload additional material such as mockups, diagrams, or sketches. - type: textarea attributes: label: Is your feature request related to a problem? description: Please describe. A clear and concise description of what the problem is. placeholder: Ex. I'm always frustrated when ... - type: textarea attributes: label: Possible Solution description: Describe the solution you'd like. validations: required: true - type: textarea attributes: label: Alternatives description: A clear and concise description of any alternative solutions or features you've considered. - type: textarea attributes: label: Additional Context description: Add any other context or screenshots about the feature request here. cpr-1.11.2/.github/dependabot.yml000066400000000000000000000001651475657722200165730ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" cpr-1.11.2/.github/workflows/000077500000000000000000000000001475657722200157765ustar00rootroot00000000000000cpr-1.11.2/.github/workflows/build-deb.yml000066400000000000000000000031721475657722200203530ustar00rootroot00000000000000name: Build Debian Package on: push: tags: [ '[0-9]+.[0-9]+.[0-9]+' ] workflow_dispatch: inputs: version: description: 'The optional semantic version number. If not supplied the branch/tag will be used.' type: string jobs: package-ubuntu-latest-amd64: runs-on: ubuntu-latest steps: - name: "Checkout" uses: actions/checkout@v3 with: submodules: true path: cpr # Install packages necessary for building libcpr and package - name: "Update package list" run: sudo apt update - name: "Install cpr dependencies" run: sudo apt install -y libssl-dev libcurl4-openssl-dev - name: "Install building tools" run: sudo apt install -y cmake debmake devscripts debhelper # Set version number - name: Set version based on input if: ${{ inputs.version }} run: echo "RELEASE_VERSION=${{ inputs.version }}" >> "$GITHUB_ENV" - name: Set version based on ref if: ${{ !inputs.version }} run: | mkdir -p cpr/build pushd cpr/build cmake .. -DCPR_BUILD_VERSION_OUTPUT_ONLY=ON echo "RELEASE_VERSION=$(cat version.txt)" >> $GITHUB_ENV popd rm -rf cpr/build - name: Print Version run: echo "deb version will be '${{ env.RELEASE_VERSION }}'" # Build package of runtime library - name: "Package build of runtime library" env: VERSION: ${{ env.RELEASE_VERSION }} run: bash cpr/package-build/build-package.sh cpr - name: "Upload deb-packages" uses: actions/upload-artifact@v4 with: name: artifact-deb path: ./*.deb cpr-1.11.2/.github/workflows/build-nuget.yml000066400000000000000000000120511475657722200207370ustar00rootroot00000000000000name: Build NuGet Package on: push: tags: [ '[0-9]+.[0-9]+.[0-9]+' ] workflow_dispatch: inputs: version: description: 'The optional semantic version number. If not supplied the branch/tag will be used.' type: string no_publish: description: 'Prevent publishing the NuGet package. Just build it and then upload it as an artifact.' type: boolean default: false jobs: package-windows-latest: runs-on: windows-2019 steps: - name: Set version based on input if: ${{ inputs.version }} run: echo "RELEASE_VERSION=${{ inputs.version }}" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - name: Set version based on ref if: ${{ !inputs.version }} run: echo "RELEASE_VERSION=$($env:GITHUB_REF -replace 'refs/.*/', '')" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - name: Print Version run: echo "NuGet version will be '${{ env.RELEASE_VERSION }}'" - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Setup NuGet.exe uses: nuget/setup-nuget@v2 - name: "[Release_x86] Build & Install" env: CMAKE_GENERATOR: "Visual Studio 16 2019" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} build-type: Release target: ALL_BUILD run-test: false configure-options: -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -A Win32 install-build: true install-options: --prefix ${{github.workspace}}\install --config Release - name: "[Release_x86] Copy install files for Release_x86" run: xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\x86\Release && xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\Win32\Release - name: "[Release_x86] Clean build" run: rm -r -fo ${{github.workspace}}/build - name: "[Debug_x86] Build & Install" env: CMAKE_GENERATOR: "Visual Studio 16 2019" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} build-type: Debug target: ALL_BUILD run-test: false configure-options: -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -A Win32 install-build: true install-options: --prefix ${{github.workspace}}\install --config Debug - name: "[Debug_x86] Copy install files for Debug_x86" run: xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\x86\Debug && xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\Win32\Debug - name: "[Debug_x86] Clean build" run: rm -r -fo ${{github.workspace}}/build - name: "[Release_x64] Build & Install" env: CMAKE_GENERATOR: "Visual Studio 16 2019" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} build-type: Release target: ALL_BUILD run-test: false configure-options: -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -A x64 install-build: true install-options: --prefix ${{github.workspace}}\install --config Release - name: "[Release_x64] Copy install files for Release_x64" run: xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\x64\Release - name: "[Release_x64] Clean build" run: rm -r -fo ${{github.workspace}}/build - name: "[Debug_x64] Build & Install" env: CMAKE_GENERATOR: "Visual Studio 16 2019" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} build-type: Debug target: ALL_BUILD run-test: false configure-options: -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -A x64 install-build: true install-options: --prefix ${{github.workspace}}\install --config Debug - name: "[Debug_x64] Copy install files for Debug_x64" run: xcopy /e /i /y ${{github.workspace}}\install ${{github.workspace}}\nuget\build\native\x64\Debug - name: "Copy Readme.md" run: xcopy /y ${{github.workspace}}\README.md ${{github.workspace}}\nuget - name: "Create NuGet package" env: VERSION: ${{ env.RELEASE_VERSION }} COMMIT_HASH: ${{ github.sha }} run: nuget pack ${{github.workspace}}\nuget\libcpr.nuspec -OutputDirectory ${{github.workspace}} -Properties "VERSION=$ENV:VERSION;COMMIT_HASH=$ENV:COMMIT_HASH" - name: "Upload artifact" uses: actions/upload-artifact@v4 with: name: artifact-nuget path: ${{github.workspace}}\*.nupkg - name: "Publish package to NuGet.org" if: ${{ !inputs.no_publish }} env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} run: nuget push ${{github.workspace}}\*.nupkg $ENV:NUGET_API_KEY -Source https://api.nuget.org/v3/index.json cpr-1.11.2/.github/workflows/ci.yml000066400000000000000000000251771475657722200171300ustar00rootroot00000000000000name: CI on: [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. env: # Enable verbose output. # Repeat up to 5 times to deal with flaky tests. CTEST_OPTIONS: "--repeat until-pass:5 --output-on-failure" # The OpenSSL path for CI runs. Found via 'brew info openssl'. MACOS_OPENSSL_ROOT_DIR: "/opt/homebrew/Cellar/openssl@3/3.3.0" jobs: ubuntu-clang-openssl: strategy: matrix: container: ["ubuntu:20.04", "ubuntu:24.04", "ubuntu:latest"] systemCurl: [ON, OFF] buildType: [Debug, Release] runs-on: ubuntu-latest container: ${{ matrix.container }} steps: - name: Update package list run: apt update - name: Install Dependencies run: apt install -y git libssl-dev cmake build-essential clang libcurl4-openssl-dev env: DEBIAN_FRONTEND: noninteractive - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.14 with: cmake-version: '3.22.x' - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON USE_SYSTEM_CURL: ${{ matrix.systemCurl }} uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} ubuntu-gcc-openssl: strategy: matrix: container: ["ubuntu:20.04", "ubuntu:24.04", "ubuntu:latest"] systemCurl: [ON, OFF] buildType: [Debug, Release] runs-on: ubuntu-latest container: ${{ matrix.container }} steps: - name: Update package list run: apt update - name: Install Dependencies run: apt install -y git libssl-dev cmake build-essential libcurl4-openssl-dev env: DEBIAN_FRONTEND: noninteractive - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.14 with: cmake-version: '3.22.x' - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON USE_SYSTEM_CURL: ${{ matrix.systemCurl }} uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: gcc cxx: g++ build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} ubuntu-gcc-mbedtls: runs-on: ubuntu-latest steps: - name: Update package list run: sudo apt update - name: Install Dependencies run: sudo apt install -y git libssl-dev libmbedtls-dev cmake build-essential env: DEBIAN_FRONTEND: noninteractive - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.14 with: cmake-version: '3.22.x' - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_MBEDTLS_BACKEND: ON uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: gcc cxx: g++ build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} fedora-clang-openssl: strategy: matrix: container: ["fedora:latest"] runs-on: ubuntu-latest container: ${{ matrix.container }} steps: - name: Update package list run: dnf update -y - name: Install Dependencies run: dnf install -y gcc clang git gcc gdb make openssl-devel libcurl-devel cmake - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON USE_SYSTEM_CURL: OFF uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} fedora-gcc-openssl: strategy: matrix: container: ["fedora:latest"] systemCurl: [ON, OFF] buildType: [Debug, Release] runs-on: ubuntu-latest container: ${{ matrix.container }} steps: - name: Update package list run: dnf update -y - name: Install Dependencies run: dnf install -y gcc clang git gcc gdb make openssl-devel libcurl-devel cmake - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON USE_SYSTEM_CURL: ${{ matrix.systemCurl }} uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: gcc cxx: g++ build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} fedora-gcc-ssl-sanitizer: strategy: matrix: buildType: [UdefSan, LeakSan, AddrSan] # ThreadSan is disabled for now until all problems are resolved: https://github.com/libcpr/cpr/issues/451 runs-on: ubuntu-latest container: "fedora:latest" # Use fedora for an up to date version of all sanitizers steps: - name: Update package list run: dnf update -y - name: Install Dependencies run: dnf install -y gcc clang git gcc gdb make openssl-devel libasan libubsan liblsan libtsan cmake - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: gcc cxx: g++ build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} windows-msvc-ssl: strategy: matrix: buildType: [Debug, Release] runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CMAKE_GENERATOR: "Visual Studio 17 2022" CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: OFF uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} windows-msvc-openssl: runs-on: windows-latest steps: - name: Install OpenSSL run: choco install openssl -y - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CMAKE_GENERATOR: "Visual Studio 17 2022" CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} macos-clang-ssl: strategy: matrix: buildType: [Debug, Release] runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: OFF uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: ${{ matrix.buildType }} run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} macos-clang-darwinssl: runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: OFF CPR_FORCE_DARWINSSL_BACKEND: ON uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} macos-clang-openssl: runs-on: macos-latest steps: - name: Install OpenSSL run: brew install openssl - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON OPENSSL_ROOT_DIR: "${{ env.MACOS_OPENSSL_ROOT_DIR }}" OPENSSL_LIBRARIES: "${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib" LDFLAGS: "-L${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib" CPPFLAGS: "-I${{ env.MACOS_OPENSSL_ROOT_DIR }}/include" PKG_CONFIG_PATH: "${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib/pkgconfig" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }} macos-clang-openssl-boost: runs-on: macos-latest steps: - name: Install Boost run: brew install boost - name: Install OpenSSL run: brew install openssl - name: Checkout uses: actions/checkout@v3 - name: "Build & Test" env: CPR_BUILD_TESTS: ON CPR_BUILD_TESTS_SSL: ON CPR_FORCE_OPENSSL_BACKEND: ON CPR_USE_BOOST_FILESYSTEM: ON OPENSSL_ROOT_DIR: "${{ env.MACOS_OPENSSL_ROOT_DIR }}" OPENSSL_LIBRARIES: "${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib" LDFLAGS: "-L${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib" CPPFLAGS: "-I${{ env.MACOS_OPENSSL_ROOT_DIR }}/include" PKG_CONFIG_PATH: "${{ env.MACOS_OPENSSL_ROOT_DIR }}/lib/pkgconfig" uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{ github.workspace }}/build source-dir: ${{ github.workspace }} cc: clang cxx: clang++ build-type: Release run-test: true ctest-options: ${{ env.CTEST_OPTIONS }}cpr-1.11.2/.github/workflows/clang-format.yml000066400000000000000000000012431475657722200210730ustar00rootroot00000000000000name: "Test Clang Format" on: [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. jobs: clang-format: runs-on: ubuntu-latest container: fedora:latest steps: - name: Update package list run: sudo dnf update -y - name: Install clang-format run: sudo dnf install -y clang-tools-extra - name: Checkout uses: actions/checkout@v3 - name: Check format run: bash scripts/check_clang_format.shcpr-1.11.2/.github/workflows/clang-tidy.yml000066400000000000000000000020731475657722200205560ustar00rootroot00000000000000name: "Test Clang Tidy" on: [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. jobs: clang-tidy: runs-on: ubuntu-latest container: fedora:latest steps: - name: Update package list run: sudo dnf update -y - name: Install dependencies run: sudo dnf install -y openssl-devel cmake git gcc clang ninja-build - name: Install clang-tidy run: sudo dnf install -y clang-tools-extra - name: Checkout uses: actions/checkout@v3 - name: "[Release g++] Build & Test" env: CPR_BUILD_TESTS: ON uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} cc: clang cxx: clang++ build-type: Release run-test: false configure-options: -DCPR_ENABLE_LINTING=ONcpr-1.11.2/.github/workflows/cppcheck.yml000066400000000000000000000020461475657722200203030ustar00rootroot00000000000000name: "Test cppcheck" on: [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. jobs: cppcheck: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Update package list run: sudo apt update - name: Install cppcheck run: sudo apt install cppcheck - name: "[Release g++] Build" env: CPR_ENABLE_CPPCHECK: ON # Avoid parallel runs so only the resulting error file is not being written by multiple processes at the same time. CMAKE_BUILD_PARALLEL_LEVEL: 1 uses: ashutoshvarma/action-cmake-build@master with: build-dir: ${{github.workspace}}/build source-dir: ${{github.workspace}} cc: gcc cxx: g++ build-type: Release run-test: falsecpr-1.11.2/.github/workflows/readme-updater.yml000066400000000000000000000012241475657722200214170ustar00rootroot00000000000000name: Update GIT_TAG In Readme on: release: types: [published] # Workflow configuration env: OUTPUT_BRANCH: "master" COMMIT_MESSAGE: "Update GIT_TAG on new release" jobs: update-git-tag: permissions: contents: write runs-on: ubuntu-latest steps: - name: "Checkout" uses: actions/checkout@v3 with: ref: ${{github.sha}} - name: "Replace GIT_TAG" run: sed -i -re 's/(GIT_TAG) [0-9a-f]+/\1 ${{github.sha}}/' README.md - name: "Commit changes" uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: ${{env.COMMIT_MESSAGE}} branch: ${{env.OUTPUT_BRANCH}} cpr-1.11.2/.gitignore000066400000000000000000000011631475657722200143720ustar00rootroot00000000000000# Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # CMake CMakeCache.txt CMakeFiles Makefile cmake_install.cmake install_manifest.txt # Custom build/ !nuget/build # Jekyll stuff _includes/ _site/ # Vim .ycm_extra_conf.py* *.swp # VSCode .vscode/* !.vscode/tasks.json .vs/ !.vs/tasks.json # clangd .cache/ # compilation database # used in various editor configurations, such as vim & YcM compile_commands.json # macOS .DS_Store cpr-1.11.2/.vscode/000077500000000000000000000000001475657722200137425ustar00rootroot00000000000000cpr-1.11.2/.vscode/tasks.json000066400000000000000000000032221475657722200157610ustar00rootroot00000000000000{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "🗑️ Delete build dir", "type": "shell", "command": "${workspaceFolder}/scripts/delete_build_dir.sh", "problemMatcher": [], "group": { "kind": "build" }, "presentation": { "clear": true }, "options": { "cwd": "${workspaceFolder}" } }, { "label": "📝 Run clang-format", "type": "shell", "command": "${workspaceFolder}/scripts/run_clang_format.sh", "args": [ "cpr", "include", "test" ], "problemMatcher": [], "group": { "kind": "build" }, "presentation": { "clear": true }, "options": { "cwd": "${workspaceFolder}" } }, { "label": "📑 Check clang-format", "type": "shell", "command": "${workspaceFolder}/scripts/check_clang_format.sh", "args": [ "cpr", "include", "test" ], "problemMatcher": [], "group": { "kind": "build" }, "presentation": { "clear": true }, "options": { "cwd": "${workspaceFolder}" } } ] }cpr-1.11.2/CMakeLists.txt000066400000000000000000000501161475657722200151440ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) project(cpr VERSION 1.11.1 LANGUAGES CXX) math(EXPR cpr_VERSION_NUM "${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL) configure_file("${cpr_SOURCE_DIR}/cmake/cprver.h.in" "${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h") # Only change the folder behaviour if cpr is not a subproject if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json to be used by other tools (e.g. vs code) else() # Check required c++ standard of parent project if(CMAKE_CXX_STANDARD) set(PARENT_CXX_STANDARD ${CMAKE_CXX_STANDARD}) message(STATUS "CXX standard of parent project: ${PARENT_CXX_STANDARD}") endif() endif() # Avoid the dll boilerplate code for windows set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) if (PARENT_CXX_STANDARD) # Don't set CMAKE_CXX_STANDARD if it is already set by parent project if (PARENT_CXX_STANDARD LESS 17) message(FATAL_ERROR "cpr ${cpr_VERSION} does not support ${PARENT_CXX_STANDARD}. Please use cpr <= 1.9.x") endif() else() # Set standard version if not already set by potential parent project set(CMAKE_CXX_STANDARD 17) endif() message(STATUS "CXX standard: ${CMAKE_CXX_STANDARD}") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CPR_LIBRARIES cpr CACHE INTERNAL "") macro(cpr_option OPTION_NAME OPTION_TEXT OPTION_DEFAULT) option(${OPTION_NAME} ${OPTION_TEXT} ${OPTION_DEFAULT}) if(DEFINED ENV{${OPTION_NAME}}) # Allow overriding the option through an environment variable set(${OPTION_NAME} $ENV{${OPTION_NAME}}) endif() if(${OPTION_NAME}) add_definitions(-D${OPTION_NAME}) endif() message(STATUS " ${OPTION_NAME}: ${${OPTION_NAME}}") endmacro() message(STATUS "C++ Requests CMake Options") message(STATUS "=======================================================") cpr_option(CPR_GENERATE_COVERAGE "Set to ON to generate coverage reports." OFF) cpr_option(CPR_CURL_NOSIGNAL "Set to ON to disable use of signals in libcurl." OFF) cpr_option(CURL_VERBOSE_LOGGING "Curl verbose logging during building curl" OFF) cpr_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) cpr_option(CPR_USE_SYSTEM_CURL "If enabled we will use the curl lib already installed on this system." OFF) cpr_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) cpr_option(CPR_ENABLE_SSL "Enables or disables the SSL backend. Required to perform HTTPS requests." ON) cpr_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) cpr_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) cpr_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) cpr_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) cpr_option(CPR_ENABLE_LINTING "Set to ON to enable clang linting." OFF) cpr_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) cpr_option(CPR_BUILD_TESTS "Set to ON to build cpr tests." OFF) cpr_option(CPR_BUILD_TESTS_SSL "Set to ON to build cpr ssl tests" ${CPR_BUILD_TESTS}) cpr_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) cpr_option(CPR_BUILD_VERSION_OUTPUT_ONLY "Set to ON to only export the version into 'build/version.txt' and exit" OFF) cpr_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) cpr_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) cpr_option(CPR_DEBUG_SANITIZER_FLAG_THREAD "Enables the ThreadSanitizer for debug builds." OFF) cpr_option(CPR_DEBUG_SANITIZER_FLAG_ADDR "Enables the AddressSanitizer for debug builds." OFF) cpr_option(CPR_DEBUG_SANITIZER_FLAG_LEAK "Enables the LeakSanitizer for debug builds." OFF) cpr_option(CPR_DEBUG_SANITIZER_FLAG_UB "Enables the UndefinedBehaviorSanitizer for debug builds." OFF) cpr_option(CPR_DEBUG_SANITIZER_FLAG_ALL "Enables all sanitizers for debug builds except the ThreadSanitizer since it is incompatible with the other sanitizers." OFF) message(STATUS "=======================================================") if (MSVC) if (BUILD_SHARED_LIBS) message(STATUS "Build windows dynamic libs.") else() # Add this to build windows pure static library. set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") message(STATUS "Build windows static libs.") endif() endif() # Save the project version as txt file for deb and NuGet builds if(CPR_BUILD_VERSION_OUTPUT_ONLY) message(STATUS "Printing version and exiting...") file(WRITE "${CMAKE_BINARY_DIR}/version.txt" "${PROJECT_VERSION}") return() endif() if (CPR_FORCE_USE_SYSTEM_CURL) message(WARNING "The variable CPR_FORCE_USE_SYSTEM_CURL is deprecated, please use CPR_USE_SYSTEM_CURL instead") set(CPR_USE_SYSTEM_CURL ${CPR_FORCE_USE_SYSTEM_CURL}) endif() include(GNUInstallDirs) include(FetchContent) include(cmake/code_coverage.cmake) include(cmake/sanitizer.cmake) include(cmake/clear_variable.cmake) # So CMake can find FindMbedTLS.cmake set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") # Linting if(CPR_ENABLE_LINTING) include(cmake/clang-tidy.cmake) endif() # Cppcheck if(CPR_ENABLE_CPPCHECK) if(CPR_BUILD_TESTS OR CPR_BUILD_TESTS_SSL) 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. ") endif() include(cmake/cppcheck.cmake) endif() # SSL if(CPR_ENABLE_SSL) if(CPR_FORCE_OPENSSL_BACKEND OR CPR_FORCE_WINSSL_BACKEND OR CPR_FORCE_DARWINSSL_BACKEND OR CPR_FORCE_MBEDTLS_BACKEND) 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.") set(DETECT_SSL_BACKEND OFF CACHE INTERNAL "" FORCE) else() message(STATUS "Automatically detecting SSL backend.") set(DETECT_SSL_BACKEND ON CACHE INTERNAL "" FORCE) endif() if(CPR_FORCE_WINSSL_BACKEND AND (NOT WIN32)) message(FATAL_ERROR "WinSSL is only available on Windows! Use either OpenSSL (CPR_FORCE_OPENSSL_BACKEND) or DarwinSSL (CPR_FORCE_DARWINSSL_BACKEND) instead.") endif() if(DETECT_SSL_BACKEND) message(STATUS "Detecting SSL backend...") if(WIN32) message(STATUS "SSL auto detect: Using WinSSL.") set(SSL_BACKEND_USED "WinSSL") elseif(APPLE) message(STATUS "SSL auto detect: Using DarwinSSL.") set(CPR_BUILD_TESTS_SSL OFF) set(SSL_BACKEND_USED "DarwinSSL") else() find_package(OpenSSL) if(OPENSSL_FOUND) message(STATUS "SSL auto detect: Using OpenSSL.") set(SSL_BACKEND_USED "OpenSSL") else() find_package(MbedTLS) if(MBEDTLS_FOUND) set(SSL_BACKEND_USED "MbedTLS") else() message(FATAL_ERROR "No valid SSL backend found! Please install OpenSSL, Mbed TLS or disable SSL by setting CPR_ENABLE_SSL to OFF.") endif() endif() endif() else() if(CPR_FORCE_OPENSSL_BACKEND) find_package(OpenSSL) if(OPENSSL_FOUND) message(STATUS "Using OpenSSL.") set(SSL_BACKEND_USED "OpenSSL") else() message(FATAL_ERROR "CPR_FORCE_OPENSSL_BACKEND enabled but we were not able to find OpenSSL!") endif() elseif(CPR_FORCE_WINSSL_BACKEND) message(STATUS "Using WinSSL.") set(SSL_BACKEND_USED "WinSSL") elseif(CPR_FORCE_DARWINSSL_BACKEND) message(STATUS "Using DarwinSSL.") set(CPR_BUILD_TESTS_SSL OFF) set(SSL_BACKEND_USED "DarwinSSL") elseif(CPR_FORCE_MBEDTLS_BACKEND) message(STATUS "Using Mbed TLS.") set(CPR_BUILD_TESTS_SSL OFF) set(SSL_BACKEND_USED "MbedTLS") endif() endif() endif() if(SSL_BACKEND_USED STREQUAL "OpenSSL") # Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly find_package(OpenSSL REQUIRED) add_compile_definitions(OPENSSL_BACKEND_USED) endif() # Curl configuration if(CPR_USE_SYSTEM_CURL) if(CPR_ENABLE_SSL) find_package(CURL COMPONENTS HTTP HTTPS) if(CURL_FOUND) message(STATUS "Curl ${CURL_VERSION_STRING} found on this system.") # To be able to load certificates under Windows when using OpenSSL: if(CMAKE_USE_OPENSSL AND WIN32 AND (NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.71.0"))) 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.") endif() else() find_package(CURL COMPONENTS HTTP) if(CURL_FOUND) 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.") else() message(FATAL_ERROR "Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.") endif() endif() else() find_package(CURL COMPONENTS HTTP) if(CURL_FOUND) message(STATUS "Curl found on this system.") else() message(FATAL_ERROR "Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.") endif() endif() # Check for the minimum supported curl version if(NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.64.0")) 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.") endif() else() message(STATUS "Configuring built-in curl...") # ZLIB is optional for curl # to disable it: # * from command line: # -DCURL_ZLIB=OFF # * from CMake script: if (CURL_ZLIB OR CURL_ZLIB STREQUAL AUTO OR NOT DEFINED CACHE{CURL_ZLIB}) include(cmake/zlib_external.cmake) endif() if (CPR_ENABLE_CURL_HTTP_ONLY) # We only need HTTP (and HTTPS) support: set(HTTP_ONLY ON CACHE INTERNAL "" FORCE) endif() set(BUILD_CURL_EXE OFF CACHE INTERNAL "" FORCE) set(BUILD_TESTING OFF) if (CURL_VERBOSE_LOGGING) message(STATUS "Enabled curl debug features") set(ENABLE_DEBUG ON CACHE INTERNAL "" FORCE) endif() if (CPR_ENABLE_SSL) set(CURL_ENABLE_SSL ON CACHE INTERNAL "" FORCE) if(ANDROID) set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE INTERNAL "") elseif(CPR_SKIP_CA_BUNDLE_SEARCH) set(CURL_CA_PATH "none" CACHE INTERNAL "") else() set(CURL_CA_PATH "auto" CACHE INTERNAL "") endif() if(CPR_SKIP_CA_BUNDLE_SEARCH) set(CURL_CA_BUNDLE "none" CACHE INTERNAL "") elseif(NOT DEFINED CURL_CA_BUNDLE) set(CURL_CA_BUNDLE "auto" CACHE INTERNAL "") endif() if(SSL_BACKEND_USED STREQUAL "WinSSL") set(CURL_USE_SCHANNEL ON CACHE INTERNAL "" FORCE) set(CURL_WINDOWS_SSPI ON CACHE INTERNAL "" FORCE) endif() if(SSL_BACKEND_USED STREQUAL "OpenSSL") set(CURL_USE_OPENSSL ON CACHE INTERNAL "" FORCE) endif() if(SSL_BACKEND_USED STREQUAL "DarwinSSL") set(CURL_USE_SECTRANSP ON CACHE INTERNAL "" FORCE) endif() if(SSL_BACKEND_USED STREQUAL "MbedTLS") set(CURL_USE_MBEDTLS ON CACHE INTERNAL "" FORCE) endif() message(STATUS "Enabled curl SSL") else() set(CURL_ENABLE_SSL OFF CACHE INTERNAL "" FORCE) set(CURL_CA_PATH "none" CACHE INTERNAL "" FORCE) set(CURL_USE_SCHANNEL OFF CACHE INTERNAL "" FORCE) set(CURL_WINDOWS_SSPI OFF CACHE INTERNAL "" FORCE) set(CURL_USE_OPENSSL OFF CACHE INTERNAL "" FORCE) set(CURL_USE_SECTRANSP OFF CACHE INTERNAL "" FORCE) set(CURL_USE_MBEDTLS OFF CACHE INTERNAL "" FORCE) message(STATUS "Disabled curl SSL") endif() # Disable linting for curl clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() FetchContent_Declare(curl URL https://github.com/curl/curl/releases/download/curl-8_10_1/curl-8.10.1.tar.xz URL_HASH SHA256=73a4b0e99596a09fa5924a4fb7e4b995a85fda0d18a2c02ab9cf134bebce04ee # the file hash for curl-8.10.1.tar.xz USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress FetchContent_MakeAvailable(curl) restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) endif() # Depending on which version of libcurl we are using the CMake target is called differently if(TARGET libcurl) # Old curl CMake target name set(CURL_LIB libcurl) else() # New curl CMake target name set(CURL_LIB CURL::libcurl) endif() # GTest configuration if(CPR_BUILD_TESTS) if(CPR_USE_SYSTEM_GTEST) find_package(GTest) endif() if(NOT CPR_USE_SYSTEM_GTEST OR NOT GTEST_FOUND) message(STATUS "Not using system gtest, using built-in googletest project instead.") if(MSVC) # By default, GTest compiles on Windows in CRT static linkage mode. We use this # variable to force it into using the CRT in dynamic linkage (DLL), just as CPR # does. set(gtest_force_shared_crt ON CACHE BOOL "Force gtest to use the shared c runtime") endif() # Disable linting for google test clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 # the file hash for release-1.14.0.tar.gz USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress FetchContent_MakeAvailable(googletest) restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) add_library(gtest_int INTERFACE) target_link_libraries(gtest_int INTERFACE gtest) target_include_directories(gtest_int INTERFACE ${googletest_SOURCE_DIR}/include) add_library(GTest::GTest ALIAS gtest_int) # Group under the "tests/gtest" project folder in IDEs such as Visual Studio. set_property(TARGET gtest PROPERTY FOLDER "tests/gtest") set_property(TARGET gtest_main PROPERTY FOLDER "tests/gtest") endif() endif() # Mongoose configuration if(CPR_BUILD_TESTS) message(STATUS "Building mongoose project for test support.") if(CPR_BUILD_TESTS_SSL) if(NOT CPR_ENABLE_SSL) 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.") endif() if(NOT(SSL_BACKEND_USED STREQUAL "OpenSSL")) 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.") endif() set(ENABLE_SSL_TESTS ON CACHE INTERNAL "") else() set(ENABLE_SSL_TESTS OFF CACHE INTERNAL "") endif() # Disable linting for mongoose clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) FetchContent_Declare(mongoose URL https://github.com/cesanta/mongoose/archive/7.7.tar.gz URL_HASH SHA256=4e5733dae31c3a81156af63ca9aa3a6b9b736547f21f23c3ab2f8e3f1ecc16c0 # the hash for 7.7.tar.gz USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress # We can not use FetchContent_MakeAvailable, since we need to patch mongoose to use CMake if (NOT mongoose_POPULATED) FetchContent_POPULATE(mongoose) file(INSTALL cmake/mongoose.CMakeLists.txt DESTINATION ${mongoose_SOURCE_DIR}) file(RENAME ${mongoose_SOURCE_DIR}/mongoose.CMakeLists.txt ${mongoose_SOURCE_DIR}/CMakeLists.txt) add_subdirectory(${mongoose_SOURCE_DIR} ${mongoose_BINARY_DIR}) endif() # Group under the "external" project folder in IDEs such as Visual Studio. set_property(TARGET mongoose PROPERTY FOLDER "external") restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # Disable C++98 compatibility support in clang: https://github.com/libcpr/cpr/issues/927 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") endif() endif() add_subdirectory(cpr) add_subdirectory(include) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND CPR_BUILD_TESTS) # Disable linting for tests since they are currently not up to the standard clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) enable_testing() add_subdirectory(test) restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) endif() cpr-1.11.2/CODE_OF_CONDUCT.md000066400000000000000000000121371475657722200152040ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at cc@libcpr.org. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. cpr-1.11.2/CONTRIBUTING.md000066400000000000000000000023741475657722200146400ustar00rootroot00000000000000# Contributing to C++ Requests Please 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. ## Formatting To 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: ``` apt-get install clang-format-3.8 ``` To install `clang-format` on OS X, run this: ``` brew install clang-format ``` Note that `brew` might install a later version of `clang-format`, but it should be mostly compatible with what's run on the Travis servers. To run `clang-format` on every source file, run this in the root directory: ``` ./scripts/run_clang_format.sh cpr include/cpr ``` This 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). cpr-1.11.2/LICENSE000066400000000000000000000023151475657722200134070ustar00rootroot00000000000000This license applies to everything except the contents of the "test" directory and its subdirectories. MIT License Copyright (c) 2017-2021 Huu Nguyen Copyright (c) 2022 libcpr and many other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.cpr-1.11.2/README.md000066400000000000000000000217271475657722200136710ustar00rootroot00000000000000# C++ Requests: Curl for People [![Documentation](https://img.shields.io/badge/docs-online-informational?style=flat&link=https://docs.libcpr.org/)](https://docs.libcpr.org/) ![CI](https://github.com/libcpr/cpr/workflows/CI/badge.svg) [![Gitter](https://badges.gitter.im/libcpr/community.svg)](https://gitter.im/libcpr/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ## Announcements * This project is being maintained by [Fabian Sauter](https://github.com/com8) and [Kilian Traub](https://github.com/KingKili). * 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. ## Supported Releases | Release | Min. C++ Standard | Status | Notes | |----------|-------------------|--------|-------| | master | `cpp17` | ![alt text][preview] | | | 1.11.x | `cpp17` | ![alt text][supported] | | | 1.10.x | `cpp17` | ![alt text][unsupported] | | | <= 1.9.x | `cpp11` | ![alt text][unsupported] | | [unsupported]: https://img.shields.io/badge/-unsupported-red "unsupported" [supported]: https://img.shields.io/badge/-supported-green "supported" [preview]: https://img.shields.io/badge/-preview-orange "preview" ## TLDR C++ Requests is a simple wrapper around [libcurl](http://curl.haxx.se/libcurl) inspired by the excellent [Python Requests](https://github.com/kennethreitz/requests) project. Despite 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. Here's a quick GET request: ```c++ #include int main(int argc, char** argv) { cpr::Response r = cpr::Get(cpr::Url{"https://api.github.com/repos/whoshuu/cpr/contributors"}, cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC}, cpr::Parameters{{"anon", "true"}, {"key", "value"}}); r.status_code; // 200 r.header["content-type"]; // application/json; charset=utf-8 r.text; // JSON text string return 0; } ``` And here's [less functional, more complicated code, without cpr](https://gist.github.com/whoshuu/2dc858b8730079602044). ## Documentation [![Documentation](https://img.shields.io/badge/docs-online-informational?style=for-the-badge&link=https://docs.libcpr.org/)](https://docs.libcpr.org/) You can find the latest documentation [here](https://docs.libcpr.org/). 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. ## Features C++ Requests currently supports: * Custom headers * URL-encoded parameters * URL-encoded POST values * Multipart form POST upload * File POST upload * Basic authentication * Bearer authentication * Digest authentication * NTLM authentication * Connection and request timeout specification * Timeout for low speed connection * Asynchronous requests * :cookie: support! * Proxy support * Callback interfaces * PUT methods * DELETE methods * HEAD methods * OPTIONS methods * PATCH methods * Thread Safe access to [libCurl](https://curl.haxx.se/libcurl/c/threadsafe.html) * OpenSSL and WinSSL support for HTTPS requests ## Planned For a quick overview about the planned features, have a look at the next [Milestones](https://github.com/libcpr/cpr/milestones). ## Usage ### CMake #### fetch_content: If you already have a CMake project you need to integrate C++ Requests with, the primary way is to use `fetch_content`. Add the following to your `CMakeLists.txt`. ```cmake include(FetchContent) FetchContent_Declare(cpr GIT_REPOSITORY https://github.com/libcpr/cpr.git GIT_TAG dec9422db3af470641f8b0d90e4b451c4daebf64) # Replace with your desired git commit from: https://github.com/libcpr/cpr/releases FetchContent_MakeAvailable(cpr) ``` This will produce the target `cpr::cpr` which you can link against the typical way: ```cmake target_link_libraries(your_target_name PRIVATE cpr::cpr) ``` That should do it! There's no need to handle `libcurl` yourself. All dependencies are taken care of for you. All of this can be found in an example [**here**](https://github.com/libcpr/example-cmake-fetch-content). #### find_package(): If 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. **Note:** this feature is feasible only if CPR_USE_SYSTEM_CURL is set. (see [#645](https://github.com/libcpr/cpr/pull/645)) ```Bash git clone https://github.com/libcpr/cpr.git cd cpr && mkdir build && cd build cmake .. -DCPR_USE_SYSTEM_CURL=ON cmake --build . --parallel sudo cmake --install . ``` #### Build Static Library As 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). ```Bash git clone https://github.com/libcpr/cpr.git cd cpr && mkdir build && cd build cmake .. -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF cmake --build . --parallel sudo cmake --install . ``` In your `CMakeLists.txt`: ```cmake find_package(cpr REQUIRED) add_executable(your_target_name your_target_name.cpp) target_link_libraries(your_target_name PRIVATE cpr::cpr) ``` #### Tests `cpr` provides a bunch of tests that can be executed via the following commands. ```Bash git clone https://github.com/libcpr/cpr.git cd cpr && mkdir build && cd build cmake .. -DCPR_BUILD_TESTS=ON # There are other test related options like 'CPR_BUILD_TESTS_SSL' and 'CPR_BUILD_TESTS_PROXY' cmake --build . --parallel ctest -VV # -VV is optional since it enables verbose output ``` ### Bazel Please refer to [hedronvision/bazel-make-cc-https-easy](https://github.com/hedronvision/bazel-make-cc-https-easy). ### Packages for Linux Distributions Alternatively, 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. Currently, we are aware of packages for the following distributions: * [Arch Linux (AUR)](https://aur.archlinux.org/packages/cpr) * [Fedora Linux](https://src.fedoraproject.org/rpms/cpr) If 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. ### NuGet Package For Windows, there is also a libcpr NuGet package available. Currently, x86 and x64 builds are supported with release and debug configuration. The package can be found here: [NuGet.org](https://www.nuget.org/packages/libcpr/) ### Port for macOS On macOS you may install cpr via [MacPorts.org](https://ports.macports.org/port/cpr) (arm64, x86_64, powerpc) ### FreeBSD Port On FreeBSD, you can issue `pkg install cpr` or use the Ports tree to install it. ## Requirements The only explicit requirements are: * 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 * 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. * If you would like to perform https requests `OpenSSL` and its development libraries are required. * 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.64.0`. Lower versions are not supported. This means you need Debian `>= 10` or Ubuntu `>= 20.04 LTS`. ## Building cpr - Using vcpkg You can download and install cpr using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: ```Bash git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install cpr ``` The `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. ## Building cpr - Using Conan You 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). An example can be found [**here**](https://github.com/libcpr/example-cmake-conan). The `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. cpr-1.11.2/cmake/000077500000000000000000000000001475657722200134615ustar00rootroot00000000000000cpr-1.11.2/cmake/FindMbedTLS.cmake000066400000000000000000000011341475657722200165150ustar00rootroot00000000000000# Source: https://github.com/curl/curl/blob/curl-7_82_0/CMake/FindMbedTLS.cmake find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) find_library(MBEDTLS_LIBRARY mbedtls) find_library(MBEDX509_LIBRARY mbedx509) find_library(MBEDCRYPTO_LIBRARY mbedcrypto) set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) cpr-1.11.2/cmake/clang-tidy.cmake000066400000000000000000000007451475657722200165240ustar00rootroot00000000000000if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy) mark_as_advanced(CLANG_TIDY_EXECUTABLE) if (${CLANG_TIDY_EXECUTABLE}) message(FATAL_ERROR "Clang-tidy not found") else() message(STATUS "Enabling clang-tidy") set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*") endif() else() message(FATAL_ERROR "Clang-tidy is not supported when building for windows") endif() cpr-1.11.2/cmake/clear_variable.cmake000066400000000000000000000006451475657722200174230ustar00rootroot00000000000000macro(clear_variable) cmake_parse_arguments(CLEAR_VAR "" "DESTINATION;BACKUP;REPLACE" "" ${ARGN}) set(${CLEAR_VAR_BACKUP} ${${CLEAR_VAR_DESTINATION}}) set(${CLEAR_VAR_DESTINATION} ${CLEAR_VAR_REPLACE}) endmacro() macro(restore_variable) cmake_parse_arguments(CLEAR_VAR "" "DESTINATION;BACKUP" "" ${ARGN}) set(${CLEAR_VAR_DESTINATION} ${${CLEAR_VAR_BACKUP}}) unset(${CLEAR_VAR_BACKUP}) endmacro() cpr-1.11.2/cmake/code_coverage.cmake000066400000000000000000000021741475657722200172540ustar00rootroot00000000000000# Code coverage if(CPR_BUILD_TESTS AND CPR_GENERATE_COVERAGE) set(CMAKE_BUILD_TYPE COVERAGE CACHE INTERNAL "Coverage enabled build") message(STATUS "Enabling gcov support") if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(COVERAGE_FLAG "--coverage") endif() set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 ${COVERAGE_FLAG} -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE) set(CMAKE_C_FLAGS_COVERAGE "-g -O0 ${COVERAGE_FLAG} -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C compiler during coverage builds." FORCE) set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used for linking binaries during coverage builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE) mark_as_advanced( CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE) endif() cpr-1.11.2/cmake/cppcheck.cmake000066400000000000000000000007761475657722200162550ustar00rootroot00000000000000find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) if(CMAKE_CXX_CPPCHECK) list(APPEND CMAKE_CXX_CPPCHECK "--xml" "--error-exitcode=1" "--enable=warning,style" "--force" "--inline-suppr" "--addon=y2038" "--std=c++${CMAKE_CXX_STANDARD}" "--cppcheck-build-dir=${PROJECT_BINARY_DIR}" "--suppress-xml=${PROJECT_SOURCE_DIR}/cppcheck-suppressions.xml" "--output-file=${PROJECT_BINARY_DIR}/cppcheck.xml" "--check-level=normal") endif() cpr-1.11.2/cmake/cprConfig.cmake.in000066400000000000000000000002451475657722200170030ustar00rootroot00000000000000include(CMakeFindDependencyMacro) @PACKAGE_INIT@ find_dependency(CURL REQUIRED) include(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake) check_required_components(cpr)cpr-1.11.2/cmake/cprver.h.in000066400000000000000000000013621475657722200155420ustar00rootroot00000000000000#ifndef CPR_CPRVER_H #define CPR_CPRVER_H /** * CPR version as a string. **/ #define CPR_VERSION "${cpr_VERSION}" /** * CPR version split up into parts. **/ #define CPR_VERSION_MAJOR ${cpr_VERSION_MAJOR} #define CPR_VERSION_MINOR ${cpr_VERSION_MINOR} #define CPR_VERSION_PATCH ${cpr_VERSION_PATCH} /** * CPR version as a single hex digit. * it can be split up into three parts: * 0xAABBCC * AA: The current CPR major version number in a hex format. * BB: The current CPR minor version number in a hex format. * CC: The current CPR patch version number in a hex format. * * Examples: * '0x010702' -> 01.07.02 -> CPR_VERSION: 1.7.2 * '0xA13722' -> A1.37.22 -> CPR_VERSION: 161.55.34 **/ #define CPR_VERSION_NUM ${cpr_VERSION_NUM} #endif cpr-1.11.2/cmake/mongoose.CMakeLists.txt000066400000000000000000000007401475657722200200470ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) project(mongoose C) add_library(mongoose STATIC mongoose.c) target_include_directories(mongoose PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) if(ENABLE_SSL_TESTS) # Enable mongoose SSL target_compile_definitions(mongoose PUBLIC MG_ENABLE_OPENSSL) target_link_libraries(mongoose PUBLIC OpenSSL::SSL) # Fix macOS and Windows invalid OpenSSL include path target_include_directories(mongoose PUBLIC "${OPENSSL_INCLUDE_DIR}") endif() cpr-1.11.2/cmake/sanitizer.cmake000066400000000000000000000116661475657722200165050ustar00rootroot00000000000000include(CheckCXXCompilerFlag) include(CheckCXXSourceRuns) set(ALL_SAN_FLAGS "") # No sanitizers when cross compiling to prevent stuff like this: https://github.com/whoshuu/cpr/issues/582 if(NOT CMAKE_CROSSCOMPILING) # Thread sanitizer set(THREAD_SAN_FLAGS "-fsanitize=thread") set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${THREAD_SAN_FLAGS}") check_cxx_source_runs("int main() { return 0; }" THREAD_SANITIZER_AVAILABLE) set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) # Do not add the ThreadSanitizer for builds with all sanitizers enabled because it is incompatible with other sanitizers. # Address sanitizer set(ADDR_SAN_FLAGS "-fsanitize=address") set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${ADDR_SAN_FLAGS}") check_cxx_source_runs("int main() { return 0; }" ADDRESS_SANITIZER_AVAILABLE) set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) if(ADDRESS_SANITIZER_AVAILABLE) set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${ADDR_SAN_FLAGS}") endif() # Leak sanitizer set(LEAK_SAN_FLAGS "-fsanitize=leak") check_cxx_compiler_flag(${LEAK_SAN_FLAGS} LEAK_SANITIZER_AVAILABLE) if(LEAK_SANITIZER_AVAILABLE) set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${LEAK_SAN_FLAGS}") endif() # Undefined behavior sanitizer set(UDEF_SAN_FLAGS "-fsanitize=undefined") check_cxx_compiler_flag(${UDEF_SAN_FLAGS} UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE) if(UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE) set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${UDEF_SAN_FLAGS}") endif() # All sanitizer (without thread sanitizer) if(NOT ALL_SAN_FLAGS STREQUAL "") set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${ALL_SAN_FLAGS}") check_cxx_source_runs("int main() { return 0; }" ALL_SANITIZERS_AVAILABLE) set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) endif() if(CPR_DEBUG_SANITIZER_FLAG_THREAD AND THREAD_SANITIZER_AVAILABLE) 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) 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) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during thread sanitizer builds" FORCE) elseif(CPR_DEBUG_SANITIZER_FLAG_ADDR AND ADDRESS_SANITIZER_AVAILABLE) 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) 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) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during address sanitizer builds" FORCE) elseif(CPR_DEBUG_SANITIZER_FLAG_LEAK AND LEAK_SANITIZER_AVAILABLE) 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) 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) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during leak sanitizer builds" FORCE) elseif(CPR_DEBUG_SANITIZER_FLAG_UB AND UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during undefined behaviour sanitizer builds." FORCE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during undefined behaviour sanitizer builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during undefined behaviour sanitizer builds" FORCE) elseif(CPR_DEBUG_SANITIZER_FLAG_ALL AND ALL_SANITIZERS_AVAILABLE) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ALL_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during most possible sanitizer builds." FORCE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ALL_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during most possible sanitizer builds." FORCE) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during most possible sanitizer builds" FORCE) endif() endif() cpr-1.11.2/cmake/std_fs_support_test.cpp000066400000000000000000000003471475657722200203060ustar00rootroot00000000000000#if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif int main() { auto cwd = fs::current_path(); } cpr-1.11.2/cmake/zlib_external.cmake000066400000000000000000000014441475657722200173300ustar00rootroot00000000000000# ZLIB # Fix Windows missing "zlib.dll": if(WIN32 AND (${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/$ CACHE INTERNAL "" FORCE) endif() set(ZLIB_COMPAT ON CACHE INTERNAL "" FORCE) set(ZLIB_ENABLE_TESTS OFF CACHE INTERNAL "" FORCE) FetchContent_Declare(zlib GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng GIT_TAG 2.1.3 USES_TERMINAL_DOWNLOAD TRUE) FetchContent_MakeAvailable(zlib) # Fix Windows zlib dll names from "zlibd1.dll" to "zlib.dll": if(WIN32 AND BUILD_SHARED_LIBS) set_target_properties(zlib PROPERTIES OUTPUT_NAME "zlib") set_target_properties(zlib PROPERTIES DEBUG_POSTFIX "") set_target_properties(zlib PROPERTIES SUFFIX ".dll") endif() cpr-1.11.2/cppcheck-suppressions.xml000066400000000000000000000017541475657722200174650ustar00rootroot00000000000000 * */build/* CheckLevelMaxBranches noExplicitConstructor knownConditionTrueFalse */include/cpr/async_wrapper.h y2038-unsafe-call */cpr/cookies.cpp y2038-unsafe-call */include/cpr/low_speed.h normalCheckLevelMaxBranches constParameterPointer */cpr/util.cpp postfixOperator cpr-1.11.2/cpr-config.cmake000066400000000000000000000016071475657722200154360ustar00rootroot00000000000000# - C++ Requests, Curl for People # This module is a libcurl wrapper written in modern C++. # It provides an easy, intuitive, and efficient interface to # a host of networking methods. # # Finding this module will define the following variables: # CPR_FOUND - True if the core library has been found # CPR_LIBRARIES - Path to the core library archive # CPR_INCLUDE_DIRS - Path to the include directories. Gives access # to cpr.h, which must be included in every # file that uses this interface find_path(CPR_INCLUDE_DIR NAMES cpr.h) find_library(CPR_LIBRARY NAMES cpr HINTS ${CPR_LIBRARY_ROOT}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CPR REQUIRED_VARS CPR_LIBRARY CPR_INCLUDE_DIR) if(CPR_FOUND) set(CPR_LIBRARIES ${CPR_LIBRARY}) set(CPR_INCLUDE_DIRS ${CPR_INCLUDE_DIR}) endif() cpr-1.11.2/cpr/000077500000000000000000000000001475657722200131655ustar00rootroot00000000000000cpr-1.11.2/cpr/CMakeLists.txt000066400000000000000000000047321475657722200157330ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) add_library(cpr accept_encoding.cpp async.cpp auth.cpp bearer.cpp callback.cpp cert_info.cpp cookies.cpp cprtypes.cpp curl_container.cpp curlholder.cpp error.cpp file.cpp multipart.cpp parameters.cpp payload.cpp proxies.cpp proxyauth.cpp session.cpp threadpool.cpp timeout.cpp unix_socket.cpp util.cpp response.cpp redirect.cpp interceptor.cpp ssl_ctx.cpp curlmultiholder.cpp multiperform.cpp) add_library(cpr::cpr ALIAS cpr) target_link_libraries(cpr PUBLIC ${CURL_LIB}) # todo should be private, but first dependencies in ssl_options need to be removed # Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly if(SSL_BACKEND_USED STREQUAL "OpenSSL") target_link_libraries(cpr PRIVATE OpenSSL::SSL) target_include_directories(cpr PRIVATE ${OPENSSL_INCLUDE_DIR}) endif() # Set version for shared libraries. set_target_properties(cpr PROPERTIES VERSION ${${PROJECT_NAME}_VERSION} SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}) # Import GNU common install directory variables include(GNUInstallDirs) install(TARGETS cpr EXPORT cprTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(CPR_USE_SYSTEM_CURL) # Include CMake helpers for package config files # Follow this installation guideline: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html include(CMakePackageConfigHelpers) write_basic_package_version_file( "${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake" VERSION ${${PROJECT_NAME}_VERSION} COMPATIBILITY ExactVersion) configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in "${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) install(FILES ${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake ${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) endif() install(EXPORT cprTargets FILE cprTargets.cmake NAMESPACE cpr:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) cpr-1.11.2/cpr/accept_encoding.cpp000066400000000000000000000025541475657722200170040ustar00rootroot00000000000000#include "cpr/accept_encoding.h" #include #include #include #include #include #include #include namespace cpr { AcceptEncoding::AcceptEncoding(const std::initializer_list& methods) { methods_.clear(); std::transform(methods.begin(), methods.end(), std::inserter(methods_, methods_.begin()), [&](cpr::AcceptEncodingMethods method) { return cpr::AcceptEncodingMethodsStringMap.at(method); }); } AcceptEncoding::AcceptEncoding(const std::initializer_list& string_methods) : methods_{string_methods} {} bool AcceptEncoding::empty() const noexcept { return methods_.empty(); } const std::string AcceptEncoding::getString() const { return std::accumulate(std::next(methods_.begin()), methods_.end(), *methods_.begin(), [](std::string a, std::string b) { return std::move(a) + ", " + std::move(b); }); } [[nodiscard]] bool AcceptEncoding::disabled() const { if (methods_.find(cpr::AcceptEncodingMethodsStringMap.at(AcceptEncodingMethods::disabled)) != methods_.end()) { if (methods_.size() != 1) { throw std::invalid_argument("AcceptEncoding does not accept any other values if 'disabled' is present. You set the following encodings: " + getString()); } return true; } return false; } } // namespace cpr cpr-1.11.2/cpr/async.cpp000066400000000000000000000002511475657722200150040ustar00rootroot00000000000000#include "cpr/async.h" namespace cpr { // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) CPR_SINGLETON_IMPL(GlobalThreadPool) } // namespace cpr cpr-1.11.2/cpr/auth.cpp000066400000000000000000000012221475657722200146270ustar00rootroot00000000000000#include "cpr/auth.h" #include "cpr/util.h" #include namespace cpr { Authentication::Authentication(std::string_view username, std::string_view password, AuthMode auth_mode) : auth_mode_{auth_mode} { auth_string_.reserve(username.size() + 1 + password.size()); auth_string_ += username; auth_string_ += ':'; auth_string_ += password; } Authentication::~Authentication() noexcept { util::secureStringClear(auth_string_); } const char* Authentication::GetAuthString() const noexcept { return auth_string_.c_str(); } AuthMode Authentication::GetAuthMode() const noexcept { return auth_mode_; } } // namespace cpr cpr-1.11.2/cpr/bearer.cpp000066400000000000000000000006401475657722200151310ustar00rootroot00000000000000#include "cpr/bearer.h" #include "cpr/util.h" #include namespace cpr { // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 Bearer::~Bearer() noexcept { util::secureStringClear(token_string_); } const char* Bearer::GetToken() const noexcept { return token_string_.c_str(); } #endif } // namespace cpr cpr-1.11.2/cpr/callback.cpp000066400000000000000000000010231475657722200154210ustar00rootroot00000000000000#include "cpr/callback.h" #include "cpr/cprtypes.h" #include namespace cpr { void CancellationCallback::SetProgressCallback(ProgressCallback& u_cb) { user_cb.emplace(std::reference_wrapper{u_cb}); } bool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const { const bool cont_operation{!cancellation_state->load()}; return user_cb ? (cont_operation && (*user_cb)(dltotal, dlnow, ultotal, ulnow)) : cont_operation; } } // namespace cpr cpr-1.11.2/cpr/cert_info.cpp000066400000000000000000000016011475657722200156370ustar00rootroot00000000000000#include "cpr/cert_info.h" #include #include namespace cpr { std::string& CertInfo::operator[](const size_t& pos) { return cert_info_[pos]; } CertInfo::iterator CertInfo::begin() { return cert_info_.begin(); } CertInfo::iterator CertInfo::end() { return cert_info_.end(); } CertInfo::const_iterator CertInfo::begin() const { return cert_info_.begin(); } CertInfo::const_iterator CertInfo::end() const { return cert_info_.end(); } CertInfo::const_iterator CertInfo::cbegin() const { return cert_info_.cbegin(); } CertInfo::const_iterator CertInfo::cend() const { return cert_info_.cend(); } void CertInfo::emplace_back(const std::string& str) { cert_info_.emplace_back(str); } void CertInfo::push_back(const std::string& str) { cert_info_.push_back(str); } void CertInfo::pop_back() { cert_info_.pop_back(); } } // namespace cpr cpr-1.11.2/cpr/cookies.cpp000066400000000000000000000053231475657722200153300ustar00rootroot00000000000000#include "cpr/cookies.h" #include "cpr/curlholder.h" #include #include #include #include #include namespace cpr { const std::string& Cookie::GetDomain() const { return domain_; } bool Cookie::IsIncludingSubdomains() const { return includeSubdomains_; } const std::string& Cookie::GetPath() const { return path_; } bool Cookie::IsHttpsOnly() const { return httpsOnly_; } std::chrono::system_clock::time_point Cookie::GetExpires() const { return expires_; } std::string Cookie::GetExpiresString() const { std::stringstream ss; std::tm tm{}; const std::time_t tt = std::chrono::system_clock::to_time_t(expires_); #ifdef _WIN32 gmtime_s(&tm, &tt); #else // NOLINTNEXTLINE(misc-include-cleaner,cert-err33-c) False positive since is included. Also ignore the ret value here. gmtime_r(&tt, &tm); #endif ss << std::put_time(&tm, "%a, %d %b %Y %H:%M:%S GMT"); return ss.str(); } const std::string& Cookie::GetName() const { return name_; } const std::string& Cookie::GetValue() const { return value_; } std::string Cookies::GetEncoded(const CurlHolder& holder) const { std::stringstream stream; for (const cpr::Cookie& item : cookies_) { // Depending on if encoding is set to "true", we will URL-encode cookies stream << (encode ? holder.urlEncode(item.GetName()) : item.GetName()) << "="; // special case version 1 cookies, which can be distinguished by // beginning and trailing quotes if (!item.GetValue().empty() && item.GetValue().front() == '"' && item.GetValue().back() == '"') { stream << item.GetValue(); } else { // Depending on if encoding is set to "true", we will URL-encode cookies stream << (encode ? holder.urlEncode(item.GetValue()) : item.GetValue()); } stream << "; "; } return stream.str(); } cpr::Cookie& Cookies::operator[](size_t pos) { return cookies_[pos]; } Cookies::iterator Cookies::begin() { return cookies_.begin(); } Cookies::iterator Cookies::end() { return cookies_.end(); } Cookies::const_iterator Cookies::begin() const { return cookies_.begin(); } Cookies::const_iterator Cookies::end() const { return cookies_.end(); } Cookies::const_iterator Cookies::cbegin() const { return cookies_.cbegin(); } Cookies::const_iterator Cookies::cend() const { return cookies_.cend(); } void Cookies::emplace_back(const Cookie& str) { cookies_.emplace_back(str); } bool Cookies::empty() const { return cookies_.empty(); } void Cookies::push_back(const Cookie& str) { cookies_.push_back(str); } void Cookies::pop_back() { cookies_.pop_back(); } } // namespace cpr cpr-1.11.2/cpr/cprtypes.cpp000066400000000000000000000006101475657722200155370ustar00rootroot00000000000000#include "cpr/cprtypes.h" #include #include #include namespace cpr { bool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const noexcept { 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); }); } } // namespace cpr cpr-1.11.2/cpr/curl_container.cpp000066400000000000000000000034661475657722200167110ustar00rootroot00000000000000#include "cpr/curl_container.h" #include "cpr/curlholder.h" #include #include #include #include namespace cpr { template CurlContainer::CurlContainer(const std::initializer_list& containerList) : containerList_(containerList) {} template void CurlContainer::Add(const std::initializer_list& containerList) { std::transform(containerList.begin(), containerList.end(), std::back_inserter(containerList_), [](const T& elem) { return std::move(elem); }); } template void CurlContainer::Add(const T& element) { containerList_.push_back(std::move(element)); } template <> const std::string CurlContainer::GetContent(const CurlHolder& holder) const { std::string content{}; for (const Parameter& parameter : containerList_) { if (!content.empty()) { content += "&"; } const std::string escapedKey = encode ? holder.urlEncode(parameter.key) : parameter.key; if (parameter.value.empty()) { content += escapedKey; } else { const std::string escapedValue = encode ? holder.urlEncode(parameter.value) : parameter.value; content += escapedKey + "="; content += escapedValue; } } return content; } template <> const std::string CurlContainer::GetContent(const CurlHolder& holder) const { std::string content{}; for (const cpr::Pair& element : containerList_) { if (!content.empty()) { content += "&"; } const std::string escaped = encode ? holder.urlEncode(element.value) : element.value; content += element.key + "=" + escaped; } return content; } template class CurlContainer; template class CurlContainer; } // namespace cpr cpr-1.11.2/cpr/curlholder.cpp000066400000000000000000000026741475657722200160450ustar00rootroot00000000000000#include "cpr/curlholder.h" #include #include #include #include namespace cpr { CurlHolder::CurlHolder() { /** * Allow multithreaded access to CPR by locking curl_easy_init(). * curl_easy_init() is not thread safe. * References: * https://curl.haxx.se/libcurl/c/curl_easy_init.html * https://curl.haxx.se/libcurl/c/threadsafe.html **/ curl_easy_init_mutex_().lock(); // NOLINTNEXTLINE (cppcoreguidelines-prefer-member-initializer) since we need it to happen inside the lock handle = curl_easy_init(); curl_easy_init_mutex_().unlock(); assert(handle); } // namespace cpr CurlHolder::~CurlHolder() { curl_slist_free_all(chunk); curl_slist_free_all(resolveCurlList); curl_mime_free(multipart); curl_easy_cleanup(handle); } std::string CurlHolder::urlEncode(const std::string& s) const { assert(handle); char* output = curl_easy_escape(handle, s.c_str(), static_cast(s.length())); if (output) { std::string result = output; curl_free(output); return result; } return ""; } std::string CurlHolder::urlDecode(const std::string& s) const { assert(handle); char* output = curl_easy_unescape(handle, s.c_str(), static_cast(s.length()), nullptr); if (output) { std::string result = output; curl_free(output); return result; } return ""; } } // namespace cpr cpr-1.11.2/cpr/curlmultiholder.cpp000066400000000000000000000004221475657722200171050ustar00rootroot00000000000000#include "cpr/curlmultiholder.h" #include #include namespace cpr { CurlMultiHolder::CurlMultiHolder() : handle{curl_multi_init()} { assert(handle); } CurlMultiHolder::~CurlMultiHolder() { curl_multi_cleanup(handle); } } // namespace cpr cpr-1.11.2/cpr/error.cpp000066400000000000000000000134321475657722200150250ustar00rootroot00000000000000#include "cpr/error.h" #include #include #include #include namespace cpr { // NOLINTNEXTLINE - (needed because of static init) static const std::unordered_map curl_error_map = { {CURLE_OK, ErrorCode::OK}, {CURLE_UNSUPPORTED_PROTOCOL, ErrorCode::UNSUPPORTED_PROTOCOL}, {CURLE_FAILED_INIT, ErrorCode::FAILED_INIT}, {CURLE_URL_MALFORMAT, ErrorCode::URL_MALFORMAT}, {CURLE_NOT_BUILT_IN, ErrorCode::NOT_BUILT_IN}, {CURLE_COULDNT_RESOLVE_PROXY, ErrorCode::COULDNT_RESOLVE_PROXY}, {CURLE_COULDNT_RESOLVE_HOST, ErrorCode::COULDNT_RESOLVE_HOST}, {CURLE_COULDNT_CONNECT, ErrorCode::COULDNT_CONNECT}, // Name changed in curl >= 7.51.0. #if LIBCURL_VERSION_NUM >= 0x073300 {CURLE_WEIRD_SERVER_REPLY, ErrorCode::WEIRD_SERVER_REPLY}, #else {CURLE_FTP_WEIRD_SERVER_REPLY, ErrorCode::WEIRD_SERVER_REPLY}, #endif {CURLE_REMOTE_ACCESS_DENIED, ErrorCode::REMOTE_ACCESS_DENIED}, {CURLE_HTTP2, ErrorCode::HTTP2}, {CURLE_QUOTE_ERROR, ErrorCode::QUOTE_ERROR}, {CURLE_HTTP_RETURNED_ERROR, ErrorCode::HTTP_RETURNED_ERROR}, {CURLE_WRITE_ERROR, ErrorCode::WRITE_ERROR}, {CURLE_UPLOAD_FAILED, ErrorCode::UPLOAD_FAILED}, {CURLE_READ_ERROR, ErrorCode::READ_ERROR}, {CURLE_OUT_OF_MEMORY, ErrorCode::OUT_OF_MEMORY}, {CURLE_OPERATION_TIMEDOUT, ErrorCode::OPERATION_TIMEDOUT}, {CURLE_RANGE_ERROR, ErrorCode::RANGE_ERROR}, {CURLE_HTTP_POST_ERROR, ErrorCode::HTTP_POST_ERROR}, {CURLE_SSL_CONNECT_ERROR, ErrorCode::SSL_CONNECT_ERROR}, {CURLE_BAD_DOWNLOAD_RESUME, ErrorCode::BAD_DOWNLOAD_RESUME}, {CURLE_FILE_COULDNT_READ_FILE, ErrorCode::FILE_COULDNT_READ_FILE}, {CURLE_FUNCTION_NOT_FOUND, ErrorCode::FUNCTION_NOT_FOUND}, {CURLE_ABORTED_BY_CALLBACK, ErrorCode::ABORTED_BY_CALLBACK}, {CURLE_BAD_FUNCTION_ARGUMENT, ErrorCode::BAD_FUNCTION_ARGUMENT}, {CURLE_INTERFACE_FAILED, ErrorCode::INTERFACE_FAILED}, {CURLE_TOO_MANY_REDIRECTS, ErrorCode::TOO_MANY_REDIRECTS}, {CURLE_UNKNOWN_OPTION, ErrorCode::UNKNOWN_OPTION}, // Added in curl 7.78.0. #if LIBCURL_VERSION_NUM >= 0x074E00 {CURLE_SETOPT_OPTION_SYNTAX, ErrorCode::SETOPT_OPTION_SYNTAX}, #endif {CURLE_GOT_NOTHING, ErrorCode::GOT_NOTHING}, {CURLE_SSL_ENGINE_NOTFOUND, ErrorCode::SSL_ENGINE_NOTFOUND}, {CURLE_SSL_ENGINE_SETFAILED, ErrorCode::SSL_ENGINE_SETFAILED}, {CURLE_SEND_ERROR, ErrorCode::SEND_ERROR}, {CURLE_RECV_ERROR, ErrorCode::RECV_ERROR}, {CURLE_SSL_CERTPROBLEM, ErrorCode::SSL_CERTPROBLEM}, {CURLE_SSL_CIPHER, ErrorCode::SSL_CIPHER}, {CURLE_PEER_FAILED_VERIFICATION, ErrorCode::PEER_FAILED_VERIFICATION}, {CURLE_BAD_CONTENT_ENCODING, ErrorCode::BAD_CONTENT_ENCODING}, {CURLE_FILESIZE_EXCEEDED, ErrorCode::FILESIZE_EXCEEDED}, {CURLE_USE_SSL_FAILED, ErrorCode::USE_SSL_FAILED}, {CURLE_SEND_FAIL_REWIND, ErrorCode::SEND_FAIL_REWIND}, {CURLE_SSL_ENGINE_INITFAILED, ErrorCode::SSL_ENGINE_INITFAILED}, // Added in curl 7.13.1. #if LIBCURL_VERSION_NUM >= 0x070D01 {CURLE_LOGIN_DENIED, ErrorCode::LOGIN_DENIED}, #endif // Added in curl 7.16.0. #if LIBCURL_VERSION_NUM >= 0x071000 {CURLE_SSL_CACERT_BADFILE, ErrorCode::SSL_CACERT_BADFILE}, #endif // Added in curl 7.16.1. #if LIBCURL_VERSION_NUM >= 0x071001 {CURLE_SSL_SHUTDOWN_FAILED, ErrorCode::SSL_SHUTDOWN_FAILED}, #endif // Added in curl 7.18.2. #if LIBCURL_VERSION_NUM >= 0x071202 {CURLE_AGAIN, ErrorCode::AGAIN}, #endif // Added in curl 7.19.0. #if LIBCURL_VERSION_NUM >= 0x071300 {CURLE_SSL_CRL_BADFILE, ErrorCode::SSL_CRL_BADFILE}, {CURLE_SSL_ISSUER_ERROR, ErrorCode::SSL_ISSUER_ERROR}, #endif // Added in curl 7.21.0. #if LIBCURL_VERSION_NUM >= 0x071500 {CURLE_CHUNK_FAILED, ErrorCode::CHUNK_FAILED}, #endif // Added in curl 7.30.0. #if LIBCURL_VERSION_NUM >= 0x071E00 {CURLE_NO_CONNECTION_AVAILABLE, ErrorCode::NO_CONNECTION_AVAILABLE}, #endif // Added in curl 7.39.0. #if LIBCURL_VERSION_NUM >= 0x072700 {CURLE_SSL_PINNEDPUBKEYNOTMATCH, ErrorCode::SSL_PINNEDPUBKEYNOTMATCH}, #endif // Added in curl 7.41.0. #if LIBCURL_VERSION_NUM >= 0x072900 {CURLE_SSL_INVALIDCERTSTATUS, ErrorCode::SSL_INVALIDCERTSTATUS}, #endif // Added in curl 7.49.0. #if LIBCURL_VERSION_NUM >= 0x073100 {CURLE_HTTP2_STREAM, ErrorCode::HTTP2_STREAM}, #endif {CURLE_PARTIAL_FILE, ErrorCode::PARTIAL_FILE}, // Added in curl 7.59.0. #if LIBCURL_VERSION_NUM >= 0x073B00 {CURLE_RECURSIVE_API_CALL, ErrorCode::RECURSIVE_API_CALL}, #endif // Added in curl 7.66.0. #if LIBCURL_VERSION_NUM >= 0x074200 {CURLE_AUTH_ERROR, ErrorCode::AUTH_ERROR}, #endif // Added in curl 7.68.0. #if LIBCURL_VERSION_NUM >= 0x074400 {CURLE_HTTP3, ErrorCode::HTTP3}, #endif // Added in curl 7.69.0. #if LIBCURL_VERSION_NUM >= 0x074500 {CURLE_QUIC_CONNECT_ERROR, ErrorCode::QUIC_CONNECT_ERROR}, #endif // Added in curl 7.73.0. #if LIBCURL_VERSION_NUM >= 0x074900 {CURLE_PROXY, ErrorCode::PROXY}, #endif // Added in curl 7.77.0. #if LIBCURL_VERSION_NUM >= 0x074D00 {CURLE_SSL_CLIENTCERT, ErrorCode::SSL_CLIENTCERT}, #endif // Added in curl 7.84.0. #if LIBCURL_VERSION_NUM >= 0x075400 {CURLE_UNRECOVERABLE_POLL, ErrorCode::UNRECOVERABLE_POLL}, #endif // Added in curl 7.6.0. #if LIBCURL_VERSION_NUM >= 0x080600 {CURLE_TOO_LARGE, ErrorCode::TOO_LARGE}, #endif }; ErrorCode Error::getErrorCodeForCurlError(std::int32_t curl_code) { auto it = curl_error_map.find(curl_code); if (it == curl_error_map.end()) { // Default return value when the CURL error code is not recognized return ErrorCode::UNKNOWN_ERROR; } return it->second; } } // namespace cprcpr-1.11.2/cpr/file.cpp000066400000000000000000000021401475657722200146050ustar00rootroot00000000000000#include "cpr/file.h" #include #include #include namespace cpr { Files::Files(const std::initializer_list& p_filepaths) : files(p_filepaths.begin(), p_filepaths.end()) {} Files::iterator Files::begin() { return files.begin(); } Files::iterator Files::end() { return files.end(); } Files::const_iterator Files::begin() const { return files.begin(); } Files::const_iterator Files::end() const { return files.end(); } Files::const_iterator Files::cbegin() const { return files.cbegin(); } Files::const_iterator Files::cend() const { return files.cend(); } void Files::emplace_back(const File& file) { files.emplace_back(file); } void Files::push_back(const File& file) { files.push_back(file); } void Files::pop_back() { files.pop_back(); } Files& Files::operator=(const Files& other) { if (&other != this) { files = other.files; } return *this; } Files& Files::operator=(Files&& old) noexcept { if (&old != this) { files = std::move(old.files); } return *this; } } // namespace cpr cpr-1.11.2/cpr/interceptor.cpp000066400000000000000000000040521475657722200162300ustar00rootroot00000000000000#include "cpr/interceptor.h" #include "cpr/callback.h" #include "cpr/multiperform.h" #include "cpr/response.h" #include "cpr/session.h" #include #include #include #include namespace cpr { Response Interceptor::proceed(Session& session) { return session.proceed(); } Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod) { switch (httpMethod) { case ProceedHttpMethod::DELETE_REQUEST: return session.Delete(); case ProceedHttpMethod::GET_REQUEST: return session.Get(); case ProceedHttpMethod::HEAD_REQUEST: return session.Head(); case ProceedHttpMethod::OPTIONS_REQUEST: return session.Options(); case ProceedHttpMethod::PATCH_REQUEST: return session.Patch(); case ProceedHttpMethod::POST_REQUEST: return session.Post(); case ProceedHttpMethod::PUT_REQUEST: return session.Put(); default: throw std::invalid_argument{"Can't proceed the session with the provided http method!"}; } } Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file) { if (httpMethod == ProceedHttpMethod::DOWNLOAD_FILE_REQUEST) { return session.Download(file); } throw std::invalid_argument{"std::ofstream argument is only valid for ProceedHttpMethod::DOWNLOAD_FILE!"}; } Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write) { if (httpMethod == ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST) { return session.Download(write); } throw std::invalid_argument{"WriteCallback argument is only valid for ProceedHttpMethod::DOWNLOAD_CALLBACK!"}; } std::vector InterceptorMulti::proceed(MultiPerform& multi) { return multi.proceed(); } void InterceptorMulti::PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write) { multi.PrepareDownloadSessions(sessions_index, write); } } // namespace cpr cpr-1.11.2/cpr/multipart.cpp000066400000000000000000000005311475657722200157110ustar00rootroot00000000000000#include "cpr/multipart.h" #include #include namespace cpr { Multipart::Multipart(const std::initializer_list& p_parts) : parts{p_parts} {} Multipart::Multipart(const std::vector& p_parts) : parts{p_parts} {} Multipart::Multipart(const std::vector&& p_parts) : parts{p_parts} {} } // namespace cpr cpr-1.11.2/cpr/multiperform.cpp000066400000000000000000000311311475657722200164150ustar00rootroot00000000000000#include "cpr/multiperform.h" #include "cpr/callback.h" #include "cpr/curlmultiholder.h" #include "cpr/interceptor.h" #include "cpr/response.h" #include "cpr/session.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cpr { MultiPerform::MultiPerform() : multicurl_(new CurlMultiHolder()) { current_interceptor_ = interceptors_.end(); first_interceptor_ = interceptors_.end(); } MultiPerform::~MultiPerform() { // Unlock all sessions for (const std::pair, HttpMethod>& pair : sessions_) { pair.first->isUsedInMultiPerform = false; // Remove easy handle from multi handle const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, pair.first->curl_->handle); if (error_code) { std::cerr << "curl_multi_remove_handle() failed, code " << static_cast(error_code) << '\n'; return; } } } void MultiPerform::AddSession(std::shared_ptr& session, HttpMethod method) { // Check if this multiperform is download only if (((method != HttpMethod::DOWNLOAD_REQUEST && is_download_multi_perform) && method != HttpMethod::UNDEFINED) || (method == HttpMethod::DOWNLOAD_REQUEST && !is_download_multi_perform && !sessions_.empty())) { // Currently it is not possible to mix download and non-download methods, as download needs additional parameters throw std::invalid_argument("Failed to add session: Cannot mix download and non-download methods!"); } // Set download only if neccessary if (method == HttpMethod::DOWNLOAD_REQUEST) { is_download_multi_perform = true; } // Add easy handle to multi handle const CURLMcode error_code = curl_multi_add_handle(multicurl_->handle, session->curl_->handle); if (error_code) { std::cerr << "curl_multi_add_handle() failed, code " << static_cast(error_code) << '\n'; return; } // Lock session to the multihandle session->isUsedInMultiPerform = true; // Add session to sessions_ sessions_.emplace_back(session, method); } void MultiPerform::RemoveSession(const std::shared_ptr& session) { // Has to be handled before calling curl_multi_remove_handle to avoid it returning something != CURLM_OK. if (sessions_.empty()) { throw std::invalid_argument("Failed to find session!"); } // Remove easy handle from multihandle const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle); if (error_code != CURLM_OK) { std::cerr << "curl_multi_remove_handle() failed, code " << static_cast(error_code) << '\n'; return; } // Unlock session session->isUsedInMultiPerform = false; // Remove session from sessions_ auto it = std::find_if(sessions_.begin(), sessions_.end(), [&session](const std::pair, HttpMethod>& pair) { return session->curl_->handle == pair.first->curl_->handle; }); if (it == sessions_.end()) { throw std::invalid_argument("Failed to find session!"); } sessions_.erase(it); // Reset download only if empty if (sessions_.empty()) { is_download_multi_perform = false; } } std::vector, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() { return sessions_; } const std::vector, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() const { return sessions_; } void MultiPerform::DoMultiPerform() { // Do multi perform until every handle has finished int still_running{0}; do { CURLMcode error_code = curl_multi_perform(multicurl_->handle, &still_running); if (error_code) { std::cerr << "curl_multi_perform() failed, code " << static_cast(error_code) << '\n'; break; } if (still_running) { const int timeout_ms{250}; #if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 error_code = curl_multi_poll(multicurl_->handle, nullptr, 0, timeout_ms, nullptr); if (error_code) { std::cerr << "curl_multi_poll() failed, code " << static_cast(error_code) << '\n'; #else error_code = curl_multi_wait(multicurl_->handle, nullptr, 0, timeout_ms, nullptr); if (error_code) { std::cerr << "curl_multi_wait() failed, code " << static_cast(error_code) << '\n'; #endif break; } } } while (still_running); } std::vector MultiPerform::ReadMultiInfo(const std::function& complete_function) { // Get infos and create Response objects std::vector responses; struct CURLMsg* info{nullptr}; do { int msgq = 0; // Read info from multihandle info = curl_multi_info_read(multicurl_->handle, &msgq); if (info) { // Find current session auto it = std::find_if(sessions_.begin(), sessions_.end(), [&info](const std::pair, HttpMethod>& pair) { return pair.first->curl_->handle == info->easy_handle; }); if (it == sessions_.end()) { std::cerr << "Failed to find current session!" << '\n'; break; } const std::shared_ptr current_session = (*it).first; // Add response object // NOLINTNEXTLINE (cppcoreguidelines-pro-type-union-access) responses.push_back(complete_function(*current_session, info->data.result)); } } while (info); // Sort response objects to match order of added sessions std::vector sorted_responses; for (const std::pair, HttpMethod>& pair : sessions_) { Session& current_session = *(pair.first); auto it = std::find_if(responses.begin(), responses.end(), [¤t_session](const Response& response) { return current_session.curl_->handle == response.curl_->handle; }); const Response current_response = *it; // NOLINT (performance-unnecessary-copy-initialization) False possible // Erase response from original vector to increase future search speed responses.erase(it); sorted_responses.push_back(current_response); } return sorted_responses; } std::vector MultiPerform::MakeRequest() { const std::optional> r = intercept(); if (r.has_value()) { return r.value(); } DoMultiPerform(); return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.Complete(curl_error); }); } std::vector MultiPerform::MakeDownloadRequest() { const std::optional> r = intercept(); if (r.has_value()) { return r.value(); } DoMultiPerform(); return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.CompleteDownload(curl_error); }); } void MultiPerform::PrepareSessions() { for (const std::pair, HttpMethod>& pair : sessions_) { switch (pair.second) { case HttpMethod::GET_REQUEST: pair.first->PrepareGet(); break; case HttpMethod::POST_REQUEST: pair.first->PreparePost(); break; case HttpMethod::PUT_REQUEST: pair.first->PreparePut(); break; case HttpMethod::DELETE_REQUEST: pair.first->PrepareDelete(); break; case HttpMethod::PATCH_REQUEST: pair.first->PreparePatch(); break; case HttpMethod::HEAD_REQUEST: pair.first->PrepareHead(); break; case HttpMethod::OPTIONS_REQUEST: pair.first->PrepareOptions(); break; default: std::cerr << "PrepareSessions failed: Undefined HttpMethod or download without arguments!" << '\n'; return; } } } void MultiPerform::PrepareDownloadSession(size_t sessions_index, const WriteCallback& write) { const std::pair, HttpMethod>& pair = sessions_[sessions_index]; switch (pair.second) { case HttpMethod::DOWNLOAD_REQUEST: pair.first->PrepareDownload(write); break; default: std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << '\n'; return; } } void MultiPerform::PrepareDownloadSession(size_t sessions_index, std::ofstream& file) { const std::pair, HttpMethod>& pair = sessions_[sessions_index]; switch (pair.second) { case HttpMethod::DOWNLOAD_REQUEST: pair.first->PrepareDownload(file); break; default: std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << '\n'; return; } } void MultiPerform::SetHttpMethod(HttpMethod method) { for (std::pair, HttpMethod>& pair : sessions_) { pair.second = method; } } void MultiPerform::PrepareGet() { SetHttpMethod(HttpMethod::GET_REQUEST); PrepareSessions(); } void MultiPerform::PrepareDelete() { SetHttpMethod(HttpMethod::DELETE_REQUEST); PrepareSessions(); } void MultiPerform::PreparePut() { SetHttpMethod(HttpMethod::PUT_REQUEST); PrepareSessions(); } void MultiPerform::PreparePatch() { SetHttpMethod(HttpMethod::PATCH_REQUEST); PrepareSessions(); } void MultiPerform::PrepareHead() { SetHttpMethod(HttpMethod::HEAD_REQUEST); PrepareSessions(); } void MultiPerform::PrepareOptions() { SetHttpMethod(HttpMethod::OPTIONS_REQUEST); PrepareSessions(); } void MultiPerform::PreparePost() { SetHttpMethod(HttpMethod::POST_REQUEST); PrepareSessions(); } std::vector MultiPerform::Get() { PrepareGet(); return MakeRequest(); } std::vector MultiPerform::Delete() { PrepareDelete(); return MakeRequest(); } std::vector MultiPerform::Put() { PreparePut(); return MakeRequest(); } std::vector MultiPerform::Head() { PrepareHead(); return MakeRequest(); } std::vector MultiPerform::Options() { PrepareOptions(); return MakeRequest(); } std::vector MultiPerform::Patch() { PreparePatch(); return MakeRequest(); } std::vector MultiPerform::Post() { PreparePost(); return MakeRequest(); } std::vector MultiPerform::Perform() { PrepareSessions(); return MakeRequest(); } std::vector MultiPerform::proceed() { // Check if this multiperform mixes download and non download requests if (!sessions_.empty()) { const bool new_is_download_multi_perform = sessions_.front().second == HttpMethod::DOWNLOAD_REQUEST; for (const std::pair, HttpMethod>& s : sessions_) { const HttpMethod method = s.second; if ((new_is_download_multi_perform && method != HttpMethod::DOWNLOAD_REQUEST) || (!new_is_download_multi_perform && method == HttpMethod::DOWNLOAD_REQUEST)) { throw std::invalid_argument("Failed to proceed with session: Cannot mix download and non-download methods!"); } } is_download_multi_perform = new_is_download_multi_perform; } PrepareSessions(); return MakeRequest(); } const std::optional> MultiPerform::intercept() { if (current_interceptor_ == interceptors_.end()) { current_interceptor_ = first_interceptor_; } else { current_interceptor_++; } if (current_interceptor_ != interceptors_.end()) { auto icpt = current_interceptor_; // Nested makeRequest() start at first_interceptor_, thus excluding previous interceptors. first_interceptor_ = current_interceptor_; ++first_interceptor_; const std::optional> r = (*current_interceptor_)->intercept(*this); first_interceptor_ = icpt; return r; } return std::nullopt; } void MultiPerform::AddInterceptor(const std::shared_ptr& pinterceptor) { // Shall only add before first interceptor run assert(current_interceptor_ == interceptors_.end()); interceptors_.push_back(pinterceptor); first_interceptor_ = interceptors_.begin(); } } // namespace cpr cpr-1.11.2/cpr/parameters.cpp000066400000000000000000000002111475657722200160260ustar00rootroot00000000000000// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future #include "cpr/parameters.h" namespace cpr {} // namespace cpr cpr-1.11.2/cpr/payload.cpp000066400000000000000000000002061475657722200153200ustar00rootroot00000000000000// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future #include "cpr/payload.h" namespace cpr {} // namespace cpr cpr-1.11.2/cpr/proxies.cpp000066400000000000000000000010371475657722200153630ustar00rootroot00000000000000#include "cpr/proxies.h" #include #include #include #include namespace cpr { Proxies::Proxies(const std::initializer_list>& hosts) : hosts_{hosts} {} Proxies::Proxies(const std::map& hosts) : hosts_{hosts} {} bool Proxies::has(const std::string& protocol) const { return hosts_.count(protocol) > 0; } const std::string& Proxies::operator[](const std::string& protocol) { return hosts_[protocol]; } } // namespace cpr cpr-1.11.2/cpr/proxyauth.cpp000066400000000000000000000014261475657722200157370ustar00rootroot00000000000000#include "cpr/proxyauth.h" #include "cpr/util.h" #include namespace cpr { EncodedAuthentication::~EncodedAuthentication() noexcept { util::secureStringClear(username); util::secureStringClear(password); } const std::string& EncodedAuthentication::GetUsername() const { return username; } const std::string& EncodedAuthentication::GetPassword() const { return password; } bool ProxyAuthentication::has(const std::string& protocol) const { return proxyAuth_.count(protocol) > 0; } const char* ProxyAuthentication::GetUsername(const std::string& protocol) { return proxyAuth_[protocol].username.c_str(); } const char* ProxyAuthentication::GetPassword(const std::string& protocol) { return proxyAuth_[protocol].password.c_str(); } } // namespace cpr cpr-1.11.2/cpr/redirect.cpp000066400000000000000000000027411475657722200154760ustar00rootroot00000000000000#include "cpr/redirect.h" #include namespace cpr { PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs) { return static_cast(static_cast(lhs) ^ static_cast(rhs)); } PostRedirectFlags operator~(PostRedirectFlags flag) { return static_cast(~static_cast(flag)); } PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs) { lhs = static_cast(static_cast(lhs) | static_cast(rhs)); const uint8_t tmp = static_cast(lhs); lhs = static_cast(tmp); return lhs; } PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs) { lhs = static_cast(static_cast(lhs) & static_cast(rhs)); return lhs; } PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs) { lhs = static_cast(static_cast(lhs) ^ static_cast(rhs)); return lhs; } bool any(PostRedirectFlags flag) { return flag != PostRedirectFlags::NONE; } } // namespace cpr cpr-1.11.2/cpr/response.cpp000066400000000000000000000044041475657722200155310ustar00rootroot00000000000000#include "cpr/response.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cpr { Response::Response(std::shared_ptr 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)) { header = cpr::util::parseHeader(raw_header, &status_line, &reason); assert(curl_); assert(curl_->handle); curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code); curl_easy_getinfo(curl_->handle, CURLINFO_TOTAL_TIME, &elapsed); char* url_string{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_EFFECTIVE_URL, &url_string); url = Url(url_string); #if LIBCURL_VERSION_NUM >= 0x073700 // 7.55.0 curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD_T, &downloaded_bytes); curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD_T, &uploaded_bytes); #else double downloaded_bytes_double, uploaded_bytes_double; curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD, &downloaded_bytes_double); curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD, &uploaded_bytes_double); downloaded_bytes = downloaded_bytes_double; uploaded_bytes = uploaded_bytes_double; #endif curl_easy_getinfo(curl_->handle, CURLINFO_REDIRECT_COUNT, &redirect_count); } std::vector Response::GetCertInfos() const { assert(curl_); assert(curl_->handle); curl_certinfo* ci{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_CERTINFO, &ci); std::vector cert_infos; for (int i = 0; i < ci->num_of_certs; i++) { CertInfo cert_info; // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) for (curl_slist* slist = ci->certinfo[i]; slist; slist = slist->next) { cert_info.emplace_back(std::string{slist->data}); } cert_infos.emplace_back(cert_info); } return cert_infos; } } // namespace cpr cpr-1.11.2/cpr/session.cpp000066400000000000000000001147211475657722200153620ustar00rootroot00000000000000#include "cpr/session.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpr/accept_encoding.h" #include "cpr/async.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/body.h" #include "cpr/callback.h" #include "cpr/connect_timeout.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" #include "cpr/error.h" #include "cpr/file.h" #include "cpr/filesystem.h" // IWYU pragma: keep #include "cpr/http_version.h" #include "cpr/interceptor.h" #include "cpr/interface.h" #include "cpr/limit_rate.h" #include "cpr/local_port.h" #include "cpr/local_port_range.h" #include "cpr/low_speed.h" #include "cpr/multipart.h" #include "cpr/parameters.h" #include "cpr/payload.h" #include "cpr/proxies.h" #include "cpr/proxyauth.h" #include "cpr/range.h" #include "cpr/redirect.h" #include "cpr/reserve_size.h" #include "cpr/resolve.h" #include "cpr/response.h" #include "cpr/ssl_options.h" #include "cpr/timeout.h" #include "cpr/unix_socket.h" #include "cpr/user_agent.h" #include "cpr/util.h" #include "cpr/verbose.h" #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION #include "cpr/ssl_ctx.h" #endif namespace cpr { // Ignored here since libcurl reqires a long: // NOLINTNEXTLINE(google-runtime-int) constexpr long ON = 1L; // Ignored here since libcurl reqires a long: // NOLINTNEXTLINE(google-runtime-int) constexpr long OFF = 0L; CURLcode Session::DoEasyPerform() { if (isUsedInMultiPerform) { std::cerr << "curl_easy_perform cannot be executed if the CURL handle is used in a MultiPerform.\n"; return CURLcode::CURLE_FAILED_INIT; } return curl_easy_perform(curl_->handle); } void Session::prepareHeader() { curl_slist* chunk = nullptr; for (const std::pair& item : header_) { std::string header_string = item.first; if (item.second.empty()) { header_string += ";"; } else { header_string += ": " + item.second; } curl_slist* temp = curl_slist_append(chunk, header_string.c_str()); if (temp) { chunk = temp; } } // Set the chunked transfer encoding in case it does not already exist: if (chunkedTransferEncoding_ && header_.find("Transfer-Encoding") == header_.end()) { curl_slist* temp = curl_slist_append(chunk, "Transfer-Encoding:chunked"); if (temp) { chunk = temp; } } // libcurl would prepare the header "Expect: 100-continue" by default when uploading files larger than 1 MB. // Here we would like to disable this feature: curl_slist* temp = curl_slist_append(chunk, "Expect:"); if (temp) { chunk = temp; } curl_easy_setopt(curl_->handle, CURLOPT_HTTPHEADER, chunk); curl_slist_free_all(curl_->chunk); curl_->chunk = chunk; } void Session::prepareProxy() { const std::string protocol = url_.str().substr(0, url_.str().find(':')); if (proxies_.has(protocol)) { curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); if (proxyAuth_.has(protocol)) { curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERNAME, proxyAuth_.GetUsername(protocol)); curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPassword(protocol)); } } } // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 void Session::SetBearer(const Bearer& token) { // Ignore here since this has been defined by libcurl. curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); curl_easy_setopt(curl_->handle, CURLOPT_XOAUTH2_BEARER, token.GetToken()); } #endif Session::Session() : curl_(new CurlHolder()) { // Set up some sensible defaults curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); const std::string version = "curl/" + std::string{version_info->version}; curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str()); SetRedirect(Redirect()); curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data()); curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, ""); #ifdef CPR_CURL_NOSIGNAL curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L); #endif #if LIBCURL_VERSION_NUM >= 0x071900 // 7.25.0 curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L); #endif current_interceptor_ = interceptors_.end(); first_interceptor_ = interceptors_.end(); } Response Session::makeDownloadRequest() { const std::optional r = intercept(); if (r.has_value()) { return r.value(); } const CURLcode curl_error = DoEasyPerform(); return CompleteDownload(curl_error); } void Session::prepareCommonShared() { assert(curl_->handle); // Set Header: prepareHeader(); // URL parameter: const std::string parametersContent = parameters_.GetContent(*curl_); if (!parametersContent.empty()) { const Url new_url{url_ + "?" + parametersContent}; curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); } else { curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); } // Proxy: prepareProxy(); #if LIBCURL_VERSION_NUM >= 0x071506 // 7.21.6 if (acceptEncoding_.empty()) { // Enable all supported built-in compressions curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, ""); } else if (acceptEncoding_.disabled()) { // Disable curl adding the 'Accept-Encoding' header curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, nullptr); } else { curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str()); } #endif curl_->error[0] = '\0'; // Clear the response response_string_.clear(); if (response_string_reserve_size_ > 0) { response_string_.reserve(response_string_reserve_size_); } // Enable so we are able to retrieve certificate information: curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L); } void Session::prepareCommon() { assert(curl_->handle); // Everything else: prepareCommonShared(); // Set Content: prepareBodyPayloadOrMultipart(); if (!cbs_->writecb_.callback) { curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction); curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_); } header_string_.clear(); if (!cbs_->headercb_.callback) { curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); } } void Session::prepareCommonDownload() { assert(curl_->handle); // Everything else: prepareCommonShared(); header_string_.clear(); if (cbs_->headercb_.callback) { curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &cbs_->headercb_); } else { curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); } } Response Session::makeRequest() { const std::optional r = intercept(); if (r.has_value()) { return r.value(); } const CURLcode curl_error = DoEasyPerform(); return Complete(curl_error); } void Session::SetLimitRate(const LimitRate& limit_rate) { curl_easy_setopt(curl_->handle, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate.downrate); curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate); } const Content& Session::GetContent() const { return content_; } void Session::RemoveContent() { // inverse function to prepareBodyPayloadOrMultipart() if (std::holds_alternative(content_) || std::holds_alternative(content_)) { // set default values, so curl does not send a body in subsequent requests curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, -1); curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, nullptr); } else if (std::holds_alternative(content_)) { if (curl_->multipart) { // remove multipart data curl_mime_free(curl_->multipart); curl_->multipart = nullptr; } } content_ = std::monostate{}; } void Session::SetReadCallback(const ReadCallback& read) { cbs_->readcb_ = read; curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size); curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size); curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &cbs_->readcb_); chunkedTransferEncoding_ = read.size == -1; } void Session::SetHeaderCallback(const HeaderCallback& header) { curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); cbs_->headercb_ = header; curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &cbs_->headercb_); } void Session::SetWriteCallback(const WriteCallback& write) { curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction); cbs_->writecb_ = write; curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &cbs_->writecb_); } void Session::SetProgressCallback(const ProgressCallback& progress) { cbs_->progresscb_ = progress; if (isCancellable) { cbs_->cancellationcb_.SetProgressCallback(cbs_->progresscb_); return; } #if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0 curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cbs_->progresscb_); #else curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cbs_->progresscb_); #endif curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); } void Session::SetDebugCallback(const DebugCallback& debug) { curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction); cbs_->debugcb_ = debug; curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &cbs_->debugcb_); curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L); } void Session::SetUrl(const Url& url) { url_ = url; } void Session::SetResolve(const Resolve& resolve) { SetResolves({resolve}); } void Session::SetResolves(const std::vector& resolves) { curl_slist_free_all(curl_->resolveCurlList); curl_->resolveCurlList = nullptr; for (const Resolve& resolve : resolves) { for (const uint16_t port : resolve.ports) { curl_->resolveCurlList = curl_slist_append(curl_->resolveCurlList, (resolve.host + ":" + std::to_string(port) + ":" + resolve.addr).c_str()); } } curl_easy_setopt(curl_->handle, CURLOPT_RESOLVE, curl_->resolveCurlList); } void Session::SetParameters(const Parameters& parameters) { parameters_ = parameters; } void Session::SetParameters(Parameters&& parameters) { parameters_ = std::move(parameters); } void Session::SetHeader(const Header& header) { header_ = header; } void Session::UpdateHeader(const Header& header) { for (const std::pair& item : header) { header_[item.first] = item.second; } } Header& Session::GetHeader() { return header_; } const Header& Session::GetHeader() const { return header_; } void Session::SetTimeout(const Timeout& timeout) { curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds()); } void Session::SetConnectTimeout(const ConnectTimeout& timeout) { curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds()); } void Session::SetAuth(const Authentication& auth) { // Ignore here since this has been defined by libcurl. switch (auth.GetAuthMode()) { case AuthMode::BASIC: curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); break; case AuthMode::DIGEST: curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); break; case AuthMode::NTLM: curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); break; case AuthMode::NEGOTIATE: curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE); curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); break; } } void Session::SetUserAgent(const UserAgent& ua) { curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str()); } void Session::SetPayload(const Payload& payload) { content_ = payload; } void Session::SetPayload(Payload&& payload) { content_ = std::move(payload); } void Session::SetProxies(const Proxies& proxies) { proxies_ = proxies; } void Session::SetProxies(Proxies&& proxies) { proxies_ = std::move(proxies); } void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) { proxyAuth_ = std::move(proxy_auth); } void Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) { proxyAuth_ = proxy_auth; } void Session::SetMultipart(const Multipart& multipart) { content_ = multipart; } void Session::SetMultipart(Multipart&& multipart) { content_ = std::move(multipart); } void Session::SetRedirect(const Redirect& redirect) { curl_easy_setopt(curl_->handle, CURLOPT_FOLLOWLOCATION, redirect.follow ? 1L : 0L); curl_easy_setopt(curl_->handle, CURLOPT_MAXREDIRS, redirect.maximum); curl_easy_setopt(curl_->handle, CURLOPT_UNRESTRICTED_AUTH, redirect.cont_send_cred ? 1L : 0L); // NOLINTNEXTLINE (google-runtime-int) long mask = 0; if (any(redirect.post_flags & PostRedirectFlags::POST_301)) { mask |= CURL_REDIR_POST_301; } if (any(redirect.post_flags & PostRedirectFlags::POST_302)) { mask |= CURL_REDIR_POST_302; } if (any(redirect.post_flags & PostRedirectFlags::POST_303)) { mask |= CURL_REDIR_POST_303; } curl_easy_setopt(curl_->handle, CURLOPT_POSTREDIR, mask); } void Session::SetCookies(const Cookies& cookies) { curl_easy_setopt(curl_->handle, CURLOPT_COOKIELIST, "ALL"); curl_easy_setopt(curl_->handle, CURLOPT_COOKIE, cookies.GetEncoded(*curl_).c_str()); } void Session::SetBody(const Body& body) { content_ = body; } void Session::SetBody(Body&& body) { content_ = std::move(body); } void Session::SetLowSpeed(const LowSpeed& low_speed) { curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, low_speed.limit); curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, low_speed.time); // cppcheck-suppress y2038-unsafe-call } void Session::SetVerifySsl(const VerifySsl& verify) { curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF); curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L); } void Session::SetUnixSocket(const UnixSocket& unix_socket) { curl_easy_setopt(curl_->handle, CURLOPT_UNIX_SOCKET_PATH, unix_socket.GetUnixSocketString()); } void Session::SetSslOptions(const SslOptions& options) { if (!options.cert_file.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT, options.cert_file.c_str()); if (!options.cert_type.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLCERTTYPE, options.cert_type.c_str()); } } if (!options.key_file.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY, options.key_file.c_str()); if (!options.key_type.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str()); } if (!options.key_pass.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); } #if SUPPORT_CURLOPT_SSLKEY_BLOB } else if (!options.key_blob.empty()) { std::string key_blob(options.key_blob); curl_blob blob{}; // NOLINTNEXTLINE (readability-container-data-pointer) blob.data = &key_blob[0]; blob.len = key_blob.length(); blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY_BLOB, &blob); if (!options.key_type.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str()); } if (!options.key_pass.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); } #endif } if (!options.pinned_public_key.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_PINNEDPUBLICKEY, options.pinned_public_key.c_str()); } #if SUPPORT_ALPN curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_ALPN, options.enable_alpn ? ON : OFF); #endif #if SUPPORT_NPN curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_NPN, options.enable_npn ? ON : OFF); #endif curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, options.verify_peer ? ON : OFF); curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, options.verify_host ? 2L : 0L); #if LIBCURL_VERSION_NUM >= 0x072900 // 7.41.0 curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYSTATUS, options.verify_status ? ON : OFF); #endif int maxTlsVersion = options.ssl_version; #if SUPPORT_MAX_TLS_VERSION maxTlsVersion |= options.max_version; #endif curl_easy_setopt(curl_->handle, CURLOPT_SSLVERSION, // Ignore here since this has been defined by libcurl. maxTlsVersion); // NOLINTNEXTLINE (google-runtime-int) long curlSslOptions = 0; #if SUPPORT_SSL_NO_REVOKE sslNoRevoke_ = options.ssl_no_revoke; if (options.ssl_no_revoke) { curlSslOptions |= CURLSSLOPT_NO_REVOKE; } #endif #if LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0 // Fix loading certs from Windows cert store when using OpenSSL: curlSslOptions |= CURLSSLOPT_NATIVE_CA; #endif curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, curlSslOptions); if (!options.ca_info.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_CAINFO, options.ca_info.c_str()); } if (!options.ca_path.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_CAPATH, options.ca_path.c_str()); } #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION #ifdef OPENSSL_BACKEND_USED if (!options.ca_buffer.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function_load_ca_cert_from_buffer); curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_DATA, options.ca_buffer.c_str()); } #endif #endif if (!options.crl_file.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_CRLFILE, options.crl_file.c_str()); } if (!options.ciphers.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSL_CIPHER_LIST, options.ciphers.c_str()); } #if SUPPORT_TLSv13_CIPHERS if (!options.tls13_ciphers.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_TLS13_CIPHERS, options.ciphers.c_str()); } #endif #if SUPPORT_SESSIONID_CACHE curl_easy_setopt(curl_->handle, CURLOPT_SSL_SESSIONID_CACHE, options.session_id_cache ? ON : OFF); #endif } void Session::SetVerbose(const Verbose& verbose) { curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF); } void Session::SetInterface(const Interface& iface) { if (iface.str().empty()) { curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr); } else { curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str()); } } void Session::SetLocalPort(const LocalPort& local_port) { curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORT, static_cast(static_cast(local_port))); } void Session::SetLocalPortRange(const LocalPortRange& local_port_range) { curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORTRANGE, static_cast(static_cast(local_port_range))); } void Session::SetHttpVersion(const HttpVersion& version) { switch (version.code) { case HttpVersionCode::VERSION_NONE: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); break; case HttpVersionCode::VERSION_1_0: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); break; case HttpVersionCode::VERSION_1_1: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); break; #if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0 case HttpVersionCode::VERSION_2_0: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); break; #endif #if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0 case HttpVersionCode::VERSION_2_0_TLS: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); break; #endif #if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0 case HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); break; #endif #if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 case HttpVersionCode::VERSION_3_0: curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3); break; #endif default: // Should not happen throw std::invalid_argument("Invalid/Unknown HTTP version type."); } } void Session::SetRange(const Range& range) { const std::string range_str = range.str(); curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str()); } void Session::SetMultiRange(const MultiRange& multi_range) { const std::string multi_range_str = multi_range.str(); curl_easy_setopt(curl_->handle, CURLOPT_RANGE, multi_range_str.c_str()); } void Session::SetReserveSize(const ReserveSize& reserve_size) { ResponseStringReserve(reserve_size.size); } void Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) { acceptEncoding_ = accept_encoding; } void Session::SetAcceptEncoding(AcceptEncoding&& accept_encoding) { acceptEncoding_ = std::move(accept_encoding); } cpr_off_t Session::GetDownloadFileLength() { cpr_off_t downloadFileLength = -1; curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); prepareProxy(); curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1); if (DoEasyPerform() == CURLE_OK) { // NOLINTNEXTLINE (google-runtime-int) long status_code{}; curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code); if (200 == status_code) { curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLength); } } return downloadFileLength; } void Session::ResponseStringReserve(size_t size) { response_string_reserve_size_ = size; } Response Session::Delete() { PrepareDelete(); return makeRequest(); } Response Session::Download(const WriteCallback& write) { PrepareDownload(write); return makeDownloadRequest(); } Response Session::Download(std::ofstream& file) { PrepareDownload(file); return makeDownloadRequest(); } Response Session::Get() { PrepareGet(); return makeRequest(); } Response Session::Head() { PrepareHead(); return makeRequest(); } Response Session::Options() { PrepareOptions(); return makeRequest(); } Response Session::Patch() { PreparePatch(); return makeRequest(); } Response Session::Post() { PreparePost(); return makeRequest(); } Response Session::Put() { PreparePut(); return makeRequest(); } std::shared_ptr Session::GetSharedPtrFromThis() { try { return shared_from_this(); } catch (std::bad_weak_ptr&) { 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."); } } AsyncResponse Session::GetAsync() { auto shared_this = shared_from_this(); return async([shared_this]() { return shared_this->Get(); }); } AsyncResponse Session::DeleteAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Delete(); }); } AsyncResponse Session::DownloadAsync(const WriteCallback& write) { return async([shared_this = GetSharedPtrFromThis(), write]() { return shared_this->Download(write); }); } AsyncResponse Session::DownloadAsync(std::ofstream& file) { return async([shared_this = GetSharedPtrFromThis(), &file]() { return shared_this->Download(file); }); } AsyncResponse Session::HeadAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Head(); }); } AsyncResponse Session::OptionsAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Options(); }); } AsyncResponse Session::PatchAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Patch(); }); } AsyncResponse Session::PostAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Post(); }); } AsyncResponse Session::PutAsync() { return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Put(); }); } std::shared_ptr Session::GetCurlHolder() { return curl_; } std::string Session::GetFullRequestUrl() { const std::string parametersContent = parameters_.GetContent(*curl_); return url_.str() + (parametersContent.empty() ? "" : "?") + parametersContent; } void Session::PrepareDelete() { curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L); curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "DELETE"); prepareCommon(); } void Session::PrepareGet() { // In case there is a body or payload for this request, we create a custom GET-Request since a // GET-Request with body is based on the HTTP RFC **not** a leagal request. if (hasBodyOrPayload()) { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "GET"); } else { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1L); } prepareCommon(); } void Session::PrepareHead() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); prepareCommon(); } void Session::PrepareOptions() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "OPTIONS"); prepareCommon(); } void Session::PreparePatch() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PATCH"); prepareCommon(); } void Session::PreparePost() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); // In case there is no body or payload set it to an empty post: if (hasBodyOrPayload()) { curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); } else { curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, cbs_->readcb_.callback ? nullptr : ""); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "POST"); } prepareCommon(); } void Session::PreparePut() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); if (!hasBodyOrPayload() && cbs_->readcb_.callback) { /** * Yes, this one has to be CURLOPT_POSTFIELDS even if we are performing a PUT request. * In case we don't set this one, performing a POST-request with PUT won't work. * It in theory this only enforces the usage of the readcallback for POST requests, but works here as well. **/ curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr); } curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PUT"); curl_easy_setopt(curl_->handle, CURLOPT_RANGE, nullptr); prepareCommon(); } void Session::PrepareDownload(std::ofstream& file) { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction); curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); prepareCommonDownload(); } void Session::PrepareDownload(const WriteCallback& write) { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); SetWriteCallback(write); prepareCommonDownload(); } Response Session::Complete(CURLcode curl_error) { curl_slist* raw_cookies{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); Cookies cookies = util::parseCookies(raw_cookies); curl_slist_free_all(raw_cookies); std::string errorMsg = curl_->error.data(); return Response(curl_, std::move(response_string_), std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); } Response Session::CompleteDownload(CURLcode curl_error) { if (!cbs_->headercb_.callback) { curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr); curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0); } curl_slist* raw_cookies{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); Cookies cookies = util::parseCookies(raw_cookies); curl_slist_free_all(raw_cookies); std::string errorMsg = curl_->error.data(); return Response(curl_, "", std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); } void Session::AddInterceptor(const std::shared_ptr& pinterceptor) { // Shall only add before first interceptor run assert(current_interceptor_ == interceptors_.end()); interceptors_.push_back(pinterceptor); first_interceptor_ = interceptors_.begin(); } Response Session::proceed() { prepareCommon(); return makeRequest(); } const std::optional Session::intercept() { if (current_interceptor_ == interceptors_.end()) { current_interceptor_ = first_interceptor_; } else { current_interceptor_++; } if (current_interceptor_ != interceptors_.end()) { auto icpt = current_interceptor_; // Nested makeRequest() start at first_interceptor_, thus excluding previous interceptors. first_interceptor_ = current_interceptor_; ++first_interceptor_; const std::optional r = (*current_interceptor_)->intercept(*this); first_interceptor_ = icpt; return r; } return std::nullopt; } void Session::prepareBodyPayloadOrMultipart() const { // Either a body, multipart or a payload is allowed. Inverse function to RemoveContent() if (std::holds_alternative(content_)) { const std::string payload = std::get(content_).GetContent(*curl_); curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(payload.length())); curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, payload.c_str()); } else if (std::holds_alternative(content_)) { const std::string& body = std::get(content_).str(); curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(body.length())); curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str()); } else if (std::holds_alternative(content_)) { // Make sure, we have a empty multipart to start with: if (curl_->multipart) { curl_mime_free(curl_->multipart); } curl_->multipart = curl_mime_init(curl_->handle); // Add all multipart pieces: const cpr::Multipart& multipart = std::get(content_); for (const Part& part : multipart.parts) { if (part.is_file) { for (const File& file : part.files) { curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart); if (!part.content_type.empty()) { curl_mime_type(mimePart, part.content_type.c_str()); } curl_mime_filedata(mimePart, file.filepath.c_str()); curl_mime_name(mimePart, part.name.c_str()); if (file.hasOverridenFilename()) { curl_mime_filename(mimePart, file.overriden_filename.c_str()); } else { // NOLINTNEXTLINE (misc-include-cleaner) curl_mime_filename(mimePart, fs::path(file.filepath).filename().string().c_str()); } } } else { curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart); if (!part.content_type.empty()) { curl_mime_type(mimePart, part.content_type.c_str()); } if (part.is_buffer) { // Do not use formdata, to prevent having to use reinterpreter_cast: curl_mime_name(mimePart, part.name.c_str()); curl_mime_data(mimePart, part.data, part.datalen); curl_mime_filename(mimePart, part.value.c_str()); } else { curl_mime_name(mimePart, part.name.c_str()); curl_mime_data(mimePart, part.value.c_str(), CURL_ZERO_TERMINATED); } } } curl_easy_setopt(curl_->handle, CURLOPT_MIMEPOST, curl_->multipart); } } [[nodiscard]] bool Session::hasBodyOrPayload() const { return std::holds_alternative(content_) || std::holds_alternative(content_); } // clang-format off void Session::SetOption(const Resolve& resolve) { SetResolve(resolve); } void Session::SetOption(const std::vector& resolves) { SetResolves(resolves); } void Session::SetOption(const ReadCallback& read) { SetReadCallback(read); } void Session::SetOption(const HeaderCallback& header) { SetHeaderCallback(header); } void Session::SetOption(const WriteCallback& write) { SetWriteCallback(write); } void Session::SetOption(const ProgressCallback& progress) { SetProgressCallback(progress); } void Session::SetOption(const DebugCallback& debug) { SetDebugCallback(debug); } void Session::SetOption(const Url& url) { SetUrl(url); } void Session::SetOption(const Parameters& parameters) { SetParameters(parameters); } void Session::SetOption(Parameters&& parameters) { SetParameters(std::move(parameters)); } void Session::SetOption(const Header& header) { SetHeader(header); } void Session::SetOption(const Timeout& timeout) { SetTimeout(timeout); } void Session::SetOption(const ConnectTimeout& timeout) { SetConnectTimeout(timeout); } void Session::SetOption(const Authentication& auth) { SetAuth(auth); } void Session::SetOption(const LimitRate& limit_rate) { SetLimitRate(limit_rate); } // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 void Session::SetOption(const Bearer& auth) { SetBearer(auth); } #endif void Session::SetOption(const UserAgent& ua) { SetUserAgent(ua); } void Session::SetOption(const Payload& payload) { SetPayload(payload); } void Session::SetOption(Payload&& payload) { SetPayload(std::move(payload)); } void Session::SetOption(const Proxies& proxies) { SetProxies(proxies); } void Session::SetOption(Proxies&& proxies) { SetProxies(std::move(proxies)); } void Session::SetOption(ProxyAuthentication&& proxy_auth) { SetProxyAuth(std::move(proxy_auth)); } void Session::SetOption(const ProxyAuthentication& proxy_auth) { SetProxyAuth(proxy_auth); } void Session::SetOption(const Multipart& multipart) { SetMultipart(multipart); } void Session::SetOption(Multipart&& multipart) { SetMultipart(std::move(multipart)); } void Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); } void Session::SetOption(const Cookies& cookies) { SetCookies(cookies); } void Session::SetOption(const Body& body) { SetBody(body); } void Session::SetOption(Body&& body) { SetBody(std::move(body)); } void Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); } void Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); } void Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); } void Session::SetOption(const UnixSocket& unix_socket) { SetUnixSocket(unix_socket); } void Session::SetOption(const SslOptions& options) { SetSslOptions(options); } void Session::SetOption(const Interface& iface) { SetInterface(iface); } void Session::SetOption(const LocalPort& local_port) { SetLocalPort(local_port); } void Session::SetOption(const LocalPortRange& local_port_range) { SetLocalPortRange(local_port_range); } void Session::SetOption(const HttpVersion& version) { SetHttpVersion(version); } void Session::SetOption(const Range& range) { SetRange(range); } void Session::SetOption(const MultiRange& multi_range) { SetMultiRange(multi_range); } void Session::SetOption(const ReserveSize& reserve_size) { SetReserveSize(reserve_size.size); } void Session::SetOption(const AcceptEncoding& accept_encoding) { SetAcceptEncoding(accept_encoding); } void Session::SetOption(AcceptEncoding&& accept_encoding) { SetAcceptEncoding(std::move(accept_encoding)); } // clang-format on void Session::SetCancellationParam(std::shared_ptr param) { cbs_->cancellationcb_ = CancellationCallback{std::move(param)}; isCancellable = true; #if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0 curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cbs_->cancellationcb_); #else curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction); curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cbs_->cancellationcb_); #endif curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); } } // namespace cpr cpr-1.11.2/cpr/ssl_ctx.cpp000066400000000000000000000072341475657722200153560ustar00rootroot00000000000000 #include "cpr/ssl_ctx.h" #include "cpr/ssl_options.h" #include #include #include #include #include #include #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION #ifdef OPENSSL_BACKEND_USED #include #include #include #include #include #include #include // openssl/types.h was added in later version of openssl and is therefore not always available. // This is for example the case on Ubuntu 20.04. // We try to include it if available to satisfy clang-tidy. // Ref: https://github.com/openssl/openssl/commit/50cd4768c6b89c757645f28519236bb989216f8d #if __has_include() #include #else #include #endif namespace cpr { /** * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL. * If an error is returned from the callback no attempt to establish a connection is made and * the perform operation will return the callback's error code. * * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html */ template struct deleter_from_fn { template constexpr void operator()(T* arg) const { fn(arg); } }; template using custom_unique_ptr = std::unique_ptr>; using x509_ptr = custom_unique_ptr; using bio_ptr = custom_unique_ptr; namespace { inline std::string get_openssl_print_errors() { std::ostringstream oss; ERR_print_errors_cb( [](char const* str, size_t len, void* data) -> int { auto& oss = *static_cast(data); oss << str; return static_cast(len); }, &oss); return oss.str(); } } // namespace CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) { // Check arguments if (raw_cert_buf == nullptr || sslctx == nullptr) { std::cerr << "Invalid callback arguments!\n"; return CURLE_ABORTED_BY_CALLBACK; } // Get a pointer to the current certificate verification storage auto* store = SSL_CTX_get_cert_store(static_cast(sslctx)); // Create a memory BIO using the data of cert_buf. // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen. const bio_ptr bio{BIO_new_mem_buf(static_cast(raw_cert_buf), -1)}; bool at_least_got_one = false; for (;;) { // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use. const x509_ptr x{PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)}; if (x == nullptr) { if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && at_least_got_one) { ERR_clear_error(); break; } std::cerr << "PEM_read_bio_X509_AUX failed: \n" << get_openssl_print_errors() << '\n'; return CURLE_ABORTED_BY_CALLBACK; } // Add the loaded certificate to the verification storage if (X509_STORE_add_cert(store, x.get()) == 0) { std::cerr << "X509_STORE_add_cert failed: \n" << get_openssl_print_errors() << '\n'; return CURLE_ABORTED_BY_CALLBACK; } at_least_got_one = true; } // The CA certificate was loaded successfully into the verification storage return CURLE_OK; } } // namespace cpr #endif // OPENSSL_BACKEND_USED #endif // SUPPORT_CURLOPT_SSL_CTX_FUNCTION cpr-1.11.2/cpr/threadpool.cpp000066400000000000000000000101231475657722200160270ustar00rootroot00000000000000#include "cpr/threadpool.h" #include #include #include #include #include #include #include #include namespace cpr { ThreadPool::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) {} ThreadPool::~ThreadPool() { Stop(); } int ThreadPool::Start(size_t start_threads) { if (status != STOP) { return -1; } status = RUNNING; start_threads = std::clamp(start_threads, min_thread_num, max_thread_num); for (size_t i = 0; i < start_threads; ++i) { CreateThread(); } return 0; } int ThreadPool::Stop() { const std::unique_lock status_lock(status_wait_mutex); if (status == STOP) { return -1; } status = STOP; status_wait_cond.notify_all(); task_cond.notify_all(); for (auto& i : threads) { if (i.thread->joinable()) { i.thread->join(); } } threads.clear(); cur_thread_num = 0; idle_thread_num = 0; return 0; } int ThreadPool::Pause() { if (status == RUNNING) { status = PAUSE; } return 0; } int ThreadPool::Resume() { const std::unique_lock status_lock(status_wait_mutex); if (status == PAUSE) { status = RUNNING; status_wait_cond.notify_all(); } return 0; } int ThreadPool::Wait() { while (true) { if (status == STOP || (tasks.empty() && idle_thread_num == cur_thread_num)) { break; } std::this_thread::yield(); } return 0; } bool ThreadPool::CreateThread() { if (cur_thread_num >= max_thread_num) { return false; } std::thread* thread = new std::thread([this] { bool initialRun = true; while (status != STOP) { { std::unique_lock status_lock(status_wait_mutex); status_wait_cond.wait(status_lock, [this]() { return status != Status::PAUSE; }); } Task task; { std::unique_lock locker(task_mutex); task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() { return status == STOP || !tasks.empty(); }); if (status == STOP) { return; } if (tasks.empty()) { if (cur_thread_num > min_thread_num) { DelThread(std::this_thread::get_id()); return; } continue; } if (!initialRun) { --idle_thread_num; } task = std::move(tasks.front()); tasks.pop(); } if (task) { task(); ++idle_thread_num; initialRun = false; } } }); AddThread(thread); return true; } void ThreadPool::AddThread(std::thread* thread) { thread_mutex.lock(); ++cur_thread_num; ThreadData data; data.thread = std::shared_ptr(thread); data.id = thread->get_id(); data.status = RUNNING; data.start_time = std::chrono::steady_clock::now(); data.stop_time = std::chrono::steady_clock::time_point::max(); threads.emplace_back(data); thread_mutex.unlock(); } void ThreadPool::DelThread(std::thread::id id) { const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); thread_mutex.lock(); --cur_thread_num; --idle_thread_num; auto iter = threads.begin(); while (iter != threads.end()) { if (iter->status == STOP && now > iter->stop_time) { if (iter->thread->joinable()) { iter->thread->join(); iter = threads.erase(iter); continue; } } else if (iter->id == id) { iter->status = STOP; iter->stop_time = std::chrono::steady_clock::now(); } ++iter; } thread_mutex.unlock(); } } // namespace cpr cpr-1.11.2/cpr/timeout.cpp000066400000000000000000000022241475657722200153570ustar00rootroot00000000000000#include "cpr/timeout.h" #include #include #include #include #include namespace cpr { // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) long Timeout::Milliseconds() const { static_assert(std::is_same_v, "Following casting expects milliseconds."); // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) if (ms.count() > static_cast(std::numeric_limits::max())) { throw std::overflow_error("cpr::Timeout: timeout value overflow: " + std::to_string(ms.count()) + " ms."); } // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) if (ms.count() < static_cast(std::numeric_limits::min())) { throw std::underflow_error("cpr::Timeout: timeout value underflow: " + std::to_string(ms.count()) + " ms."); } // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) return static_cast(ms.count()); } } // namespace cpr cpr-1.11.2/cpr/unix_socket.cpp000066400000000000000000000002431475657722200162230ustar00rootroot00000000000000 #include "cpr/unix_socket.h" namespace cpr { const char* UnixSocket::GetUnixSocketString() const noexcept { return unix_socket_.data(); } } // namespace cpr cpr-1.11.2/cpr/util.cpp000066400000000000000000000200511475657722200146440ustar00rootroot00000000000000#include "cpr/util.h" #include "cpr/callback.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_Win32) #include #else #ifdef __clang__ #pragma clang diagnostic push #if __has_warning("-Wreserved-macro-identifier") // Not all versions of clang support this flag like the one used on Ubuntu 18.04 #pragma clang diagnostic ignored "-Wreserved-macro-identifier" #endif #pragma clang diagnostic ignored "-Wunused-macros" #endif // https://en.cppreference.com/w/c/string/byte/memset // NOLINTNEXTLINE(bugprone-reserved-identifier, cert-dcl37-c, cert-dcl51-cpp, cppcoreguidelines-macro-usage) #define __STDC_WANT_LIB_EXT1__ 1 #ifdef __clang__ #pragma clang diagnostic pop #endif #include #endif namespace cpr::util { enum class CurlHTTPCookieField : uint8_t { Domain = 0, IncludeSubdomains, Path, HttpsOnly, Expires, Name, Value, }; Cookies parseCookies(curl_slist* raw_cookies) { const int CURL_HTTP_COOKIE_SIZE = static_cast(CurlHTTPCookieField::Value) + 1; Cookies cookies; for (curl_slist* nc = raw_cookies; nc; nc = nc->next) { std::vector tokens = cpr::util::split(nc->data, '\t'); while (tokens.size() < CURL_HTTP_COOKIE_SIZE) { tokens.emplace_back(""); } const std::time_t expires = sTimestampToT(tokens.at(static_cast(CurlHTTPCookieField::Expires))); cookies.emplace_back(Cookie{ tokens.at(static_cast(CurlHTTPCookieField::Name)), tokens.at(static_cast(CurlHTTPCookieField::Value)), tokens.at(static_cast(CurlHTTPCookieField::Domain)), isTrue(tokens.at(static_cast(CurlHTTPCookieField::IncludeSubdomains))), tokens.at(static_cast(CurlHTTPCookieField::Path)), isTrue(tokens.at(static_cast(CurlHTTPCookieField::HttpsOnly))), std::chrono::system_clock::from_time_t(expires), }); } return cookies; } Header parseHeader(const std::string& headers, std::string* status_line, std::string* reason) { Header header; std::istringstream stream(headers); std::string line; while (std::getline(stream, line, '\n')) { if (line.substr(0, 5) == "HTTP/") { // set the status_line if it was given if ((status_line != nullptr) || (reason != nullptr)) { line.resize(std::min(line.size(), line.find_last_not_of("\t\n\r ") + 1)); if (status_line != nullptr) { *status_line = line; } // set the reason if it was given if (reason != nullptr) { const size_t pos1 = line.find_first_of("\t "); size_t pos2 = std::string::npos; if (pos1 != std::string::npos) { pos2 = line.find_first_of("\t ", pos1 + 1); } if (pos2 != std::string::npos) { line.erase(0, pos2 + 1); *reason = line; } } } header.clear(); } if (!line.empty()) { const size_t found = line.find(':'); if (found != std::string::npos) { std::string value = line.substr(found + 1); value.erase(0, value.find_first_not_of("\t ")); value.resize(std::min(value.size(), value.find_last_not_of("\t\n\r ") + 1)); header[line.substr(0, found)] = value; } } } return header; } std::vector split(const std::string& to_split, char delimiter) { std::vector tokens; std::stringstream stream(to_split); std::string item; while (std::getline(stream, item, delimiter)) { tokens.push_back(item); } return tokens; } size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read) { size *= nitems; return (*read)(ptr, size) ? size : CURL_READFUNC_ABORT; } size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header) { size *= nmemb; return (*header)({ptr, size}) ? size : 0; } size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data) { size *= nmemb; data->append(ptr, size); return size; } size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file) { size *= nmemb; file->write(ptr, static_cast(size)); return size; } size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write) { size *= nmemb; return (*write)({ptr, size}) ? size : 0; } int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, const DebugCallback* debug) { (*debug)(static_cast(type), std::string(data, size)); return 0; } /** * Creates a temporary CurlHolder object and uses it to escape the given string. * If you plan to use this methode on a regular basis think about creating a CurlHolder * object and calling urlEncode(std::string) on it. * * Example: * CurlHolder holder; * std::string input = "Hello World!"; * std::string result = holder.urlEncode(input); **/ std::string urlEncode(const std::string& s) { const CurlHolder holder; // Create a temporary new holder for URL encoding return holder.urlEncode(s); } /** * Creates a temporary CurlHolder object and uses it to unescape the given string. * If you plan to use this methode on a regular basis think about creating a CurlHolder * object and calling urlDecode(std::string) on it. * * Example: * CurlHolder holder; * std::string input = "Hello%20World%21"; * std::string result = holder.urlDecode(input); **/ std::string urlDecode(const std::string& s) { const CurlHolder holder; // Create a temporary new holder for URL decoding return holder.urlDecode(s); } #if defined(__STDC_LIB_EXT1__) void secureStringClear(std::string& s) { if (s.empty()) { return; } memset_s(&s.front(), s.length(), 0, s.length()); s.clear(); } #elif defined(_WIN32) void secureStringClear(std::string& s) { if (s.empty()) { return; } SecureZeroMemory(&s.front(), s.length()); s.clear(); } #else #if defined(__clang__) #pragma clang optimize off // clang #elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__) #pragma GCC push_options // g++ #pragma GCC optimize("O0") // g++ #endif void secureStringClear(std::string& s) { if (s.empty()) { return; } // NOLINTNEXTLINE (readability-container-data-pointer) char* ptr = &(s[0]); memset(ptr, '\0', s.length()); s.clear(); } #if defined(__clang__) #pragma clang optimize on // clang #elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__) #pragma GCC pop_options // g++ #endif #endif bool isTrue(const std::string& s) { std::string temp_string{s}; std::transform(temp_string.begin(), temp_string.end(), temp_string.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return temp_string == "true"; } time_t sTimestampToT(const std::string& st) { // NOLINTNEXTLINE(google-runtime-int) if (std::is_same_v) { return static_cast(std::stoul(st)); } // NOLINTNEXTLINE(google-runtime-int) if (std::is_same_v) { return static_cast(std::stoull(st)); } if (std::is_same_v) { return static_cast(std::stoi(st)); } // NOLINTNEXTLINE(google-runtime-int) if (std::is_same_v) { return static_cast(std::stol(st)); } return static_cast(std::stoll(st)); } } // namespace cpr::util cpr-1.11.2/include/000077500000000000000000000000001475657722200140245ustar00rootroot00000000000000cpr-1.11.2/include/CMakeLists.txt000066400000000000000000000053141475657722200165670ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) target_include_directories(cpr PUBLIC $ $ $) target_sources(cpr PRIVATE # Header files (useful in IDEs) cpr/accept_encoding.h cpr/api.h cpr/async.h cpr/async_wrapper.h cpr/auth.h cpr/bearer.h cpr/body.h cpr/buffer.h cpr/cert_info.h cpr/cookies.h cpr/cpr.h cpr/cprtypes.h cpr/curlholder.h cpr/curlholder.h cpr/error.h cpr/file.h cpr/limit_rate.h cpr/local_port.h cpr/local_port_range.h cpr/multipart.h cpr/parameters.h cpr/payload.h cpr/proxies.h cpr/proxyauth.h cpr/response.h cpr/session.h cpr/singleton.h cpr/ssl_ctx.h cpr/ssl_options.h cpr/threadpool.h cpr/timeout.h cpr/unix_socket.h cpr/util.h cpr/verbose.h cpr/interface.h cpr/redirect.h cpr/http_version.h cpr/interceptor.h cpr/filesystem.h cpr/curlmultiholder.h cpr/multiperform.h cpr/resolve.h ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h ) # Filesystem if(CPR_USE_BOOST_FILESYSTEM) find_package(Boost 1.77 REQUIRED COMPONENTS filesystem) if(Boost_FOUND) target_link_libraries(cpr PUBLIC Boost::filesystem) endif() else() try_compile( STD_FS_SUPPORTED "${CMAKE_CURRENT_BINARY_DIR}/std_fs" SOURCES "${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp" CXX_STANDARD ${CMAKE_CXX_STANDARD} CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) if(NOT STD_FS_SUPPORTED) try_compile( STDCXXFS_SUPPORTED "${CMAKE_CURRENT_BINARY_DIR}/stdcxxfs" SOURCES "${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp" LINK_LIBRARIES "stdc++fs" CXX_STANDARD ${CMAKE_CXX_STANDARD} CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) if(STDCXXFS_SUPPORTED) target_link_libraries(cpr PUBLIC stdc++fs) else() try_compile( CXXFS_SUPPORTED "${CMAKE_CURRENT_BINARY_DIR}/cxxfs" SOURCES "${PROJECT_SOURCE_DIR}/cmake/std_fs_support_test.cpp" LINK_LIBRARIES "c++fs" CXX_STANDARD ${CMAKE_CXX_STANDARD} CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) if(CXXFS_SUPPORTED) target_link_libraries(cpr PUBLIC c++fs) else() message(FATAL_ERROR "Your compiler does not support the `` include or cpr is unable to determine the proper compiler flags for it.") endif() endif() endif() endif() install(DIRECTORY cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) cpr-1.11.2/include/cpr/000077500000000000000000000000001475657722200146105ustar00rootroot00000000000000cpr-1.11.2/include/cpr/accept_encoding.h000066400000000000000000000022411475657722200200650ustar00rootroot00000000000000#ifndef CPR_ACCEPT_ENCODING_H #define CPR_ACCEPT_ENCODING_H #include #include #include #include #include namespace cpr { enum class AcceptEncodingMethods { identity, deflate, zlib, gzip, disabled, }; // NOLINTNEXTLINE(cert-err58-cpp) static const std::map AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}, {AcceptEncodingMethods::disabled, "disabled"}}; class AcceptEncoding { public: AcceptEncoding() = default; // NOLINTNEXTLINE(google-explicit-constructor) AcceptEncoding(const std::initializer_list& methods); // NOLINTNEXTLINE(google-explicit-constructor) AcceptEncoding(const std::initializer_list& methods); [[nodiscard]] bool empty() const noexcept; [[nodiscard]] const std::string getString() const; [[nodiscard]] bool disabled() const; private: std::unordered_set methods_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/api.h000066400000000000000000000312121475657722200155310ustar00rootroot00000000000000#ifndef CPR_API_H #define CPR_API_H #include #include #include #include #include #include "cpr/async.h" #include "cpr/async_wrapper.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/cprtypes.h" #include "cpr/filesystem.h" #include "cpr/multipart.h" #include "cpr/multiperform.h" #include "cpr/payload.h" #include "cpr/response.h" #include "cpr/session.h" namespace cpr { using AsyncResponse = AsyncWrapper; namespace priv { template void set_option_internal(Session& session, CurrentType&& current_option) { session.SetOption(std::forward(current_option)); } template <> inline void set_option_internal(Session& session, Header&& current_option) { // Header option was already provided -> Update previous header session.UpdateHeader(std::forward
(current_option)); } template void set_option_internal(Session& session, CurrentType&& current_option, Ts&&... ts) { set_option_internal(session, std::forward(current_option)); if (std::is_same_v) { set_option_internal(session, std::forward(ts)...); } else { set_option_internal(session, std::forward(ts)...); } } template void set_option(Session& session, Ts&&... ts) { set_option_internal(session, std::forward(ts)...); } // Idea: https://stackoverflow.com/a/19060157 template void apply_set_option_internal(Session& session, Tuple&& t, std::index_sequence) { set_option(session, std::get(std::forward(t))...); } // Idea: https://stackoverflow.com/a/19060157 template void apply_set_option(Session& session, Tuple&& t) { using Indices = std::make_index_sequence>::value>; apply_set_option_internal(session, std::forward(t), Indices()); } template void setup_multiperform_internal(MultiPerform& multiperform, T&& t) { std::shared_ptr session = std::make_shared(); apply_set_option(*session, t); multiperform.AddSession(session); } template void setup_multiperform_internal(MultiPerform& multiperform, T&& t, Ts&&... ts) { std::shared_ptr session = std::make_shared(); apply_set_option(*session, t); multiperform.AddSession(session); setup_multiperform_internal(multiperform, std::forward(ts)...); } template void setup_multiperform(MultiPerform& multiperform, Ts&&... ts) { setup_multiperform_internal(multiperform, std::forward(ts)...); } using session_action_t = cpr::Response (cpr::Session::*)(); template void setup_multiasync(std::vector>& responses, T&& parameters) { std::shared_ptr cancellation_state = std::make_shared(false); std::function execFn{[cancellation_state](T params) { if (cancellation_state->load()) { return Response{}; } cpr::Session s{}; s.SetCancellationParam(cancellation_state); apply_set_option(s, std::forward(params)); return std::invoke(SessionAction, s); }}; responses.emplace_back(GlobalThreadPool::GetInstance()->Submit(std::move(execFn), std::forward(parameters)), std::move(cancellation_state)); } template void setup_multiasync(std::vector>& responses, T&& head, Ts&&... tail) { setup_multiasync(responses, std::forward(head)); if constexpr (sizeof...(Ts) > 0) { setup_multiasync(responses, std::forward(tail)...); } } } // namespace priv // Get methods template Response Get(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Get(); } // Get async methods template AsyncResponse GetAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...); } // Get callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto GetCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Post methods template Response Post(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Post(); } // Post async methods template AsyncResponse PostAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...); } // Post callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PostCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Put methods template Response Put(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Put(); } // Put async methods template AsyncResponse PutAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...); } // Put callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PutCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Head methods template Response Head(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Head(); } // Head async methods template AsyncResponse HeadAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...); } // Head callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto HeadCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Delete methods template Response Delete(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Delete(); } // Delete async methods template AsyncResponse DeleteAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...); } // Delete callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto DeleteCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Options methods template Response Options(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Options(); } // Options async methods template AsyncResponse OptionsAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...); } // Options callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto OptionsCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Patch methods template Response Patch(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Patch(); } // Patch async methods template AsyncResponse PatchAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...); } // Patch callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PatchCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Download methods template Response Download(std::ofstream& file, Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Download(file); } // Download async method template AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) { return AsyncWrapper{std::async( std::launch::async, [](fs::path local_path_, Ts... ts_) { std::ofstream f(local_path_.c_str()); return Download(f, std::move(ts_)...); }, std::move(local_path), std::move(ts)...)}; } // Download with user callback template Response Download(const WriteCallback& write, Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); return session.Download(write); } // Multi requests template std::vector MultiGet(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Get(); } template std::vector MultiDelete(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Delete(); } template std::vector MultiPut(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Put(); } template std::vector MultiHead(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Head(); } template std::vector MultiOptions(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Options(); } template std::vector MultiPatch(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Patch(); } template std::vector MultiPost(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Post(); } template std::vector> MultiGetAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Get>(ret, std::forward(ts)...); return ret; } template std::vector> MultiDeleteAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Delete>(ret, std::forward(ts)...); return ret; } template std::vector> MultiHeadAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Head>(ret, std::forward(ts)...); return ret; } template std::vector> MultiOptionsAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Options>(ret, std::forward(ts)...); return ret; } template std::vector> MultiPatchAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Patch>(ret, std::forward(ts)...); return ret; } template std::vector> MultiPostAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Post>(ret, std::forward(ts)...); return ret; } template std::vector> MultiPutAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Put>(ret, std::forward(ts)...); return ret; } } // namespace cpr #endif cpr-1.11.2/include/cpr/async.h000066400000000000000000000025511475657722200161010ustar00rootroot00000000000000#ifndef CPR_ASYNC_H #define CPR_ASYNC_H #include "async_wrapper.h" #include "singleton.h" #include "threadpool.h" namespace cpr { class GlobalThreadPool : public ThreadPool { CPR_SINGLETON_DECL(GlobalThreadPool) protected: GlobalThreadPool() = default; public: ~GlobalThreadPool() override = default; }; /** * Return a wrapper for a future, calling future.get() will wait until the task is done and return RetType. * async(fn, args...) * async(std::bind(&Class::mem_fn, &obj)) * async(std::mem_fn(&Class::mem_fn, &obj)) **/ template auto async(Fn&& fn, Args&&... args) { return AsyncWrapper{GlobalThreadPool::GetInstance()->Submit(std::forward(fn), std::forward(args)...)}; } class async { public: 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) { GlobalThreadPool* gtp = GlobalThreadPool::GetInstance(); if (gtp->IsStarted()) { return; } gtp->SetMinThreadNum(min_threads); gtp->SetMaxThreadNum(max_threads); gtp->SetMaxIdleTime(max_idle_ms); gtp->Start(); } static void cleanup() { GlobalThreadPool::ExitInstance(); } }; } // namespace cpr #endif cpr-1.11.2/include/cpr/async_wrapper.h000066400000000000000000000106741475657722200176460ustar00rootroot00000000000000#ifndef CPR_ASYNC_WRAPPER_H #define CPR_ASYNC_WRAPPER_H #include #include #include #include "cpr/response.h" namespace cpr { enum class [[nodiscard]] CancellationResult{failure, success, invalid_operation}; /** * A class template intended to wrap results of async operations (instances of std::future) * and also provide extended capablilities relaed to these requests, for example cancellation. * * The RAII semantics are the same as std::future - moveable, not copyable. */ template class AsyncWrapper { private: std::future future; std::shared_ptr is_cancelled; public: // Constructors explicit AsyncWrapper(std::future&& f) : future{std::move(f)} {} AsyncWrapper(std::future&& f, std::shared_ptr&& cancelledState) : future{std::move(f)}, is_cancelled{std::move(cancelledState)} {} // Copy Semantics AsyncWrapper(const AsyncWrapper&) = delete; AsyncWrapper& operator=(const AsyncWrapper&) = delete; // Move Semantics AsyncWrapper(AsyncWrapper&&) noexcept = default; AsyncWrapper& operator=(AsyncWrapper&&) noexcept = default; // Destructor ~AsyncWrapper() { if constexpr (isCancellable) { if (is_cancelled) { is_cancelled->store(true); } } } // These methods replicate the behaviour of std::future [[nodiscard]] T get() { if constexpr (isCancellable) { if (IsCancelled()) { throw std::logic_error{"Calling AsyncWrapper::get on a cancelled request!"}; } } if (!future.valid()) { throw std::logic_error{"Calling AsyncWrapper::get when the associated future instance is invalid!"}; } return future.get(); } [[nodiscard]] bool valid() const noexcept { if constexpr (isCancellable) { return !is_cancelled->load() && future.valid(); } else { return future.valid(); } } void wait() const { if constexpr (isCancellable) { if (is_cancelled->load()) { throw std::logic_error{"Calling AsyncWrapper::wait when the associated future is invalid or cancelled!"}; } } if (!future.valid()) { throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; } future.wait(); } template std::future_status wait_for(const std::chrono::duration& timeout_duration) const { if constexpr (isCancellable) { if (IsCancelled()) { throw std::logic_error{"Calling AsyncWrapper::wait_for when the associated future is cancelled!"}; } } if (!future.valid()) { throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; } return future.wait_for(timeout_duration); } template std::future_status wait_until(const std::chrono::time_point& timeout_time) const { if constexpr (isCancellable) { if (IsCancelled()) { throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is cancelled!"}; } } if (!future.valid()) { throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; } return future.wait_until(timeout_time); } std::shared_future share() noexcept { return future.share(); } // Cancellation-related methods CancellationResult Cancel() { if constexpr (!isCancellable) { return CancellationResult::invalid_operation; } if (!future.valid() || is_cancelled->load()) { return CancellationResult::invalid_operation; } is_cancelled->store(true); return CancellationResult::success; } [[nodiscard]] bool IsCancelled() const { if constexpr (isCancellable) { return is_cancelled->load(); } else { return false; } } }; // Deduction guides template AsyncWrapper(std::future&&) -> AsyncWrapper; template AsyncWrapper(std::future&&, std::shared_ptr&&) -> AsyncWrapper; } // namespace cpr #endif cpr-1.11.2/include/cpr/auth.h000066400000000000000000000011721475657722200157230ustar00rootroot00000000000000#ifndef CPR_AUTH_H #define CPR_AUTH_H #include #include namespace cpr { enum class AuthMode { BASIC, DIGEST, NTLM, NEGOTIATE }; class Authentication { public: Authentication(std::string_view username, std::string_view password, AuthMode auth_mode); Authentication(const Authentication& other) = default; ~Authentication() noexcept; Authentication& operator=(const Authentication& other) = default; const char* GetAuthString() const noexcept; AuthMode GetAuthMode() const noexcept; private: std::string auth_string_; AuthMode auth_mode_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/bearer.h000066400000000000000000000014431475657722200162230ustar00rootroot00000000000000#ifndef CPR_BEARER_H #define CPR_BEARER_H #include #include #include namespace cpr { // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 class Bearer { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Bearer(std::string token) : token_string_{std::move(token)} {} Bearer(const Bearer& other) = default; Bearer(Bearer&& old) noexcept = default; virtual ~Bearer() noexcept; Bearer& operator=(Bearer&& old) noexcept = default; Bearer& operator=(const Bearer& other) = default; virtual const char* GetToken() const noexcept; protected: std::string token_string_; }; #endif } // namespace cpr #endif cpr-1.11.2/include/cpr/body.h000066400000000000000000000035661475657722200157300ustar00rootroot00000000000000#ifndef CPR_BODY_H #define CPR_BODY_H #include #include #include #include #include #include "cpr/buffer.h" #include "cpr/cprtypes.h" #include "cpr/file.h" namespace cpr { class Body : public StringHolder { public: Body() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Body(std::string body) : StringHolder(std::move(body)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Body(std::string_view body) : StringHolder(body) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Body(const char* body) : StringHolder(body) {} Body(const char* str, size_t len) : StringHolder(str, len) {} Body(const std::initializer_list args) : StringHolder(args) {} // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-pro-type-reinterpret-cast) Body(const Buffer& buffer) : StringHolder(reinterpret_cast(buffer.data), static_cast(buffer.datalen)) {} // NOLINTNEXTLINE(google-explicit-constructor) Body(const File& file) { std::ifstream is(file.filepath, std::ifstream::binary); if (!is) { throw std::invalid_argument("Can't open the file for HTTP request body!"); } is.seekg(0, std::ios::end); const std::streampos length = is.tellg(); is.seekg(0, std::ios::beg); std::string buffer; buffer.resize(static_cast(length)); is.read(buffer.data(), length); str_ = std::move(buffer); } Body(const Body& other) = default; Body(Body&& old) noexcept = default; ~Body() override = default; Body& operator=(Body&& old) noexcept = default; Body& operator=(const Body& other) = default; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/buffer.h000066400000000000000000000021151475657722200162310ustar00rootroot00000000000000#ifndef CPR_BUFFER_H #define CPR_BUFFER_H #include #include "cpr/filesystem.h" namespace cpr { struct Buffer { using data_t = const char*; template Buffer(Iterator begin, Iterator end, fs::path&& p_filename) // Ignored here since libcurl reqires a long. // There is also no way around the reinterpret_cast. // NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast) : data{reinterpret_cast(&(*begin))}, datalen{static_cast(std::distance(begin, end))}, filename(std::move(p_filename)) { is_random_access_iterator(begin, end); static_assert(sizeof(*begin) == 1, "Only byte buffers can be used"); } template typename std::enable_if::iterator_category, std::random_access_iterator_tag>::value>::type is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {} data_t data; size_t datalen; const fs::path filename; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/callback.h000066400000000000000000000105341475657722200165200ustar00rootroot00000000000000#ifndef CPR_CALLBACK_H #define CPR_CALLBACK_H #include "cprtypes.h" #include #include #include #include #include namespace cpr { class ReadCallback { public: ReadCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ReadCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {} ReadCallback(cpr_off_t p_size, std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{p_size}, callback{std::move(p_callback)} {} bool operator()(char* buffer, size_t& buffer_size) const { return callback(buffer, buffer_size, userdata); } intptr_t userdata{}; cpr_off_t size{}; std::function callback; }; class HeaderCallback { public: HeaderCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) HeaderCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} bool operator()(const std::string_view& header) const { return callback(header, userdata); } intptr_t userdata{}; std::function callback; }; class WriteCallback { public: WriteCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) WriteCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} bool operator()(const std::string_view& data) const { return callback(data, userdata); } intptr_t userdata{}; std::function callback; }; class ProgressCallback { public: ProgressCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ProgressCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} bool operator()(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow) const { return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata); } intptr_t userdata{}; std::function callback; }; class DebugCallback { public: enum class InfoType { TEXT = 0, HEADER_IN = 1, HEADER_OUT = 2, DATA_IN = 3, DATA_OUT = 4, SSL_DATA_IN = 5, SSL_DATA_OUT = 6, }; DebugCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) DebugCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} void operator()(InfoType type, std::string data) const { callback(type, std::move(data), userdata); } intptr_t userdata{}; std::function callback; }; /** * Functor class for progress functions that will be used in cancellable requests. */ class CancellationCallback { public: CancellationCallback() = default; explicit CancellationCallback(std::shared_ptr&& cs) : cancellation_state{std::move(cs)} {} CancellationCallback(std::shared_ptr&& cs, ProgressCallback& u_cb) : cancellation_state{std::move(cs)}, user_cb{std::reference_wrapper{u_cb}} {} bool operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const; void SetProgressCallback(ProgressCallback& u_cb); private: std::shared_ptr cancellation_state; std::optional> user_cb; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/cert_info.h000066400000000000000000000016531475657722200167360ustar00rootroot00000000000000#ifndef CPR_CERT_INFO_H #define CPR_CERT_INFO_H #include #include #include namespace cpr { class CertInfo { private: std::vector cert_info_; public: CertInfo() = default; CertInfo(const CertInfo& other) = default; CertInfo(CertInfo&& old) = default; CertInfo(const std::initializer_list& entry) : cert_info_{entry} {} ~CertInfo() noexcept = default; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; std::string& operator[](const size_t& pos); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; void emplace_back(const std::string& str); void push_back(const std::string& str); void pop_back(); }; } // namespace cpr #endif cpr-1.11.2/include/cpr/connect_timeout.h000066400000000000000000000007741475657722200201700ustar00rootroot00000000000000#ifndef CPR_CONNECT_TIMEOUT_H #define CPR_CONNECT_TIMEOUT_H #include "cpr/timeout.h" namespace cpr { class ConnectTimeout : public Timeout { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ConnectTimeout(const std::chrono::milliseconds& duration) : Timeout{duration} {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ConnectTimeout(const std::int32_t& milliseconds) : Timeout{milliseconds} {} }; } // namespace cpr #endif cpr-1.11.2/include/cpr/cookies.h000066400000000000000000000071011475657722200164140ustar00rootroot00000000000000#ifndef CPR_COOKIES_H #define CPR_COOKIES_H #include "cpr/curlholder.h" #include #include #include #include namespace cpr { /** * EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable. * To be used for "std::array", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value. **/ static const std::size_t EXPIRES_STRING_SIZE = 100; class Cookie { public: Cookie() = default; /** * Some notes for the default value used by expires: * std::chrono::system_clock::time_point::min() won't work on Windows due to the min, max clash there. * So we fall back to std::chrono::system_clock::from_time_t(0) for the minimum value here. **/ 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} {} [[nodiscard]] const std::string& GetDomain() const; [[nodiscard]] bool IsIncludingSubdomains() const; [[nodiscard]] const std::string& GetPath() const; [[nodiscard]] bool IsHttpsOnly() const; [[nodiscard]] std::chrono::system_clock::time_point GetExpires() const; [[nodiscard]] std::string GetExpiresString() const; [[nodiscard]] const std::string& GetName() const; [[nodiscard]] const std::string& GetValue() const; private: std::string name_; std::string value_; std::string domain_; bool includeSubdomains_{}; std::string path_; bool httpsOnly_{}; /** * TODO: Update the implementation using `std::chrono::utc_clock` of C++20 **/ std::chrono::system_clock::time_point expires_{}; }; class Cookies { public: /** * Should we URL-encode cookies when making a request. * Based on RFC6265, it is recommended but not mandatory to encode cookies. * * ------- * To maximize compatibility with user agents, servers that wish to * store arbitrary data in a cookie-value SHOULD encode that data, for * example, using Base64 [RFC4648]. * ------- * Source: RFC6265 (https://www.ietf.org/rfc/rfc6265.txt) **/ bool encode{true}; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Cookies(bool p_encode = true) : encode{p_encode} {} Cookies(const std::initializer_list& cookies, bool p_encode = true) : encode{p_encode}, cookies_{cookies} {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Cookies(const cpr::Cookie& cookie, bool p_encode = true) : encode{p_encode}, cookies_{cookie} {} cpr::Cookie& operator[](size_t pos); [[nodiscard]] std::string GetEncoded(const CurlHolder& holder) const; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; iterator begin(); iterator end(); [[nodiscard]] const_iterator begin() const; [[nodiscard]] const_iterator end() const; [[nodiscard]] const_iterator cbegin() const; [[nodiscard]] const_iterator cend() const; void emplace_back(const Cookie& str); [[nodiscard]] bool empty() const; void push_back(const Cookie& str); void pop_back(); private: std::vector cookies_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/cpr.h000066400000000000000000000021661475657722200155520ustar00rootroot00000000000000#ifndef CPR_CPR_H #define CPR_CPR_H #include "cpr/api.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/callback.h" #include "cpr/cert_info.h" #include "cpr/connect_timeout.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/cprver.h" #include "cpr/curl_container.h" #include "cpr/curlholder.h" #include "cpr/error.h" #include "cpr/http_version.h" #include "cpr/interceptor.h" #include "cpr/interface.h" #include "cpr/limit_rate.h" #include "cpr/local_port.h" #include "cpr/local_port_range.h" #include "cpr/low_speed.h" #include "cpr/multipart.h" #include "cpr/multiperform.h" #include "cpr/parameters.h" #include "cpr/payload.h" #include "cpr/proxies.h" #include "cpr/proxyauth.h" #include "cpr/range.h" #include "cpr/redirect.h" #include "cpr/reserve_size.h" #include "cpr/resolve.h" #include "cpr/response.h" #include "cpr/session.h" #include "cpr/ssl_ctx.h" #include "cpr/ssl_options.h" #include "cpr/status_codes.h" #include "cpr/timeout.h" #include "cpr/unix_socket.h" #include "cpr/user_agent.h" #include "cpr/util.h" #include "cpr/verbose.h" #define CPR_LIBCURL_VERSION_NUM LIBCURL_VERSION_NUM #endif cpr-1.11.2/include/cpr/cprtypes.h000066400000000000000000000075441475657722200166440ustar00rootroot00000000000000#ifndef CPR_CPR_TYPES_H #define CPR_CPR_TYPES_H #include #include #include #include #include #include #include #include namespace cpr { /** * Wrapper around "curl_off_t" to prevent applications from having to link against libcurl. **/ using cpr_off_t = curl_off_t; /** * The argument type for progress functions, dependent on libcurl version **/ #if LIBCURL_VERSION_NUM < 0x072000 using cpr_pf_arg_t = double; #else using cpr_pf_arg_t = cpr_off_t; #endif template class StringHolder { public: StringHolder() = default; explicit StringHolder(std::string str) : str_(std::move(str)) {} explicit StringHolder(std::string_view str) : str_(str) {} explicit StringHolder(const char* str) : str_(str) {} StringHolder(const char* str, size_t len) : str_(str, len) {} StringHolder(const std::initializer_list args) { str_ = std::accumulate(args.begin(), args.end(), str_); } StringHolder(const StringHolder& other) = default; StringHolder(StringHolder&& old) noexcept = default; virtual ~StringHolder() = default; StringHolder& operator=(StringHolder&& old) noexcept = default; StringHolder& operator=(const StringHolder& other) = default; explicit operator std::string() const { return str_; } T operator+(const char* rhs) const { return T(str_ + rhs); } T operator+(const std::string& rhs) const { return T(str_ + rhs); } T operator+(const StringHolder& rhs) const { return T(str_ + rhs.str_); } void operator+=(const char* rhs) { str_ += rhs; } void operator+=(const std::string& rhs) { str_ += rhs; } void operator+=(const StringHolder& rhs) { str_ += rhs; } bool operator==(const char* rhs) const { return str_ == rhs; } bool operator==(const std::string& rhs) const { return str_ == rhs; } bool operator==(const StringHolder& rhs) const { return str_ == rhs.str_; } bool operator!=(const char* rhs) const { return str_.c_str() != rhs; } bool operator!=(const std::string& rhs) const { return str_ != rhs; } bool operator!=(const StringHolder& rhs) const { return str_ != rhs.str_; } const std::string& str() { return str_; } [[nodiscard]] const std::string& str() const { return str_; } [[nodiscard]] const char* c_str() const { return str_.c_str(); } [[nodiscard]] const char* data() const { return str_.data(); } protected: std::string str_{}; }; template std::ostream& operator<<(std::ostream& os, const StringHolder& s) { os << s.str(); return os; } class Url : public StringHolder { public: Url() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Url(std::string url) : StringHolder(std::move(url)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Url(std::string_view url) : StringHolder(url) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Url(const char* url) : StringHolder(url) {} Url(const char* str, size_t len) : StringHolder(std::string(str, len)) {} Url(const std::initializer_list args) : StringHolder(args) {} Url(const Url& other) = default; Url(Url&& old) noexcept = default; ~Url() override = default; Url& operator=(Url&& old) noexcept = default; Url& operator=(const Url& other) = default; }; struct CaseInsensitiveCompare { bool operator()(const std::string& a, const std::string& b) const noexcept; }; using Header = std::map; } // namespace cpr #endif cpr-1.11.2/include/cpr/curl_container.h000066400000000000000000000017551475657722200200000ustar00rootroot00000000000000#ifndef CURL_CONTAINER_H #define CURL_CONTAINER_H #include #include #include #include #include "cpr/curlholder.h" namespace cpr { struct Parameter { Parameter(std::string p_key, std::string p_value) : key{std::move(p_key)}, value{std::move(p_value)} {} std::string key; std::string value; }; struct Pair { Pair(std::string p_key, std::string p_value) : key(std::move(p_key)), value(std::move(p_value)) {} std::string key; std::string value; }; template class CurlContainer { public: /** * Enables or disables URL encoding for keys and values when calling GetContent(...). **/ bool encode = true; CurlContainer() = default; CurlContainer(const std::initializer_list&); void Add(const std::initializer_list&); void Add(const T&); const std::string GetContent(const CurlHolder&) const; protected: std::vector containerList_; }; } // namespace cpr #endif // cpr-1.11.2/include/cpr/curlholder.h000066400000000000000000000026161475657722200171310ustar00rootroot00000000000000#ifndef CPR_CURL_HOLDER_H #define CPR_CURL_HOLDER_H #include #include #include #include namespace cpr { struct CurlHolder { private: /** * Mutex for curl_easy_init(). * curl_easy_init() is not thread save. * References: * https://curl.haxx.se/libcurl/c/curl_easy_init.html * https://curl.haxx.se/libcurl/c/threadsafe.html **/ // Avoids initalization order problems in a static build static std::mutex& curl_easy_init_mutex_() { static std::mutex curl_easy_init_mutex_; return curl_easy_init_mutex_; } public: CURL* handle{nullptr}; struct curl_slist* chunk{nullptr}; struct curl_slist* resolveCurlList{nullptr}; curl_mime* multipart{nullptr}; std::array error{}; CurlHolder(); CurlHolder(const CurlHolder& other) = default; CurlHolder(CurlHolder&& old) noexcept = default; ~CurlHolder(); CurlHolder& operator=(CurlHolder&& old) noexcept = default; CurlHolder& operator=(const CurlHolder& other) = default; /** * Uses curl_easy_escape(...) for escaping the given string. **/ [[nodiscard]] std::string urlEncode(const std::string& s) const; /** * Uses curl_easy_unescape(...) for unescaping the given string. **/ [[nodiscard]] std::string urlDecode(const std::string& s) const; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/curlmultiholder.h000066400000000000000000000003631475657722200202010ustar00rootroot00000000000000#ifndef CPR_CURLMULTIHOLDER_H #define CPR_CURLMULTIHOLDER_H #include namespace cpr { class CurlMultiHolder { public: CurlMultiHolder(); ~CurlMultiHolder(); CURLM* handle{nullptr}; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/error.h000066400000000000000000000047201475657722200161150ustar00rootroot00000000000000#ifndef CPR_ERROR_H #define CPR_ERROR_H #include #include #include "cpr/cprtypes.h" #include namespace cpr { /** * cpr error codes that match the ones found inside 'curl.h'. * These error codes only include relevant error codes meaning no support for e.g. FTP errors since cpr does only support HTTP. **/ enum class ErrorCode { /** * Everything is good and no error occurred. **/ OK = 0, UNSUPPORTED_PROTOCOL, FAILED_INIT, URL_MALFORMAT, NOT_BUILT_IN, COULDNT_RESOLVE_PROXY, COULDNT_RESOLVE_HOST, COULDNT_CONNECT, WEIRD_SERVER_REPLY, REMOTE_ACCESS_DENIED, HTTP2, PARTIAL_FILE, QUOTE_ERROR, HTTP_RETURNED_ERROR, WRITE_ERROR, UPLOAD_FAILED, READ_ERROR, OUT_OF_MEMORY, OPERATION_TIMEDOUT, RANGE_ERROR, HTTP_POST_ERROR, SSL_CONNECT_ERROR, BAD_DOWNLOAD_RESUME, FILE_COULDNT_READ_FILE, FUNCTION_NOT_FOUND, ABORTED_BY_CALLBACK, BAD_FUNCTION_ARGUMENT, INTERFACE_FAILED, TOO_MANY_REDIRECTS, UNKNOWN_OPTION, SETOPT_OPTION_SYNTAX, GOT_NOTHING, SSL_ENGINE_NOTFOUND, SSL_ENGINE_SETFAILED, SEND_ERROR, RECV_ERROR, SSL_CERTPROBLEM, SSL_CIPHER, PEER_FAILED_VERIFICATION, BAD_CONTENT_ENCODING, FILESIZE_EXCEEDED, USE_SSL_FAILED, SEND_FAIL_REWIND, SSL_ENGINE_INITFAILED, LOGIN_DENIED, SSL_CACERT_BADFILE, SSL_SHUTDOWN_FAILED, AGAIN, SSL_CRL_BADFILE, SSL_ISSUER_ERROR, CHUNK_FAILED, NO_CONNECTION_AVAILABLE, SSL_PINNEDPUBKEYNOTMATCH, SSL_INVALIDCERTSTATUS, HTTP2_STREAM, RECURSIVE_API_CALL, AUTH_ERROR, HTTP3, QUIC_CONNECT_ERROR, PROXY, SSL_CLIENTCERT, UNRECOVERABLE_POLL, TOO_LARGE, /** * An unknown error inside curl occurred. * Please try to reproduce it and then report it to us. * It might be that there is a new curl error code we are not aware yet. * Reporting bugs: https://github.com/libcpr/cpr **/ UNKNOWN_ERROR = 1000, }; class Error { public: ErrorCode code = ErrorCode::OK; std::string message; Error() = default; Error(const std::int32_t& curl_code, std::string&& p_error_message) : code{getErrorCodeForCurlError(curl_code)}, message(std::move(p_error_message)) {} explicit operator bool() const { return code != ErrorCode::OK; } private: static ErrorCode getErrorCodeForCurlError(std::int32_t curl_code); }; } // namespace cpr #endif cpr-1.11.2/include/cpr/file.h000066400000000000000000000027641475657722200157110ustar00rootroot00000000000000#ifndef CPR_FILE_H #define CPR_FILE_H #include #include #include #include "cpr/filesystem.h" namespace cpr { struct File { explicit File(std::string p_filepath, const std::string& p_overriden_filename = {}) : filepath(std::move(p_filepath)), overriden_filename(p_overriden_filename) {} std::string filepath; std::string overriden_filename; [[nodiscard]] bool hasOverridenFilename() const noexcept { return !overriden_filename.empty(); } }; class Files { public: Files() = default; // NOLINTNEXTLINE(google-explicit-constructor) Files(const File& p_file) : files{p_file} {} Files(const Files& other) = default; Files(Files&& old) noexcept = default; Files(const std::initializer_list& p_files) : files{p_files} {} Files(const std::initializer_list& p_filepaths); ~Files() noexcept = default; Files& operator=(const Files& other); Files& operator=(Files&& old) noexcept; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; iterator begin(); iterator end(); [[nodiscard]] const_iterator begin() const; [[nodiscard]] const_iterator end() const; [[nodiscard]] const_iterator cbegin() const; [[nodiscard]] const_iterator cend() const; void emplace_back(const File& file); void push_back(const File& file); void pop_back(); private: std::vector files; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/filesystem.h000066400000000000000000000013661475657722200171530ustar00rootroot00000000000000#ifndef CPR_FILESYSTEM_H #define CPR_FILESYSTEM_H // Include filesystem into the namespace "fs" from either "filesystem" or "experimental/filesystem" or "boost/filesystem" #ifdef CPR_USE_BOOST_FILESYSTEM #define BOOST_FILESYSTEM_VERSION 4 // Use the latest, with the closest behavior to std::filesystem. #include namespace cpr { namespace fs = boost::filesystem; } // cppcheck-suppress preprocessorErrorDirective #elif __has_include() #include namespace cpr { namespace fs = std::filesystem; } #elif __has_include("experimental/filesystem") #include namespace cpr { namespace fs = std::experimental::filesystem; } #else #error "Failed to include header!" #endif #endif cpr-1.11.2/include/cpr/http_version.h000066400000000000000000000032571475657722200175140ustar00rootroot00000000000000#ifndef CPR_HTTP_VERSION_H #define CPR_HTTP_VERSION_H #include namespace cpr { enum class HttpVersionCode { /** * Let libcurl decide which version is the best. **/ VERSION_NONE, /** * Enforce HTTP 1.0 requests. **/ VERSION_1_0, /** * Enforce HTTP 1.1 requests. **/ VERSION_1_1, #if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0 /** * Attempt HTTP 2.0 requests. * Fallback to HTTP 1.1 if negotiation fails. **/ VERSION_2_0, #endif #if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0 /** * Attempt HTTP 2.0 for HTTPS requests only. * Fallback to HTTP 1.1 if negotiation fails. * HTTP 1.1 will be used for HTTP connections. **/ VERSION_2_0_TLS, #endif #if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0 /** * Start HTTP 2.0 for HTTP requests. * Requires prior knowledge that the server supports HTTP 2.0. * For HTTPS requests we will negotiate the protocol version in the TLS handshake. **/ VERSION_2_0_PRIOR_KNOWLEDGE, #endif #if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 /** * Attempt HTTP 3.0 requests. * Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade. * Fallback to HTTP 1.1 if negotiation fails. **/ VERSION_3_0 #endif }; class HttpVersion { public: /** * The HTTP version that should be used by libcurl when initiating a HTTP(S) connection. * Default: HttpVersionCode::VERSION_NONE **/ HttpVersionCode code = HttpVersionCode::VERSION_NONE; HttpVersion() = default; explicit HttpVersion(HttpVersionCode _code) : code(_code) {} }; } // namespace cpr #endif cpr-1.11.2/include/cpr/interceptor.h000066400000000000000000000040571475657722200173250ustar00rootroot00000000000000#ifndef CPR_INTERCEPTOR_H #define CPR_INTERCEPTOR_H #include "cpr/multiperform.h" #include "cpr/response.h" #include "cpr/session.h" #include namespace cpr { class Interceptor { public: enum class ProceedHttpMethod { GET_REQUEST = 0, POST_REQUEST, PUT_REQUEST, DELETE_REQUEST, PATCH_REQUEST, HEAD_REQUEST, OPTIONS_REQUEST, DOWNLOAD_CALLBACK_REQUEST, DOWNLOAD_FILE_REQUEST, }; Interceptor() = default; Interceptor(const Interceptor& other) = default; Interceptor(Interceptor&& old) = default; virtual ~Interceptor() = default; Interceptor& operator=(const Interceptor& other) = default; Interceptor& operator=(Interceptor&& old) = default; virtual Response intercept(Session& session) = 0; protected: static Response proceed(Session& session); static Response proceed(Session& session, ProceedHttpMethod httpMethod); static Response proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file); static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write); }; class InterceptorMulti { public: enum class ProceedHttpMethod { GET_REQUEST = 0, POST_REQUEST, PUT_REQUEST, DELETE_REQUEST, PATCH_REQUEST, HEAD_REQUEST, OPTIONS_REQUEST, DOWNLOAD_CALLBACK_REQUEST, DOWNLOAD_FILE_REQUEST, }; InterceptorMulti() = default; InterceptorMulti(const InterceptorMulti& other) = default; InterceptorMulti(InterceptorMulti&& old) = default; virtual ~InterceptorMulti() = default; InterceptorMulti& operator=(const InterceptorMulti& other) = default; InterceptorMulti& operator=(InterceptorMulti&& old) = default; virtual std::vector intercept(MultiPerform& multi) = 0; protected: static std::vector proceed(MultiPerform& multi); static void PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write); }; } // namespace cpr #endif cpr-1.11.2/include/cpr/interface.h000066400000000000000000000022051475657722200167200ustar00rootroot00000000000000#ifndef CPR_INTERFACE_H #define CPR_INTERFACE_H #include #include #include "cpr/cprtypes.h" namespace cpr { class Interface : public StringHolder { public: Interface() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Interface(std::string iface) : StringHolder(std::move(iface)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Interface(std::string_view iface) : StringHolder(iface) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Interface(const char* iface) : StringHolder(iface) {} Interface(const char* str, size_t len) : StringHolder(str, len) {} Interface(const std::initializer_list args) : StringHolder(args) {} Interface(const Interface& other) = default; Interface(Interface&& old) noexcept = default; ~Interface() override = default; Interface& operator=(Interface&& old) noexcept = default; Interface& operator=(const Interface& other) = default; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/limit_rate.h000066400000000000000000000005101475657722200171060ustar00rootroot00000000000000#ifndef CPR_SPEED_LIMIT_H #define CPR_SPEED_LIMIT_H #include namespace cpr { class LimitRate { public: LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {} std::int64_t downrate = 0; std::int64_t uprate = 0; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/local_port.h000066400000000000000000000006071475657722200171220ustar00rootroot00000000000000#ifndef CPR_LOCAL_PORT_H #define CPR_LOCAL_PORT_H #include namespace cpr { class LocalPort { public: // NOLINTNEXTLINE(google-explicit-constructor) LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {} operator std::uint16_t() const { return localport_; } private: std::uint16_t localport_ = 0; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/local_port_range.h000066400000000000000000000006661475657722200203030ustar00rootroot00000000000000#ifndef CPR_LOCAL_PORT_RANGE_H #define CPR_LOCAL_PORT_RANGE_H #include namespace cpr { class LocalPortRange { public: // NOLINTNEXTLINE(google-explicit-constructor) LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {} operator std::uint16_t() const { return localportrange_; } private: std::uint16_t localportrange_ = 0; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/low_speed.h000066400000000000000000000004461475657722200167460ustar00rootroot00000000000000#ifndef CPR_LOW_SPEED_H #define CPR_LOW_SPEED_H #include namespace cpr { class LowSpeed { public: LowSpeed(const std::int32_t p_limit, const std::int32_t p_time) : limit(p_limit), time(p_time) {} std::int32_t limit; std::int32_t time; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/multipart.h000066400000000000000000000034051475657722200170040ustar00rootroot00000000000000#ifndef CPR_MULTIPART_H #define CPR_MULTIPART_H #include #include #include #include #include #include "buffer.h" #include "file.h" namespace cpr { struct Part { 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} {} 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} {} 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} {} 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{p_files} {} 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} {} std::string name; // We don't use fs::path here, as this leads to problems using windows std::string value; std::string content_type; Buffer::data_t data{nullptr}; size_t datalen{0}; bool is_file; bool is_buffer; Files files; }; class Multipart { public: Multipart(const std::initializer_list& parts); Multipart(const std::vector& parts); Multipart(const std::vector&& parts); std::vector parts; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/multiperform.h000066400000000000000000000120521475657722200175060ustar00rootroot00000000000000#ifndef CPR_MULTIPERFORM_H #define CPR_MULTIPERFORM_H #include "cpr/curlmultiholder.h" #include "cpr/response.h" #include "cpr/session.h" #include #include #include #include #include namespace cpr { class InterceptorMulti; class MultiPerform { public: enum class HttpMethod { UNDEFINED = 0, GET_REQUEST, POST_REQUEST, PUT_REQUEST, DELETE_REQUEST, PATCH_REQUEST, HEAD_REQUEST, OPTIONS_REQUEST, DOWNLOAD_REQUEST, }; MultiPerform(); MultiPerform(const MultiPerform& other) = delete; MultiPerform(MultiPerform&& old) = default; ~MultiPerform(); MultiPerform& operator=(const MultiPerform& other) = delete; MultiPerform& operator=(MultiPerform&& old) noexcept = default; std::vector Get(); std::vector Delete(); template std::vector Download(DownloadArgTypes... args); std::vector Put(); std::vector Head(); std::vector Options(); std::vector Patch(); std::vector Post(); std::vector Perform(); template std::vector PerformDownload(DownloadArgTypes... args); void AddSession(std::shared_ptr& session, HttpMethod method = HttpMethod::UNDEFINED); void RemoveSession(const std::shared_ptr& session); std::vector, HttpMethod>>& GetSessions(); [[nodiscard]] const std::vector, HttpMethod>>& GetSessions() const; void AddInterceptor(const std::shared_ptr& pinterceptor); private: // Interceptors should be able to call the private proceed() and PrepareDownloadSessions() functions friend InterceptorMulti; void SetHttpMethod(HttpMethod method); void PrepareSessions(); template void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args); template void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg); void PrepareDownloadSession(size_t sessions_index, std::ofstream& file); void PrepareDownloadSession(size_t sessions_index, const WriteCallback& write); void PrepareGet(); void PrepareDelete(); void PreparePut(); void PreparePatch(); void PrepareHead(); void PrepareOptions(); void PreparePost(); template void PrepareDownload(DownloadArgTypes... args); const std::optional> intercept(); std::vector proceed(); std::vector MakeRequest(); std::vector MakeDownloadRequest(); void DoMultiPerform(); std::vector ReadMultiInfo(const std::function& complete_function); std::vector, HttpMethod>> sessions_; std::unique_ptr multicurl_; bool is_download_multi_perform{false}; using InterceptorsContainer = std::list>; InterceptorsContainer interceptors_; // Currently running interceptor InterceptorsContainer::iterator current_interceptor_; // Interceptor within the chain where to start with each repeated request InterceptorsContainer::iterator first_interceptor_; }; template void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg) { PrepareDownloadSession(sessions_index, current_arg); } template void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args) { PrepareDownloadSession(sessions_index, current_arg); PrepareDownloadSessions(sessions_index + 1, args...); } template void MultiPerform::PrepareDownload(DownloadArgTypes... args) { SetHttpMethod(HttpMethod::DOWNLOAD_REQUEST); PrepareDownloadSessions(0, args...); } template std::vector MultiPerform::Download(DownloadArgTypes... args) { if (sizeof...(args) != sessions_.size()) { throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!"); } PrepareDownload(args...); return MakeDownloadRequest(); } template std::vector MultiPerform::PerformDownload(DownloadArgTypes... args) { if (sizeof...(args) != sessions_.size()) { throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!"); } PrepareDownloadSessions(0, args...); return MakeDownloadRequest(); } } // namespace cpr #endif cpr-1.11.2/include/cpr/parameters.h000066400000000000000000000005511475657722200171250ustar00rootroot00000000000000#ifndef CPR_PARAMETERS_H #define CPR_PARAMETERS_H #include #include "cpr/curl_container.h" namespace cpr { class Parameters : public CurlContainer { public: Parameters() = default; Parameters(const std::initializer_list& parameters) : CurlContainer(parameters) {} }; } // namespace cpr #endif cpr-1.11.2/include/cpr/payload.h000066400000000000000000000007111475657722200164110ustar00rootroot00000000000000#ifndef CPR_PAYLOAD_H #define CPR_PAYLOAD_H #include #include "cpr/curl_container.h" namespace cpr { class Payload : public CurlContainer { public: template Payload(const It begin, const It end) { for (It pair = begin; pair != end; ++pair) { Add(*pair); } } Payload(const std::initializer_list& pairs) : CurlContainer(pairs) {} }; } // namespace cpr #endif cpr-1.11.2/include/cpr/proxies.h000066400000000000000000000010231475657722200164460ustar00rootroot00000000000000#ifndef CPR_PROXIES_H #define CPR_PROXIES_H #include #include #include namespace cpr { class Proxies { public: Proxies() = default; Proxies(const std::initializer_list>& hosts); Proxies(const std::map& hosts); bool has(const std::string& protocol) const; const std::string& operator[](const std::string& protocol); private: std::map hosts_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/proxyauth.h000066400000000000000000000031531475657722200170260ustar00rootroot00000000000000#ifndef CPR_PROXYAUTH_H #define CPR_PROXYAUTH_H #include #include #include #include "cpr/auth.h" #include "cpr/util.h" namespace cpr { class ProxyAuthentication; class EncodedAuthentication { friend ProxyAuthentication; public: EncodedAuthentication() = default; EncodedAuthentication(const std::string& p_username, const std::string& p_password) : username(util::urlEncode(p_username)), password(util::urlEncode(p_password)) {} EncodedAuthentication(const EncodedAuthentication& other) = default; EncodedAuthentication(EncodedAuthentication&& old) noexcept = default; virtual ~EncodedAuthentication() noexcept; EncodedAuthentication& operator=(EncodedAuthentication&& old) noexcept = default; EncodedAuthentication& operator=(const EncodedAuthentication& other) = default; [[nodiscard]] const std::string& GetUsername() const; [[nodiscard]] const std::string& GetPassword() const; private: std::string username; std::string password; }; class ProxyAuthentication { public: ProxyAuthentication() = default; ProxyAuthentication(const std::initializer_list>& auths) : proxyAuth_{auths} {} explicit ProxyAuthentication(const std::map& auths) : proxyAuth_{auths} {} [[nodiscard]] bool has(const std::string& protocol) const; const char* GetUsername(const std::string& protocol); const char* GetPassword(const std::string& protocol); private: std::map proxyAuth_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/range.h000066400000000000000000000021341475657722200160550ustar00rootroot00000000000000#ifndef CPR_RANGE_H #define CPR_RANGE_H #include #include namespace cpr { class Range { public: Range(const std::optional p_resume_from = std::nullopt, const std::optional p_finish_at = std::nullopt) { resume_from = p_resume_from.value_or(0); finish_at = p_finish_at.value_or(-1); } std::int64_t resume_from; std::int64_t finish_at; const std::string str() const { std::string from_str = (resume_from < 0U) ? "" : std::to_string(resume_from); std::string to_str = (finish_at < 0U) ? "" : std::to_string(finish_at); return from_str + "-" + to_str; } }; class MultiRange { public: MultiRange(std::initializer_list rs) : ranges{rs} {} const std::string str() const { std::string multi_range_string{}; for (Range range : ranges) { multi_range_string += ((multi_range_string.empty()) ? "" : ", ") + range.str(); } return multi_range_string; } private: std::vector ranges; }; // namespace cpr } // namespace cpr #endif cpr-1.11.2/include/cpr/redirect.h000066400000000000000000000060041475657722200165620ustar00rootroot00000000000000#ifndef CPR_REDIRECT_H #define CPR_REDIRECT_H #include namespace cpr { enum class PostRedirectFlags : uint8_t { /** * Respect RFC 7231 (section 6.4.2 to 6.4.4). * Same as CURL_REDIR_POST_301 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html). **/ POST_301 = 0x1 << 0, /** * Maintain the request method after a 302 redirect. * Same as CURL_REDIR_POST_302 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html). **/ POST_302 = 0x1 << 1, /** * Maintain the request method after a 303 redirect. * Same as CURL_REDIR_POST_303 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html). **/ POST_303 = 0x1 << 2, /** * Default value. * Convenience option to enable all flags. * Same as CURL_REDIR_POST_ALL (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html). **/ POST_ALL = POST_301 | POST_302 | POST_303, /** * * Convenience option to disable all flags. **/ NONE = 0x0 }; PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs); PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs); PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs); PostRedirectFlags operator~(PostRedirectFlags flag); PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs); PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs); PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs); bool any(PostRedirectFlags flag); class Redirect { public: /** * The maximum number of redirects to follow. * 0: Refuse any redirects. * -1: Infinite number of redirects. * Default: 50 * https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html **/ // NOLINTNEXTLINE (google-runtime-int) long maximum{50L}; /** * Follow 3xx redirects. * Default: true * https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html **/ bool follow{true}; /** * Continue to send authentication (user+password) credentials when following locations, even when hostname changed. * Default: false * https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html **/ bool cont_send_cred{false}; /** * Flags to control how to act after a redirect for a post request. * Default: POST_ALL **/ PostRedirectFlags post_flags{PostRedirectFlags::POST_ALL}; Redirect() = default; // NOLINTNEXTLINE (google-runtime-int) 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) {} // NOLINTNEXTLINE (google-runtime-int) explicit Redirect(long p_maximum) : maximum(p_maximum) {} explicit Redirect(bool p_follow) : follow(p_follow) {} Redirect(bool p_follow, bool p_cont_send_cred) : follow(p_follow), cont_send_cred(p_cont_send_cred) {} explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags) {} }; } // namespace cpr #endif cpr-1.11.2/include/cpr/reserve_size.h000066400000000000000000000003441475657722200174670ustar00rootroot00000000000000#ifndef CPR_RESERVE_SIZE_H #define CPR_RESERVE_SIZE_H #include namespace cpr { class ReserveSize { public: ReserveSize(const size_t _size) : size(_size) {} size_t size = 0; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/resolve.h000066400000000000000000000010541475657722200164400ustar00rootroot00000000000000#ifndef CPR_RESOLVE_H #define CPR_RESOLVE_H #include #include namespace cpr { class Resolve { public: std::string host; std::string addr; std::set ports; Resolve(const std::string& host_param, const std::string& addr_param, const std::set& ports_param = std::set{80U, 443U}) : host(host_param), addr(addr_param), ports(ports_param) { if (this->ports.empty()) { this->ports.insert(80U); this->ports.insert(443U); } } }; } // namespace cpr #endif cpr-1.11.2/include/cpr/response.h000066400000000000000000000027271475657722200166270ustar00rootroot00000000000000#ifndef CPR_RESPONSE_H #define CPR_RESPONSE_H #include #include #include #include #include #include #include "cpr/cert_info.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/error.h" #include "cpr/ssl_options.h" #include "cpr/util.h" namespace cpr { class MultiPerform; class Response { private: friend MultiPerform; std::shared_ptr curl_{nullptr}; public: // Ignored here since libcurl uses a long for this. // NOLINTNEXTLINE(google-runtime-int) long status_code{}; std::string text{}; Header header{}; Url url{}; double elapsed{}; Cookies cookies{}; Error error{}; std::string raw_header{}; std::string status_line{}; std::string reason{}; cpr_off_t uploaded_bytes{}; cpr_off_t downloaded_bytes{}; // Ignored here since libcurl uses a long for this. // NOLINTNEXTLINE(google-runtime-int) long redirect_count{}; Response() = default; Response(std::shared_ptr curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies, Error&& p_error); [[nodiscard]] std::vector GetCertInfos() const; Response(const Response& other) = default; Response(Response&& old) noexcept = default; ~Response() noexcept = default; Response& operator=(Response&& old) noexcept = default; Response& operator=(const Response& other) = default; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/session.h000066400000000000000000000307321475657722200164510ustar00rootroot00000000000000#ifndef CPR_SESSION_H #define CPR_SESSION_H #include #include #include #include #include #include #include #include #include "cpr/accept_encoding.h" #include "cpr/async_wrapper.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/body.h" #include "cpr/callback.h" #include "cpr/connect_timeout.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" #include "cpr/http_version.h" #include "cpr/interface.h" #include "cpr/limit_rate.h" #include "cpr/local_port.h" #include "cpr/local_port_range.h" #include "cpr/low_speed.h" #include "cpr/multipart.h" #include "cpr/parameters.h" #include "cpr/payload.h" #include "cpr/proxies.h" #include "cpr/proxyauth.h" #include "cpr/range.h" #include "cpr/redirect.h" #include "cpr/reserve_size.h" #include "cpr/resolve.h" #include "cpr/response.h" #include "cpr/ssl_options.h" #include "cpr/timeout.h" #include "cpr/unix_socket.h" #include "cpr/user_agent.h" #include "cpr/util.h" #include "cpr/verbose.h" namespace cpr { using AsyncResponse = AsyncWrapper; using Content = std::variant; class Interceptor; class MultiPerform; class Session : public std::enable_shared_from_this { public: Session(); Session(const Session& other) = delete; Session(Session&& old) = delete; ~Session() = default; Session& operator=(Session&& old) noexcept = delete; Session& operator=(const Session& other) = delete; void SetUrl(const Url& url); void SetParameters(const Parameters& parameters); void SetParameters(Parameters&& parameters); void SetHeader(const Header& header); void UpdateHeader(const Header& header); [[nodiscard]] Header& GetHeader(); [[nodiscard]] const Header& GetHeader() const; void SetTimeout(const Timeout& timeout); void SetConnectTimeout(const ConnectTimeout& timeout); void SetAuth(const Authentication& auth); // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 void SetBearer(const Bearer& token); #endif void SetUserAgent(const UserAgent& ua); void SetPayload(Payload&& payload); void SetPayload(const Payload& payload); void SetProxies(Proxies&& proxies); void SetProxies(const Proxies& proxies); void SetProxyAuth(ProxyAuthentication&& proxy_auth); void SetProxyAuth(const ProxyAuthentication& proxy_auth); void SetMultipart(Multipart&& multipart); void SetMultipart(const Multipart& multipart); void SetRedirect(const Redirect& redirect); void SetCookies(const Cookies& cookies); void SetBody(Body&& body); void SetBody(const Body& body); void SetLowSpeed(const LowSpeed& low_speed); void SetVerifySsl(const VerifySsl& verify); void SetUnixSocket(const UnixSocket& unix_socket); void SetSslOptions(const SslOptions& options); void SetReadCallback(const ReadCallback& read); void SetHeaderCallback(const HeaderCallback& header); void SetWriteCallback(const WriteCallback& write); void SetProgressCallback(const ProgressCallback& progress); void SetDebugCallback(const DebugCallback& debug); void SetVerbose(const Verbose& verbose); void SetInterface(const Interface& iface); void SetLocalPort(const LocalPort& local_port); void SetLocalPortRange(const LocalPortRange& local_port_range); void SetHttpVersion(const HttpVersion& version); void SetRange(const Range& range); void SetResolve(const Resolve& resolve); void SetResolves(const std::vector& resolves); void SetMultiRange(const MultiRange& multi_range); void SetReserveSize(const ReserveSize& reserve_size); void SetAcceptEncoding(const AcceptEncoding& accept_encoding); void SetAcceptEncoding(AcceptEncoding&& accept_encoding); void SetLimitRate(const LimitRate& limit_rate); /** * Returns a reference to the content sent in previous request. **/ [[nodiscard]] const Content& GetContent() const; /** * Removes the content sent in previous request from internal state, so it will not be sent with the next request. * Call this before doing a request that is specified not to send a body, e.g. GET. **/ void RemoveContent(); // For cancellable requests void SetCancellationParam(std::shared_ptr param); // Used in templated functions void SetOption(const Url& url); void SetOption(const Parameters& parameters); void SetOption(Parameters&& parameters); void SetOption(const Header& header); void SetOption(const Timeout& timeout); void SetOption(const ConnectTimeout& timeout); void SetOption(const Authentication& auth); // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 void SetOption(const Bearer& auth); #endif void SetOption(const UserAgent& ua); void SetOption(Payload&& payload); void SetOption(const Payload& payload); void SetOption(const LimitRate& limit_rate); void SetOption(Proxies&& proxies); void SetOption(const Proxies& proxies); void SetOption(ProxyAuthentication&& proxy_auth); void SetOption(const ProxyAuthentication& proxy_auth); void SetOption(Multipart&& multipart); void SetOption(const Multipart& multipart); void SetOption(const Redirect& redirect); void SetOption(const Cookies& cookies); void SetOption(Body&& body); void SetOption(const Body& body); void SetOption(const ReadCallback& read); void SetOption(const HeaderCallback& header); void SetOption(const WriteCallback& write); void SetOption(const ProgressCallback& progress); void SetOption(const DebugCallback& debug); void SetOption(const LowSpeed& low_speed); void SetOption(const VerifySsl& verify); void SetOption(const Verbose& verbose); void SetOption(const UnixSocket& unix_socket); void SetOption(const SslOptions& options); void SetOption(const Interface& iface); void SetOption(const LocalPort& local_port); void SetOption(const LocalPortRange& local_port_range); void SetOption(const HttpVersion& version); void SetOption(const Range& range); void SetOption(const MultiRange& multi_range); void SetOption(const ReserveSize& reserve_size); void SetOption(const AcceptEncoding& accept_encoding); void SetOption(AcceptEncoding&& accept_encoding); void SetOption(const Resolve& resolve); void SetOption(const std::vector& resolves); cpr_off_t GetDownloadFileLength(); /** * Attempt to preallocate enough memory for specified number of characters in the response string. * Pass 0 to disable this behavior and let the response string be allocated dynamically on demand. * * Example: * cpr::Session session; * session.SetUrl(cpr::Url{"http://xxx/file"}); * session.ResponseStringReserve(1024 * 512); // Reserve space for at least 1024 * 512 characters * cpr::Response r = session.Get(); **/ void ResponseStringReserve(size_t size); Response Delete(); Response Download(const WriteCallback& write); Response Download(std::ofstream& file); Response Get(); Response Head(); Response Options(); Response Patch(); Response Post(); Response Put(); AsyncResponse GetAsync(); AsyncResponse DeleteAsync(); AsyncResponse DownloadAsync(const WriteCallback& write); AsyncResponse DownloadAsync(std::ofstream& file); AsyncResponse HeadAsync(); AsyncResponse OptionsAsync(); AsyncResponse PatchAsync(); AsyncResponse PostAsync(); AsyncResponse PutAsync(); template auto GetCallback(Then then); template auto PostCallback(Then then); template auto PutCallback(Then then); template auto HeadCallback(Then then); template auto DeleteCallback(Then then); template auto OptionsCallback(Then then); template auto PatchCallback(Then then); std::shared_ptr GetCurlHolder(); std::string GetFullRequestUrl(); void PrepareDelete(); void PrepareGet(); void PrepareHead(); void PrepareOptions(); void PreparePatch(); void PreparePost(); void PreparePut(); void PrepareDownload(const WriteCallback& write); void PrepareDownload(std::ofstream& file); Response Complete(CURLcode curl_error); Response CompleteDownload(CURLcode curl_error); void AddInterceptor(const std::shared_ptr& pinterceptor); std::shared_ptr GetSharedPtrFromThis(); private: // Interceptors should be able to call the private proceed() function friend Interceptor; friend MultiPerform; bool chunkedTransferEncoding_{false}; Content content_{std::monostate{}}; std::shared_ptr curl_; Url url_; Parameters parameters_; Proxies proxies_; ProxyAuthentication proxyAuth_; Header header_; AcceptEncoding acceptEncoding_; struct Callbacks { /** * Will be set by the read callback. * Ensures that the "Transfer-Encoding" is set to "chunked", if not overriden in header_. **/ ReadCallback readcb_; HeaderCallback headercb_; WriteCallback writecb_; ProgressCallback progresscb_; DebugCallback debugcb_; CancellationCallback cancellationcb_; }; std::unique_ptr cbs_{std::make_unique()}; size_t response_string_reserve_size_{0}; std::string response_string_; std::string header_string_; // Container type is required to keep iterator valid on elem insertion. E.g. list but not vector. using InterceptorsContainer = std::list>; InterceptorsContainer interceptors_; // Currently running interceptor InterceptorsContainer::const_iterator current_interceptor_; // Interceptor within the chain where to start with each repeated request InterceptorsContainer::const_iterator first_interceptor_; bool isUsedInMultiPerform{false}; bool isCancellable{false}; #if SUPPORT_SSL_NO_REVOKE bool sslNoRevoke_{false}; #endif Response makeDownloadRequest(); Response makeRequest(); Response proceed(); const std::optional intercept(); /** * Prepares the curl object for a request with everything used by all requests. **/ void prepareCommonShared(); /** * Prepares the curl object for a request with everything used by all non download related requests. **/ void prepareCommon(); /** * Prepares the curl object for a request with everything used by the download request. **/ void prepareCommonDownload(); void prepareHeader(); void prepareProxy(); CURLcode DoEasyPerform(); void prepareBodyPayloadOrMultipart() const; /** * Returns true in case content_ is of type cpr::Body or cpr::Payload. **/ [[nodiscard]] bool hasBodyOrPayload() const; }; template auto Session::GetCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Get()); }, std::move(then)); } template auto Session::PostCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Post()); }, std::move(then)); } template auto Session::PutCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Put()); }, std::move(then)); } template auto Session::HeadCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Head()); }, std::move(then)); } template auto Session::DeleteCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Delete()); }, std::move(then)); } template auto Session::OptionsCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Options()); }, std::move(then)); } template auto Session::PatchCallback(Then then) { return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Patch()); }, std::move(then)); } } // namespace cpr #endif cpr-1.11.2/include/cpr/singleton.h000066400000000000000000000034261475657722200167700ustar00rootroot00000000000000#ifndef CPR_SINGLETON_H #define CPR_SINGLETON_H #include #include #ifndef CPR_DISABLE_COPY #define CPR_DISABLE_COPY(Class) \ Class(const Class&) = delete; \ Class& operator=(const Class&) = delete; #endif #ifndef CPR_SINGLETON_DECL #define CPR_SINGLETON_DECL(Class) \ public: \ static Class* GetInstance(); \ static void ExitInstance(); \ \ private: \ CPR_DISABLE_COPY(Class) \ static Class* s_pInstance; \ static std::once_flag s_getFlag; \ static std::once_flag s_exitFlag; #endif #ifndef CPR_SINGLETON_IMPL #define CPR_SINGLETON_IMPL(Class) \ Class* Class::s_pInstance = nullptr; \ std::once_flag Class::s_getFlag{}; \ std::once_flag Class::s_exitFlag{}; \ Class* Class::GetInstance() { \ std::call_once(Class::s_getFlag, []() { s_pInstance = new Class; }); \ return s_pInstance; \ } \ void Class::ExitInstance() { \ std::call_once(Class::s_exitFlag, []() { \ assert(s_pInstance); \ delete s_pInstance; \ s_pInstance = nullptr; \ }); \ } #endif #endif cpr-1.11.2/include/cpr/ssl_ctx.h000066400000000000000000000012651475657722200164440ustar00rootroot00000000000000#ifndef CPR_SSL_CTX_H #define CPR_SSL_CTX_H #include "cpr/ssl_options.h" #include #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION namespace cpr { /** * This callback function loads a CA certificate from raw_cert_buf and gets called by libcurl * just before the initialization of an SSL connection. * The raw_cert_buf argument is set with the CURLOPT_SSL_CTX_DATA option and has to be a nul * terminated buffer. * * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html */ CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf); } // Namespace cpr #endif #endif cpr-1.11.2/include/cpr/ssl_options.h000066400000000000000000000404031475657722200173360ustar00rootroot00000000000000#ifndef CPR_SSLOPTIONS_H #define CPR_SSLOPTIONS_H #include #include #include #include "cpr/filesystem.h" #include #include "cpr/util.h" #include #ifndef SUPPORT_ALPN #define SUPPORT_ALPN LIBCURL_VERSION_NUM >= 0x072400 // 7.36.0 #endif #ifndef SUPPORT_NPN #define SUPPORT_NPN LIBCURL_VERSION_NUM >= 0x072400 && LIBCURL_VERSION_NUM < 0x075600 // 7.36.0 - 7.85.0 #endif #ifndef SUPPORT_SSLv2 #define SUPPORT_SSLv2 LIBCURL_VERSION_NUM <= 0x071300 // 7.19.0 #endif #ifndef SUPPORT_SSLv3 #define SUPPORT_SSLv3 LIBCURL_VERSION_NUM <= 0x072700 // 7.39.0 #endif #ifndef SUPPORT_TLSv1_0 #define SUPPORT_TLSv1_0 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_1 #define SUPPORT_TLSv1_1 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_2 #define SUPPORT_TLSv1_2 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_3 #define SUPPORT_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073400 // 7.52.0 #endif #ifndef SUPPORT_MAX_TLS_VERSION #define SUPPORT_MAX_TLS_VERSION LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_1 #define SUPPORT_MAX_TLSv1_1 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_2 #define SUPPORT_MAX_TLSv1_2 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_3 #define SUPPORT_MAX_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_TLSv13_CIPHERS #define SUPPORT_TLSv13_CIPHERS LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0 #endif #ifndef SUPPORT_SESSIONID_CACHE #define SUPPORT_SESSIONID_CACHE LIBCURL_VERSION_NUM >= 0x071000 // 7.16.0 #endif #ifndef SUPPORT_SSL_FALSESTART #define SUPPORT_SSL_FALSESTART LIBCURL_VERSION_NUM >= 0x072A00 // 7.42.0 #endif #ifndef SUPPORT_SSL_NO_REVOKE #define SUPPORT_SSL_NO_REVOKE LIBCURL_VERSION_NUM >= 0x072C00 // 7.44.0 #endif #ifndef SUPPORT_CURLOPT_SSLKEY_BLOB #define SUPPORT_CURLOPT_SSLKEY_BLOB LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0 #endif #ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION #define SUPPORT_CURLOPT_SSL_CTX_FUNCTION LIBCURL_VERSION_NUM >= 0x070B00 // 7.11.0 #endif namespace cpr { class VerifySsl { public: VerifySsl() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) VerifySsl(bool p_verify) : verify(p_verify) {} explicit operator bool() const { return verify; } bool verify = true; }; namespace ssl { // set SSL client certificate class CertFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} virtual ~CertFile() = default; const fs::path filename; virtual const char* GetCertType() const { return "PEM"; } }; using PemCert = CertFile; class DerCert : public CertFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {} ~DerCert() override = default; const char* GetCertType() const override { return "DER"; } }; // specify private keyfile for TLS and SSL client cert class KeyFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} template KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward(p_filename)), password(std::move(p_password)) {} virtual ~KeyFile() { util::secureStringClear(password); } fs::path filename; std::string password; virtual const char* GetKeyType() const { return "PEM"; } }; #if SUPPORT_CURLOPT_SSLKEY_BLOB class KeyBlob { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {} template KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward(p_blob)), password(std::move(p_password)) {} virtual ~KeyBlob() { util::secureStringClear(password); } std::string blob; std::string password; virtual const char* GetKeyType() const { return "PEM"; } }; #endif using PemKey = KeyFile; class DerKey : public KeyFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {} template DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward(p_filename), std::move(p_password)) {} ~DerKey() override = default; const char* GetKeyType() const override { return "DER"; } }; class PinnedPublicKey { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {} const std::string pinned_public_key; }; #if SUPPORT_ALPN // This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to // use supports it), which can be used to negotiate http2. class ALPN { public: ALPN() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ALPN(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = true; }; #endif // SUPPORT_ALPN #if SUPPORT_NPN // This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to // use supports it), which can be used to negotiate http2. class NPN { public: NPN() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) NPN(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = true; }; #endif // SUPPORT_NPN // This option determines whether libcurl verifies that the server cert is for the server it is // known as. class VerifyHost { public: VerifyHost() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) VerifyHost(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = true; }; // This option determines whether libcurl verifies the authenticity of the peer's certificate. class VerifyPeer { public: VerifyPeer() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) VerifyPeer(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = true; }; // This option determines whether libcurl verifies the status of the server cert using the // "Certificate Status Request" TLS extension (aka. OCSP stapling). class VerifyStatus { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) VerifyStatus(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = false; }; // TLS v1.0 or later struct TLSv1 {}; #if SUPPORT_SSLv2 // SSL v2 (but not SSLv3) struct SSLv2 {}; #endif #if SUPPORT_SSLv3 // SSL v3 (but not SSLv2) struct SSLv3 {}; #endif #if SUPPORT_TLSv1_0 // TLS v1.0 or later (Added in 7.34.0) struct TLSv1_0 {}; #endif #if SUPPORT_TLSv1_1 // TLS v1.1 or later (Added in 7.34.0) struct TLSv1_1 {}; #endif #if SUPPORT_TLSv1_2 // TLS v1.2 or later (Added in 7.34.0) struct TLSv1_2 {}; #endif #if SUPPORT_TLSv1_3 // TLS v1.3 or later (Added in 7.52.0) struct TLSv1_3 {}; #endif #if SUPPORT_MAX_TLS_VERSION // The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL // library is used. struct MaxTLSVersion {}; #endif #if SUPPORT_MAX_TLSv1_0 // The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0) struct MaxTLSv1_0 {}; #endif #if SUPPORT_MAX_TLSv1_1 // The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0) struct MaxTLSv1_1 {}; #endif #if SUPPORT_MAX_TLSv1_2 // The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0) struct MaxTLSv1_2 {}; #endif #if SUPPORT_MAX_TLSv1_3 // The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0) struct MaxTLSv1_3 {}; #endif // path to Certificate Authority (CA) bundle class CaInfo { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {} fs::path filename; }; // specify directory holding CA certificates class CaPath { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {} fs::path filename; }; #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION class CaBuffer { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {} const std::string buffer; }; #endif // specify a Certificate Revocation List file class Crl { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {} fs::path filename; }; // specify ciphers to use for TLS class Ciphers { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {} std::string ciphers; }; #if SUPPORT_TLSv13_CIPHERS // specify ciphers suites to use for TLS 1.3 class TLS13_Ciphers { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {} std::string ciphers; }; #endif #if SUPPORT_SESSIONID_CACHE // enable/disable use of the SSL session-ID cache class SessionIdCache { public: SessionIdCache() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) SessionIdCache(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = true; }; #endif #if SUPPORT_SSL_FALSESTART class SslFastStart { public: SslFastStart() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) SslFastStart(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = false; }; #endif class NoRevoke { public: NoRevoke() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) NoRevoke(bool p_enabled) : enabled(p_enabled) {} explicit operator bool() const { return enabled; } bool enabled = false; }; } // namespace ssl struct SslOptions { // We don't use fs::path here, as this leads to problems using windows std::string cert_file; std::string cert_type; // We don't use fs::path here, as this leads to problems using windows std::string key_file; #if SUPPORT_CURLOPT_SSLKEY_BLOB std::string key_blob; #endif std::string key_type; std::string key_pass; std::string pinned_public_key; #if SUPPORT_ALPN bool enable_alpn = true; #endif // SUPPORT_ALPN #if SUPPORT_NPN bool enable_npn = true; #endif // SUPPORT_ALPN bool verify_host = true; bool verify_peer = true; bool verify_status = false; int ssl_version = CURL_SSLVERSION_DEFAULT; #if SUPPORT_SSL_NO_REVOKE bool ssl_no_revoke = false; #endif #if SUPPORT_MAX_TLS_VERSION int max_version = CURL_SSLVERSION_MAX_DEFAULT; #endif // We don't use fs::path here, as this leads to problems using windows std::string ca_info; // We don't use fs::path here, as this leads to problems using windows std::string ca_path; #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION std::string ca_buffer; #endif // We don't use fs::path here, as this leads to problems using windows std::string crl_file; std::string ciphers; #if SUPPORT_TLSv13_CIPHERS std::string tls13_ciphers; #endif #if SUPPORT_SESSIONID_CACHE bool session_id_cache = true; #endif ~SslOptions() noexcept { #if SUPPORT_CURLOPT_SSLKEY_BLOB util::secureStringClear(key_blob); #endif util::secureStringClear(key_pass); } void SetOption(const ssl::CertFile& opt) { cert_file = opt.filename.string(); cert_type = opt.GetCertType(); } void SetOption(const ssl::KeyFile& opt) { key_file = opt.filename.string(); key_type = opt.GetKeyType(); key_pass = opt.password; } #if SUPPORT_CURLOPT_SSLKEY_BLOB void SetOption(const ssl::KeyBlob& opt) { key_blob = opt.blob; key_type = opt.GetKeyType(); key_pass = opt.password; } #endif void SetOption(const ssl::PinnedPublicKey& opt) { pinned_public_key = opt.pinned_public_key; } #if SUPPORT_ALPN void SetOption(const ssl::ALPN& opt) { enable_alpn = opt.enabled; } #endif // SUPPORT_ALPN #if SUPPORT_NPN void SetOption(const ssl::NPN& opt) { enable_npn = opt.enabled; } #endif // SUPPORT_NPN void SetOption(const ssl::VerifyHost& opt) { verify_host = opt.enabled; } void SetOption(const ssl::VerifyPeer& opt) { verify_peer = opt.enabled; } void SetOption(const ssl::VerifyStatus& opt) { verify_status = opt.enabled; } void SetOption(const ssl::TLSv1& /*opt*/) { ssl_version = CURL_SSLVERSION_TLSv1; } #if SUPPORT_SSL_NO_REVOKE void SetOption(const ssl::NoRevoke& opt) { ssl_no_revoke = opt.enabled; } #endif #if SUPPORT_SSLv2 void SetOption(const ssl::SSLv2& /*opt*/) { ssl_version = CURL_SSLVERSION_SSLv2; } #endif #if SUPPORT_SSLv3 void SetOption(const ssl::SSLv3& /*opt*/) { ssl_version = CURL_SSLVERSION_SSLv3; } #endif #if SUPPORT_TLSv1_0 void SetOption(const ssl::TLSv1_0& /*opt*/) { ssl_version = CURL_SSLVERSION_TLSv1_0; } #endif #if SUPPORT_TLSv1_1 void SetOption(const ssl::TLSv1_1& /*opt*/) { ssl_version = CURL_SSLVERSION_TLSv1_1; } #endif #if SUPPORT_TLSv1_2 void SetOption(const ssl::TLSv1_2& /*opt*/) { ssl_version = CURL_SSLVERSION_TLSv1_2; } #endif #if SUPPORT_TLSv1_3 void SetOption(const ssl::TLSv1_3& /*opt*/) { ssl_version = CURL_SSLVERSION_TLSv1_3; } #endif #if SUPPORT_MAX_TLS_VERSION void SetOption(const ssl::MaxTLSVersion& /*opt*/) { max_version = CURL_SSLVERSION_DEFAULT; } #endif #if SUPPORT_MAX_TLSv1_0 void SetOption(const ssl::MaxTLSv1_0& opt) { max_version = CURL_SSLVERSION_MAX_TLSv1_0; } #endif #if SUPPORT_MAX_TLSv1_1 void SetOption(const ssl::MaxTLSv1_1& /*opt*/) { max_version = CURL_SSLVERSION_MAX_TLSv1_1; } #endif #if SUPPORT_MAX_TLSv1_2 void SetOption(const ssl::MaxTLSv1_2& /*opt*/) { max_version = CURL_SSLVERSION_MAX_TLSv1_2; } #endif #if SUPPORT_MAX_TLSv1_3 void SetOption(const ssl::MaxTLSv1_3& /*opt*/) { max_version = CURL_SSLVERSION_MAX_TLSv1_3; } #endif void SetOption(const ssl::CaInfo& opt) { ca_info = opt.filename.string(); } void SetOption(const ssl::CaPath& opt) { ca_path = opt.filename.string(); } #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION void SetOption(const ssl::CaBuffer& opt) { ca_buffer = opt.buffer; } #endif void SetOption(const ssl::Crl& opt) { crl_file = opt.filename.string(); } void SetOption(const ssl::Ciphers& opt) { ciphers = opt.ciphers; } #if SUPPORT_TLSv13_CIPHERS void SetOption(const ssl::TLS13_Ciphers& opt) { tls13_ciphers = opt.ciphers; } #endif #if SUPPORT_SESSIONID_CACHE void SetOption(const ssl::SessionIdCache& opt) { session_id_cache = opt.enabled; } #endif }; namespace priv { template void set_ssl_option(SslOptions& opts, T&& t) { opts.SetOption(std::forward(t)); } template void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) { set_ssl_option(opts, std::forward(t)); set_ssl_option(opts, std::move(ts)...); } } // namespace priv template SslOptions Ssl(Ts&&... ts) { SslOptions opts; priv::set_ssl_option(opts, std::move(ts)...); return opts; } } // namespace cpr #endif cpr-1.11.2/include/cpr/status_codes.h000066400000000000000000000105741475657722200174700ustar00rootroot00000000000000#ifndef CPR_STATUS_CODES #define CPR_STATUS_CODES #include namespace cpr { namespace status { // Information responses constexpr std::int32_t HTTP_CONTINUE = 100; constexpr std::int32_t HTTP_SWITCHING_PROTOCOL = 101; constexpr std::int32_t HTTP_PROCESSING = 102; constexpr std::int32_t HTTP_EARLY_HINTS = 103; // Successful responses constexpr std::int32_t HTTP_OK = 200; constexpr std::int32_t HTTP_CREATED = 201; constexpr std::int32_t HTTP_ACCEPTED = 202; constexpr std::int32_t HTTP_NON_AUTHORITATIVE_INFORMATION = 203; constexpr std::int32_t HTTP_NO_CONTENT = 204; constexpr std::int32_t HTTP_RESET_CONTENT = 205; constexpr std::int32_t HTTP_PARTIAL_CONTENT = 206; constexpr std::int32_t HTTP_MULTI_STATUS = 207; constexpr std::int32_t HTTP_ALREADY_REPORTED = 208; constexpr std::int32_t HTTP_IM_USED = 226; // Redirection messages constexpr std::int32_t HTTP_MULTIPLE_CHOICE = 300; constexpr std::int32_t HTTP_MOVED_PERMANENTLY = 301; constexpr std::int32_t HTTP_FOUND = 302; constexpr std::int32_t HTTP_SEE_OTHER = 303; constexpr std::int32_t HTTP_NOT_MODIFIED = 304; constexpr std::int32_t HTTP_USE_PROXY = 305; constexpr std::int32_t HTTP_UNUSED = 306; constexpr std::int32_t HTTP_TEMPORARY_REDIRECT = 307; constexpr std::int32_t HTTP_PERMANENT_REDIRECT = 308; // Client error responses constexpr std::int32_t HTTP_BAD_REQUEST = 400; constexpr std::int32_t HTTP_UNAUTHORIZED = 401; constexpr std::int32_t HTTP_PAYMENT_REQUIRED = 402; constexpr std::int32_t HTTP_FORBIDDEN = 403; constexpr std::int32_t HTTP_NOT_FOUND = 404; constexpr std::int32_t HTTP_METHOD_NOT_ALLOWED = 405; constexpr std::int32_t HTTP_NOT_ACCEPTABLE = 406; constexpr std::int32_t HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; constexpr std::int32_t HTTP_REQUEST_TIMEOUT = 408; constexpr std::int32_t HTTP_CONFLICT = 409; constexpr std::int32_t HTTP_GONE = 410; constexpr std::int32_t HTTP_LENGTH_REQUIRED = 411; constexpr std::int32_t HTTP_PRECONDITION_FAILED = 412; constexpr std::int32_t HTTP_PAYLOAD_TOO_LARGE = 413; constexpr std::int32_t HTTP_URI_TOO_LONG = 414; constexpr std::int32_t HTTP_UNSUPPORTED_MEDIA_TYPE = 415; constexpr std::int32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; constexpr std::int32_t HTTP_EXPECTATION_FAILED = 417; constexpr std::int32_t HTTP_IM_A_TEAPOT = 418; constexpr std::int32_t HTTP_MISDIRECTED_REQUEST = 421; constexpr std::int32_t HTTP_UNPROCESSABLE_ENTITY = 422; constexpr std::int32_t HTTP_LOCKED = 423; constexpr std::int32_t HTTP_FAILED_DEPENDENCY = 424; constexpr std::int32_t HTTP_TOO_EARLY = 425; constexpr std::int32_t HTTP_UPGRADE_REQUIRED = 426; constexpr std::int32_t HTTP_PRECONDITION_REQUIRED = 428; constexpr std::int32_t HTTP_TOO_MANY_REQUESTS = 429; constexpr std::int32_t HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; constexpr std::int32_t HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // Server response errors constexpr std::int32_t HTTP_INTERNAL_SERVER_ERROR = 500; constexpr std::int32_t HTTP_NOT_IMPLEMENTED = 501; constexpr std::int32_t HTTP_BAD_GATEWAY = 502; constexpr std::int32_t HTTP_SERVICE_UNAVAILABLE = 503; constexpr std::int32_t HTTP_GATEWAY_TIMEOUT = 504; constexpr std::int32_t HTTP_HTTP_VERSION_NOT_SUPPORTED = 505; constexpr std::int32_t HTTP_VARIANT_ALSO_NEGOTIATES = 506; constexpr std::int32_t HTTP_INSUFFICIENT_STORAGE = 507; constexpr std::int32_t HTTP_LOOP_DETECTED = 508; constexpr std::int32_t HTTP_NOT_EXTENDED = 510; constexpr std::int32_t HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; constexpr std::int32_t INFO_CODE_OFFSET = 100; constexpr std::int32_t SUCCESS_CODE_OFFSET = 200; constexpr std::int32_t REDIRECT_CODE_OFFSET = 300; constexpr std::int32_t CLIENT_ERROR_CODE_OFFSET = 400; constexpr std::int32_t SERVER_ERROR_CODE_OFFSET = 500; constexpr std::int32_t MISC_CODE_OFFSET = 600; constexpr bool is_informational(const std::int32_t code) { return (code >= INFO_CODE_OFFSET && code < SUCCESS_CODE_OFFSET); } constexpr bool is_success(const std::int32_t code) { return (code >= SUCCESS_CODE_OFFSET && code < REDIRECT_CODE_OFFSET); } constexpr bool is_redirect(const std::int32_t code) { return (code >= REDIRECT_CODE_OFFSET && code < CLIENT_ERROR_CODE_OFFSET); } constexpr bool is_client_error(const std::int32_t code) { return (code >= CLIENT_ERROR_CODE_OFFSET && code < SERVER_ERROR_CODE_OFFSET); } constexpr bool is_server_error(const std::int32_t code) { return (code >= SERVER_ERROR_CODE_OFFSET && code < MISC_CODE_OFFSET); } } // namespace status } // namespace cpr #endifcpr-1.11.2/include/cpr/threadpool.h000066400000000000000000000070121475657722200171220ustar00rootroot00000000000000#ifndef CPR_THREAD_POOL_H #define CPR_THREAD_POOL_H #include #include #include #include #include #include #include #include #include #include #include #define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency() constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1; constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{250}; namespace cpr { class ThreadPool { public: using Task = std::function; 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); ThreadPool(const ThreadPool& other) = delete; ThreadPool(ThreadPool&& old) = delete; virtual ~ThreadPool(); ThreadPool& operator=(const ThreadPool& other) = delete; ThreadPool& operator=(ThreadPool&& old) = delete; void SetMinThreadNum(size_t min_threads) { min_thread_num = min_threads; } void SetMaxThreadNum(size_t max_threads) { max_thread_num = max_threads; } void SetMaxIdleTime(std::chrono::milliseconds ms) { max_idle_time = ms; } size_t GetCurrentThreadNum() { return cur_thread_num; } size_t GetIdleThreadNum() { return idle_thread_num; } bool IsStarted() { return status != STOP; } bool IsStopped() { return status == STOP; } int Start(size_t start_threads = 0); int Stop(); int Pause(); int Resume(); int Wait(); /** * Return a future, calling future.get() will wait task done and return RetType. * Submit(fn, args...) * Submit(std::bind(&Class::mem_fn, &obj)) * Submit(std::mem_fn(&Class::mem_fn, &obj)) **/ template auto Submit(Fn&& fn, Args&&... args) { if (status == STOP) { Start(); } if (idle_thread_num <= 0 && cur_thread_num < max_thread_num) { CreateThread(); } using RetType = decltype(fn(args...)); auto task = std::make_shared>([fn = std::forward(fn), args...]() mutable { return std::invoke(fn, args...); }); std::future future = task->get_future(); { std::lock_guard locker(task_mutex); tasks.emplace([task] { (*task)(); }); } task_cond.notify_one(); return future; } private: bool CreateThread(); void AddThread(std::thread* thread); void DelThread(std::thread::id id); public: size_t min_thread_num; size_t max_thread_num; std::chrono::milliseconds max_idle_time; private: enum Status { STOP, RUNNING, PAUSE, }; struct ThreadData { std::shared_ptr thread; std::thread::id id; Status status; std::chrono::steady_clock::time_point start_time; std::chrono::steady_clock::time_point stop_time; }; std::atomic status{Status::STOP}; std::condition_variable status_wait_cond{}; std::mutex status_wait_mutex{}; std::atomic cur_thread_num{0}; std::atomic idle_thread_num{0}; std::list threads{}; std::mutex thread_mutex{}; std::queue tasks{}; std::mutex task_mutex{}; std::condition_variable task_cond{}; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/timeout.h000066400000000000000000000015231475657722200164500ustar00rootroot00000000000000#ifndef CPR_TIMEOUT_H #define CPR_TIMEOUT_H #include #include namespace cpr { class Timeout { public: // Template constructor to accept any chrono duration type and convert it to milliseconds // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) template Timeout(const std::chrono::duration& duration) : ms{std::chrono::duration_cast(duration)} {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Timeout(const std::int32_t& milliseconds) : Timeout{std::chrono::milliseconds(milliseconds)} {} // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) long Milliseconds() const; std::chrono::milliseconds ms; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/unix_socket.h000066400000000000000000000006351475657722200173200ustar00rootroot00000000000000#ifndef CPR_UNIX_SOCKET_H #define CPR_UNIX_SOCKET_H #include namespace cpr { class UnixSocket { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) UnixSocket(std::string unix_socket) : unix_socket_(std::move(unix_socket)) {} const char* GetUnixSocketString() const noexcept; private: const std::string unix_socket_; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/user_agent.h000066400000000000000000000022341475657722200171160ustar00rootroot00000000000000#ifndef CPR_USERAGENT_H #define CPR_USERAGENT_H #include #include #include "cpr/cprtypes.h" namespace cpr { class UserAgent : public StringHolder { public: UserAgent() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) UserAgent(std::string useragent) : StringHolder(std::move(useragent)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) UserAgent(std::string_view useragent) : StringHolder(useragent) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) UserAgent(const char* useragent) : StringHolder(useragent) {} UserAgent(const char* str, size_t len) : StringHolder(str, len) {} UserAgent(const std::initializer_list args) : StringHolder(args) {} UserAgent(const UserAgent& other) = default; UserAgent(UserAgent&& old) noexcept = default; ~UserAgent() override = default; UserAgent& operator=(UserAgent&& old) noexcept = default; UserAgent& operator=(const UserAgent& other) = default; }; } // namespace cpr #endif cpr-1.11.2/include/cpr/util.h000066400000000000000000000042361475657722200157430ustar00rootroot00000000000000#ifndef CPR_UTIL_H #define CPR_UTIL_H #include #include #include #include "cpr/callback.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" namespace cpr::util { Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr); Cookies parseCookies(curl_slist* raw_cookies); size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read); size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header); size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data); size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file); size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write); template int progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) { const int cancel_retval{1}; #ifdef CURL_PROGRESSFUNC_CONTINUE // Not always defined. Ref: https://github.com/libcpr/cpr/issues/932 static_assert(cancel_retval != CURL_PROGRESSFUNC_CONTINUE); #endif // CURL_PROGRESSFUNC_CONTINUE return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : cancel_retval; } int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug); std::vector split(const std::string& to_split, char delimiter); std::string urlEncode(const std::string& s); std::string urlDecode(const std::string& s); /** * Override the content of the provided string to hide sensitive data. The * string content after invocation is undefined. The string size is reset to zero. * impl. based on: * https://github.com/ojeda/secure_clear/blob/master/example-implementation/secure_clear.h **/ void secureStringClear(std::string& s); bool isTrue(const std::string& s); /** * Parses the given std::string into time_t (unix ms). * This parsing happens time_t size agnostic since time_t does not use the same underlying type on all systems/compilers. **/ time_t sTimestampToT(const std::string&); } // namespace cpr::util #endif cpr-1.11.2/include/cpr/verbose.h000066400000000000000000000005131475657722200164250ustar00rootroot00000000000000#ifndef CPR_VERBOSE_H_ #define CPR_VERBOSE_H_ namespace cpr { class Verbose { public: Verbose() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Verbose(const bool p_verbose) : verbose{p_verbose} {} bool verbose = true; }; } // namespace cpr #endif /* CPR_VERBOSE_H_ */ cpr-1.11.2/nuget/000077500000000000000000000000001475657722200135235ustar00rootroot00000000000000cpr-1.11.2/nuget/build/000077500000000000000000000000001475657722200146225ustar00rootroot00000000000000cpr-1.11.2/nuget/build/native/000077500000000000000000000000001475657722200161105ustar00rootroot00000000000000cpr-1.11.2/nuget/build/native/libcpr.props000066400000000000000000000017461475657722200204600ustar00rootroot00000000000000 mdd md %(RecursiveDir)%(FileName)%(Extension) PreserveNewest @(CprLibs) $(CprLibraries);%(AdditionalDependencies) cpr-1.11.2/nuget/build/native/libcpr.targets000066400000000000000000000013101475657722200207510ustar00rootroot00000000000000 $(MSBuildThisFileDirectory)\$(Platform)\$(Configuration)\include\;%(AdditionalIncludeDirectories) cpr-1.11.2/nuget/libcpr.nuspec000066400000000000000000000015461475657722200162230ustar00rootroot00000000000000 libcpr $VERSION$ C++ Requests: Curl for People Fabian Sauter Fabian Sauter, Kilian Traub, many other libcpr contributors false MIT resources/cpr.png README.md https://github.com/libcpr C++ Requests: Curl for People, a spiritual port of Python Requests. Native, native english cpr-1.11.2/nuget/resources/000077500000000000000000000000001475657722200155355ustar00rootroot00000000000000cpr-1.11.2/nuget/resources/cpr.png000066400000000000000000000136371475657722200170410ustar00rootroot00000000000000PNG  IHDR"n pHYs\F\FCAtEXtSoftwareAdobe ImageReadyqe<,IDATxu5M֭V`QDq* N*@ +vA`;<9{Droj>-q{{{Gdz~[~%OZfKV>6pR.pA(j?+|; et b5/bpD/t5t}pC @0'%kBlaN{^PĜe;Bxp8U+A=C4y+=w4՗k}ʛ;y+A|<8O4ҧF:*yI(YnCf-P5/?b%Pfԉ2귳~ ^ Uѿۧ-XD*mVצV%:y@w7a~_XP՗iP+iv_ ~D >[.xOQ>0>UW),JMO{Y엶dhvwOUr4}aLNˡprτqWypIF &puwq=gz By!S'E\]fk߳΄.\NqfSrvYVϺ/?~",7=qgμ3 k d [2v9T T'dqܻbHwA 埯ݻ`];}@n<˽c1,O œn[Z "{lC\jDq籧vC )Zm:,l8>b#h(^ ,M-?n-h|ia >|B ̻Ha^ŜhJvgj/WcfyY0Z ;rbKe_ЃlZ }dZ W}`0P/*/iJGn) Gԑh(-dN`,':턲@&V%n)2Ya A :r%IV" ['앵JwVRl#=LCN]Jz:@>[qoWbeMܹEձX,. *M<6?{t }ԙKϿh˾Yc{E@/>J08 * X%g祯DP.GwB]8Zb\ `m݈+A< ;hδ/ Ԅ~gJ~L"4\=҅19@n?ga.'H [ZW4 P%x4~OM-7r\>뼜$!OQ8۴p^c* }_2jl-G2|T.bZr\*q}ғ-5+BEepa8kQ%?(ӌ>nR@n>2ԟ yP ;C*n啛u|R5j;24:FCa8Y~v6vδ8[Ϟ +h٨{ƻ2l=g``.$Zؖ@ȘNBa?sp^÷puݜji6Y\-nCq}DٍU@Zc$PJ+qـX75W[_)c<`N^1iV׵r1z cZ0oZ4r&*W6儹̪eXoZ^i]k('D U#-LboͦR< SU0Q-C5^[li͡\(\_0}ziMlaX*G +cE^jVP^(\w|S9Z'Z$~2J(SeiY<$PWַuG`.=LbcOL<:4j.x܂T(٪䉕RV7:҆+io{zH̳.{2ț R%gq 䗥25ymrr5S-Gp`W¦}p(535O+CSrI_M]w&vmR\x^j9kehd#o8˛j^%[FTˇŀ[+L!vfZZ'fB6bl!˂NTW]32Csnz{r pP>}24̶F(AP%ZkE۫ٹ]$ǚ0OG(?/վ޹J}kK\YeXK̿ᶚ߷|@@< S(~_Y @P6c)ʵS)#E S|]HWPjOWE< <&e]5^].-uJ>wo~]OTJ}9FUR\)ѸyD9C9~ITďfeMj}lxQM甯+'r?hZI^BʏwQ4\%+MyBy`^jVe2eYi5ͼD\'R*/>j6U)g+y>**WZ<~iJ,!E(ByX\u/"rU&yAJ9K|yyr?¯nA3M3Ur~2偋Y؂V.2d $DH(+/jyer`/.@P'G2ϕ_yGfaO[)gDŽ/2*KTiAh#اcy q%S%ǂ!arZ&JLA;rjX7:p|2e29 J ^Z('V-I](CrLSQ_ $|eiFC ag*?+J*,~w&}gw9^ky+dU2P]ΒSIWBrK񈔷HU%+ޅkA(L:Ov e8U*Rن|;.R%Oz_ϘL(v/a7cRiq&O->)CL4Nx, go<˳Rחyk\P^{_KG6o: V-UʭcVq 퇅q U}Ovnl>K0Ǥ Of|O_͇r1XT>Wx~c`,}%X63۴am-k@(7ep6=Bp%/4'[<`5 ,m&es̡yhg?/+epo{ê{ĀPf!}E B2 mkgPjPG8JHV\$~){t?@(g eH?+AݭwWK%x| =`4c IENDB`cpr-1.11.2/package-build/000077500000000000000000000000001475657722200150715ustar00rootroot00000000000000cpr-1.11.2/package-build/build-package.sh000077500000000000000000000010421475657722200201150ustar00rootroot00000000000000#!/bin/bash SRC_DIR=$1 LIB='libcpr' LIB_DIR="${LIB}_${VERSION}" DEBIAN_DIR="${LIB_DIR}/debian" ARCHIVE_NAME="$LIB_DIR.orig.tar.gz" echo -e "Preparing tar archive and directory\n" cp -r $SRC_DIR $LIB_DIR tar --exclude-vcs -czf $ARCHIVE_NAME $LIB_DIR tar -xzf $ARCHIVE_NAME cd $LIB_DIR echo -e "\n\nCopying prepared debian files to directory\n" mkdir debian cp -r package-build/debian-libcpr/* debian/ sed -i "s/\%VERSION/$VERSION/g" debian/changelog sed -i "s/\%DATE/$(date -R)/g" debian/changelog echo -e "\n\nCalling debuild\n" debuild cpr-1.11.2/package-build/debian-libcpr/000077500000000000000000000000001475657722200175645ustar00rootroot00000000000000cpr-1.11.2/package-build/debian-libcpr/README.Debian000066400000000000000000000002041475657722200216210ustar00rootroot00000000000000libcpr for Debian A package of the libcpr library. -- Philip Saendig Tue, 24 May 2022 10:37:24 +0200 cpr-1.11.2/package-build/debian-libcpr/changelog000066400000000000000000000002371475657722200214400ustar00rootroot00000000000000libcpr (%VERSION-1) UNRELEASED; urgency=low [ Philip Saendig ] * First package of libcpr %VERSION for debian. -- Philip Saendig %DATE cpr-1.11.2/package-build/debian-libcpr/control000066400000000000000000000023041475657722200211660ustar00rootroot00000000000000Source: libcpr Section: libs Priority: optional Maintainer: Philip Saendig Build-Depends: debhelper-compat (= 12), cmake, libcurl4-openssl-dev, libssl-dev, Standards-Version: 4.5.0 Homepage: https://github.com/libcpr/cpr Package: libcpr-dev Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends}, libcpr1 Description: C++ wrapper around the libcurl library - development kit This package contains the header files and development libraries of cpr, Curl for People. . The project is inspried by the Python Request project. Using the more expressive language facilities of C++11, it captures the essence of making network calls into a few concise idioms. Package: libcpr1 Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends}, Description: C++ wrapper around the libcurl library - runtime library This package contains the runtime, shared library of cpr, Curl for People. . The project is inspried by the Python Request project. Using the more expressive language facilities of C++11, it captures the essence of making network calls into a few concise idioms. cpr-1.11.2/package-build/debian-libcpr/copyright000066400000000000000000000036711475657722200215260ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: libcpr Source: https://github.com/libcpr/cpr Files: .clang-format .clang-tidy .github/* CMakeLists.txt CODE_OF_CONDUCT.md CONTRIBUTING.md CppCheckSuppressions.txt README.md cmake/* cpr-config.cmake cpr/* include/* nuget/* package-build/* debian/* Copyright: 2017-2021 Huu Nguyen 2022 libcpr and many other contributors License: Expat MIT License . Copyright (c) 2017-2021 Huu Nguyen Copyright (c) 2022 libcpr and many other contributors . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: test/* Copyright: 2022 libcpr and many other contributors License: GPL-3 On Debian systems, the full text of the GNU General Public License version 3 can be found in the file `/usr/share/common-licenses/GPL-3'. cpr-1.11.2/package-build/debian-libcpr/libcpr-dev.install000066400000000000000000000000531475657722200232010ustar00rootroot00000000000000usr/include usr/lib/*/*.so usr/lib/*/cmake cpr-1.11.2/package-build/debian-libcpr/libcpr1.install000066400000000000000000000000211475657722200225010ustar00rootroot00000000000000usr/lib/*/*.so.* cpr-1.11.2/package-build/debian-libcpr/rules000077500000000000000000000006261475657722200206500ustar00rootroot00000000000000#!/usr/bin/make -f export DH_VERBOSE = 1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed %: dh $@ override_dh_auto_configure: dh_auto_configure -- \ -DCMAKE_LIBRARY_ARCHITECTURE="$(DEB_TARGET_MULTIARCH)" -DCMAKE_BUILD_TYPE=Release \ -DCPR_USE_SYSTEM_CURL=ON -DCURL_ZLIB=OFF -DBUILD_SHARED_LIBS=ON cpr-1.11.2/package-build/debian-libcpr/source/000077500000000000000000000000001475657722200210645ustar00rootroot00000000000000cpr-1.11.2/package-build/debian-libcpr/source/format000066400000000000000000000000141475657722200222720ustar00rootroot000000000000003.0 (quilt) cpr-1.11.2/scripts/000077500000000000000000000000001475657722200140705ustar00rootroot00000000000000cpr-1.11.2/scripts/check_clang_format.sh000077500000000000000000000021771475657722200202270ustar00rootroot00000000000000#!/bin/bash # Based on: https://gist.github.com/leilee/1d0915a583f8f29414cc21cd86e7151b # Checks if all files are formatted based on the clang-format formatting rules. # Execute as follows: # ./scripts/check_clang_format.sh tests src printf "📑 Checking if your code fulfills all clang-format rules...\n" RET_CODE=0 function format() { for f in $(find $@ -name '*.h' -or -name '*.hpp' -or -name '*.c' -or -name '*.cpp'); do clang-format -i --dry-run --Werror --style=file ${f}; ret=$? if [ $ret -ne 0 ]; then RET_CODE=$ret fi done echo "~~~ $@ directory checked ~~~"; } # Check all of the arguments first to make sure they're all directories for dir in "$@"; do if [ ! -d "${dir}" ]; then echo "${dir} is not a directory"; else format ${dir}; fi done RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' if [ $RET_CODE -eq 0 ]; then printf "✅ ${GREEN}Everything up to standard :party: ${NC}\n" else printf "❌ ${RED}Not up to formatting standard :sad_face: ${NC}\n" echo "Try running run_clang_format.sh to format all files." fi exit $RET_CODEcpr-1.11.2/scripts/delete_build_dir.sh000077500000000000000000000002551475657722200177100ustar00rootroot00000000000000#!/bin/bash printf "🗑️ Clearing your build directory...\n" rm -rf build/* GREEN='\033[0;32m' NC='\033[0m' printf "✅ ${GREEN}Done. Build directory deleted.${NC}\n"cpr-1.11.2/scripts/run_clang_format.sh000077500000000000000000000014151475657722200177500ustar00rootroot00000000000000#!/bin/bash # Based on: https://gist.github.com/leilee/1d0915a583f8f29414cc21cd86e7151b # Run from the project root directory as follows: # ./scripts/run_clang_format.sh tests src printf "📝 Running clang-format...\n" function format() { for f in $(find $@ -name '*.h' -or -name '*.hpp' -or -name '*.c' -or -name '*.cpp'); do echo "format ${f}"; clang-format -i --style=file ${f}; done echo "~~~ $@ directory formatted ~~~"; } # Check all of the arguments first to make sure they're all directories for dir in "$@"; do if [ ! -d "${dir}" ]; then echo "${dir} is not a directory"; else format ${dir}; fi done GREEN='\033[0;32m' NC='\033[0m' printf "✅ ${GREEN}Done. All files were formatted (if required).${NC}\n" cpr-1.11.2/test/000077500000000000000000000000001475657722200133605ustar00rootroot00000000000000cpr-1.11.2/test/CMakeLists.txt000066400000000000000000000120241475657722200161170ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) find_package(Threads REQUIRED) if (ENABLE_SSL_TESTS) add_library(test_server STATIC abstractServer.cpp httpServer.cpp httpsServer.cpp) else () add_library(test_server STATIC abstractServer.cpp httpServer.cpp) endif() if(WIN32) target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest PUBLIC mongoose ws2_32 wsock32) else() target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest PUBLIC mongoose) endif() macro(add_cpr_test _TEST_NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp) target_link_libraries(${_TEST_NAME}_tests PRIVATE test_server GTest::GTest cpr::cpr ${CURL_LIB}) add_test(NAME cpr_${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests) # Group under the "tests" project folder in IDEs such as Visual Studio. set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") if(WIN32 AND BUILD_SHARED_LIBS) add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $) add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $) endif() endmacro() add_cpr_test(get) add_cpr_test(post) add_cpr_test(session) add_cpr_test(prepare) add_cpr_test(async) if(CPR_BUILD_TESTS_PROXY) add_cpr_test(proxy) add_cpr_test(proxy_auth) endif() add_cpr_test(head) add_cpr_test(delete) add_cpr_test(put) add_cpr_test(callback) add_cpr_test(raw_body) add_cpr_test(options) add_cpr_test(patch) add_cpr_test(error) add_cpr_test(alternating) add_cpr_test(util) add_cpr_test(structures) add_cpr_test(encoded_auth) add_cpr_test(version) add_cpr_test(download) add_cpr_test(interceptor) add_cpr_test(interceptor_multi) add_cpr_test(multiperform) add_cpr_test(resolve) add_cpr_test(multiasync) add_cpr_test(file_upload) add_cpr_test(singleton) add_cpr_test(threadpool) if (ENABLE_SSL_TESTS) add_cpr_test(ssl) # Install all ssl keys and certs. Explicit copy for each file to prevent issues when copying on Windows. add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $/data/certificates $/data/keys) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/client.crt $/data/certificates/client.crt) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/root-ca.crt $/data/certificates/root-ca.crt) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/sub-ca.crt $/data/certificates/sub-ca.crt) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/ca-bundle.crt $/data/certificates/ca-bundle.crt) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/server.crt $/data/certificates/server.crt) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/client.key $/data/keys/client.key) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/root-ca.key $/data/keys/root-ca.key) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.key $/data/keys/server.key) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.pub $/data/keys/server.pub) endif() add_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 $/data/test_file_hello_äüöp_2585_你好.txt) add_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 $/data/test_file_hello_äüöp_2585.txt) add_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file.txt $/data/test_file.txt) file(INSTALL data DESTINATION data) cpr-1.11.2/test/LICENSE000066400000000000000000001046371475657722200144000ustar00rootroot00000000000000This license applies to everything inside this directory and all subdirectories. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .cpr-1.11.2/test/abstractServer.cpp000066400000000000000000000103061475657722200170560ustar00rootroot00000000000000#include "abstractServer.hpp" namespace cpr { void AbstractServer::SetUp() { Start(); } void AbstractServer::TearDown() { Stop(); } void AbstractServer::Start() { should_run = true; serverThread = std::make_shared(&AbstractServer::Run, this); serverThread->detach(); std::unique_lock server_lock(server_mutex); server_start_cv.wait(server_lock); } void AbstractServer::Stop() { should_run = false; std::unique_lock server_lock(server_mutex); server_stop_cv.wait(server_lock); } static void EventHandler(mg_connection* conn, int event, void* event_data, void* context) { switch (event) { case MG_EV_READ: case MG_EV_WRITE: /** Do nothing. Just for housekeeping. **/ break; case MG_EV_POLL: /** Do nothing. Just for housekeeping. **/ break; case MG_EV_CLOSE: /** Do nothing. Just for housekeeping. **/ break; case MG_EV_ACCEPT: /* Initialize HTTPS connection if Server is an HTTPS Server */ static_cast(context)->acceptConnection(conn); break; case MG_EV_CONNECT: /** Do nothing. Just for housekeeping. **/ break; case MG_EV_HTTP_CHUNK: { /** Do nothing. Just for housekeeping. **/ } break; case MG_EV_HTTP_MSG: { AbstractServer* server = static_cast(context); server->OnRequest(conn, static_cast(event_data)); } break; default: break; } } void AbstractServer::Run() { // Setup a new mongoose http server. memset(&mgr, 0, sizeof(mg_mgr)); initServer(&mgr, EventHandler); // Notify the main thread that the server is up and runing: server_start_cv.notify_all(); // Main server loop: while (should_run) { // NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers) mg_mgr_poll(&mgr, 100); } // Shutdown and cleanup: timer_args.clear(); mg_mgr_free(&mgr); // Notify the main thread that we have shut down everything: server_stop_cv.notify_all(); } static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; /** * Decodes the given BASE64 string to a normal string. * Source: https://gist.github.com/williamdes/308b95ac9ef1ee89ae0143529c361d37 **/ std::string AbstractServer::Base64Decode(const std::string& in) { std::string out; std::vector T(256, -1); for (size_t i = 0; i < 64; i++) T[base64_chars[i]] = static_cast(i); int val = 0; int valb = -8; for (unsigned char c : in) { if (T[c] == -1) { break; } val = (val << 6) + T[c]; valb += 6; if (valb >= 0) { out.push_back(char((val >> valb) & 0xFF)); valb -= 8; } } return out; } // Sends error similar like in mongoose 6 method mg_http_send_error // https://github.com/cesanta/mongoose/blob/6.18/mongoose.c#L7081-L7089 void AbstractServer::SendError(mg_connection* conn, int code, std::string& reason) { std::string headers{"Content-Type: text/plain\r\nConnection: close\r\n"}; mg_http_reply(conn, code, headers.c_str(), reason.c_str()); } // Checks whether a pointer to a connection is still managed by a mg_mgr. // This check tells whether it is still possible to send a message via the given connection // Note that it is still possible that the pointer of an old connection object may be reused by mongoose. // In this case, the active connection might refer to a different connection than the one the caller refers to bool AbstractServer::IsConnectionActive(mg_mgr* mgr, mg_connection* conn) { mg_connection* c{mgr->conns}; while (c) { if (c == conn) { return true; } c = c->next; } return false; } uint16_t AbstractServer::GetRemotePort(const mg_connection* conn) { return (conn->rem.port >> 8) | (conn->rem.port << 8); } uint16_t AbstractServer::GetLocalPort(const mg_connection* conn) { return (conn->loc.port >> 8) | (conn->loc.port << 8); } } // namespace cpr cpr-1.11.2/test/abstractServer.hpp000066400000000000000000000035471475657722200170740ustar00rootroot00000000000000#ifndef CPR_TEST_ABSTRACT_SERVER_SERVER_H #define CPR_TEST_ABSTRACT_SERVER_SERVER_H #include #include #include #include #include #include #include "cpr/cpr.h" #include "mongoose.h" namespace cpr { // Helper struct for functions using timers to simulate slow connections struct TimerArg { mg_mgr* mgr; mg_connection* connection; unsigned long connection_id; mg_timer timer; unsigned counter; explicit TimerArg(mg_mgr* m, mg_connection* c, mg_timer&& t) : mgr{m}, connection{c}, connection_id{0}, timer{t}, counter{0} {} ~TimerArg() { mg_timer_free(&mgr->timers, &timer); } }; class AbstractServer : public testing::Environment { public: ~AbstractServer() override = default; void SetUp() override; void TearDown() override; void Start(); void Stop(); virtual std::string GetBaseUrl() = 0; virtual uint16_t GetPort() = 0; virtual void acceptConnection(mg_connection* conn) = 0; virtual void OnRequest(mg_connection* conn, mg_http_message* msg) = 0; private: std::shared_ptr serverThread{nullptr}; std::mutex server_mutex; std::condition_variable server_start_cv; std::condition_variable server_stop_cv; std::atomic should_run{false}; void Run(); protected: mg_mgr mgr{}; std::vector> timer_args{}; virtual mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) = 0; static std::string Base64Decode(const std::string& in); static void SendError(mg_connection* conn, int code, std::string& reason); static bool IsConnectionActive(mg_mgr* mgr, mg_connection* conn); static uint16_t GetRemotePort(const mg_connection* conn); static uint16_t GetLocalPort(const mg_connection* conn); }; } // namespace cpr #endif cpr-1.11.2/test/alternating_tests.cpp000066400000000000000000000116031475657722200176170ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(AlternatingTests, PutGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Header reflect PUT"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Get(url); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(AlternatingTests, PutGetPutGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Header reflect PUT"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Get(url); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Header reflect PUT"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Get(url); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(AlternatingTests, HeadGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { // Head shouldn't return a body Response response = cpr::Head(url); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Get(url); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(AlternatingTests, PutHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Header reflect PUT"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { // Head shouldn't return a body Response response = cpr::Head(url); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(AlternatingTests, PutPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Header reflect PUT"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Payload payload{{"x", "5"}}; Response response = cpr::Post(url, payload); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/async_tests.cpp000066400000000000000000000052571475657722200164340ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include "cpr/filesystem.h" #include "cpr/api.h" #include "cpr/response.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } TEST(AsyncTests, AsyncGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; cpr::AsyncResponse future = cpr::GetAsync(url); std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } TEST(AsyncTests, AsyncGetMultipleTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::GetAsync(url)); } for (cpr::AsyncResponse& future : responses) { std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } } TEST(AsyncTests, AsyncGetMultipleReflectTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 100; ++i) { Parameters p{{"key", std::to_string(i)}}; responses.emplace_back(cpr::GetAsync(url, p)); } int i = 0; for (cpr::AsyncResponse& future : responses) { std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); Url expected_url{url + "?key=" + std::to_string(i)}; EXPECT_EQ(expected_url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); ++i; } } TEST(AsyncTests, AsyncDownloadTest) { cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::AsyncResponse future = cpr::DownloadAsync(fs::path{"/tmp/aync_download"}, url, cpr::Header{{"Accept-Encoding", "gzip"}}, cpr::WriteCallback{write_data, 0}); cpr::Response response = future.get(); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/callback_tests.cpp000066400000000000000000001137461475657722200170560ustar00rootroot00000000000000#include #include #include #include #include #include #include "cpr/cpr.h" #include "cpr/cprtypes.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); std::chrono::milliseconds sleep_time{50}; std::chrono::seconds zero{0}; int status_callback(int& status_code, Response r) { status_code = r.status_code; return r.status_code; } int status_callback_ref(int& status_code, const Response& r) { status_code = r.status_code; return r.status_code; } std::string text_callback(std::string& expected_text, Response r) { expected_text = r.text; return r.text; } std::string text_callback_ref(std::string& expected_text, const Response& r) { expected_text = r.text; return r.text; } TEST(CallbackGetTests, CallbackGetLambdaStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::GetCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackGetTests, CallbackGetLambdaTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::GetCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackGetTests, CallbackGetLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::GetCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackGetTests, CallbackGetLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::GetCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackGetTests, CallbackGetFunctionStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::GetCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackGetTests, CallbackGetFunctionTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::GetCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackGetTests, CallbackGetFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::GetCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackGetTests, CallbackGetFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::GetCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteLambdaStatusTest) { Url url{server->GetBaseUrl() + "/delete.html"}; int status_code = 0; auto future = cpr::DeleteCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteLambdaTextTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::string expected_text{}; auto future = cpr::DeleteCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/delete.html"}; int status_code = 0; auto future = cpr::DeleteCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::string expected_text{}; auto future = cpr::DeleteCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteFunctionStatusTest) { Url url{server->GetBaseUrl() + "/delete.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::DeleteCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteFunctionTextTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::DeleteCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/delete.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::DeleteCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackDeleteTests, CallbackDeleteFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::DeleteCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackHeadTests, CallbackHeadLambdaStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::HeadCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackHeadTests, CallbackHeadLambdaTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::HeadCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackHeadTests, CallbackHeadLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::HeadCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackHeadTests, CallbackHeadLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::HeadCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackHeadTests, CallbackHeadFunctionStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::HeadCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackHeadTests, CallbackHeadFunctionTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::HeadCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackHeadTests, CallbackHeadFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::HeadCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackHeadTests, CallbackHeadFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::HeadCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPostTests, CallbackPostLambdaStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; // NOLINTNEXTLINE(google-runtime-int) long status_code = 0; auto future = cpr::PostCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPostTests, CallbackPostLambdaTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PostCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPostTests, CallbackPostLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; // NOLINTNEXTLINE(google-runtime-int) long status_code = 0; auto future = cpr::PostCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPostTests, CallbackPostLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PostCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPostTests, CallbackPostFunctionStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PostCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPostTests, CallbackPostFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PostCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPostTests, CallbackPostFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PostCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPostTests, CallbackPostFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PostCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPutTests, CallbackPutLambdaStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto future = cpr::PutCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPutTests, CallbackPutLambdaTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PutCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPutTests, CallbackPutLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto future = cpr::PutCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPutTests, CallbackPutLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PutCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPutTests, CallbackPutFunctionStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PutCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPutTests, CallbackPutFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PutCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPutTests, CallbackPutFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PutCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPutTests, CallbackPutFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PutCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsLambdaStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::OptionsCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsLambdaTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::OptionsCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto future = cpr::OptionsCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto future = cpr::OptionsCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsFunctionStatusTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::OptionsCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsFunctionTextTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::OptionsCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::OptionsCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackOptionsTests, CallbackOptionsFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::OptionsCallback(callback, url); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPatchTests, CallbackPatchLambdaStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto future = cpr::PatchCallback( [&status_code](Response r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPatchTests, CallbackPatchLambdaTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PatchCallback( [&expected_text](Response r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPatchTests, CallbackPatchLambdaStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto future = cpr::PatchCallback( [&status_code](const Response& r) { status_code = r.status_code; return r.status_code; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPatchTests, CallbackPatchLambdaTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto future = cpr::PatchCallback( [&expected_text](const Response& r) { expected_text = r.text; return r.text; }, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPatchTests, CallbackPatchFunctionStatusTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PatchCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPatchTests, CallbackPatchFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PatchCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackPatchTests, CallbackPatchFunctionStatusReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; int status_code = 0; auto callback = std::function(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1)); auto future = cpr::PatchCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(status_code, future.get()); } TEST(CallbackPatchTests, CallbackPatchFunctionTextReferenceTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::string expected_text{}; auto callback = std::function(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1)); auto future = cpr::PatchCallback(callback, url, payload); std::this_thread::sleep_for(sleep_time); EXPECT_EQ(future.wait_for(zero), std::future_status::ready); EXPECT_EQ(expected_text, future.get()); } TEST(CallbackDataTests, CallbackReadFunctionCancelTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, cpr::ReadCallback([](char* /*buffer*/, size_t& /*size*/, intptr_t /*userdata*/) -> size_t { return false; })); EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR)); } TEST(CallbackDataTests, CallbackReadFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; unsigned count = 0; Response response = cpr::Post(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t { std::string data; ++count; switch (count) { case 1: data = "x="; break; case 2: data = "5"; break; default: return false; } std::copy(data.begin(), data.end(), buffer); size = data.size(); return true; }}); EXPECT_EQ(2, count); EXPECT_EQ(expected_text, response.text); } TEST(CallbackDataTests, CallbackReadFunctionTextTestPut) { Url url{server->GetBaseUrl() + "/put.html"}; std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; unsigned count = 0; Response response = cpr::Put(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t { std::string data; ++count; switch (count) { case 1: data = "x="; break; case 2: data = "5"; break; default: return false; } std::copy(data.begin(), data.end(), buffer); size = data.size(); return true; }}); EXPECT_EQ(2, count); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } /** * Checks if the "Transfer-Encoding" header will be kept when using headers and a read callback. * Issue: https://github.com/whoshuu/cpr/issues/517 **/ TEST(CallbackDataTests, CallbackReadFunctionHeaderTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::string data = "Test"; Response response = cpr::Post(url, cpr::ReadCallback{-1, [&](char* /*buffer*/, size_t& size, intptr_t /*userdata*/) -> size_t { size = 0; return true; }}, Header{{"TestHeader", "42"}}); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); // Check Header: EXPECT_EQ(std::string{"42"}, response.header["TestHeader"]); // Set by us EXPECT_TRUE(response.header.find("TestHeader") != response.header.end()); EXPECT_EQ(std::string{"chunked"}, response.header["Transfer-Encoding"]); // Set by the read callback EXPECT_TRUE(response.header.find("Transfer-Encoding") != response.header.end()); } /* cesanta mongoose doesn't support chunked requests yet TEST(CallbackDataTests, CallbackReadFunctionChunkedTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; unsigned count = 0; Response response = cpr::Post(url, cpr::ReadCallback{[&count](char* buffer, size_t & size) -> size_t { std::string data; ++ count; switch (count) { case 1: data = "x="; break; case 2: data = "5"; break; default: data = ""; break; } std::copy(data.begin(), data.end(), buffer); size = data.size(); return true; }}); EXPECT_EQ(3, count); EXPECT_EQ(expected_text, response.text); } */ TEST(CallbackDataTests, CallbackHeaderFunctionCancelTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = Post(url, HeaderCallback{[](const std::string_view& /*header*/, intptr_t /*userdata*/) -> bool { return false; }}); EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR)); } TEST(CallbackDataTests, CallbackHeaderFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::vector expected_headers{"HTTP/1.1 201 Created\r\n", "Content-Type: application/json\r\n", "\r\n"}; std::set response_headers; Post(url, HeaderCallback{[&response_headers](const std::string_view& header, intptr_t /*userdata*/) -> bool { response_headers.insert(std::string{header}); return true; }}); for (std::string& header : expected_headers) { std::cout << header << '\n'; EXPECT_TRUE(response_headers.count(header)); } } TEST(CallbackDataTests, CallbackWriteFunctionCancelTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = Post(url, WriteCallback{[](const std::string_view& /*header*/, intptr_t /*userdata*/) -> bool { return false; }}); EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR)); } TEST(CallbackDataTests, CallbackWriteFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; std::string response_text; Post(url, Payload{{"x", "5"}}, WriteCallback{[&response_text](const std::string_view& header, intptr_t /*userdata*/) -> bool { response_text.append(header); return true; }}); EXPECT_EQ(expected_text, response_text); } TEST(CallbackDataTests, CallbackProgressFunctionCancelTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = Post(url, ProgressCallback{[](size_t /*downloadTotal*/, size_t /*downloadNow*/, size_t /*uploadTotal*/, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool { return false; }}); EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR)); } TEST(CallbackDataTests, CallbackProgressFunctionTotalTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Body body{"x=5"}; size_t response_upload = 0; size_t response_download = 0; Response response = Post(url, body, ProgressCallback{[&](size_t downloadTotal, size_t /*downloadNow*/, size_t uploadTotal, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool { response_upload = uploadTotal; response_download = downloadTotal; return true; }}); EXPECT_EQ(body.str().length(), response_upload); EXPECT_EQ(response.text.length(), response_download); } TEST(CallbackDataTests, CallbackDebugFunctionTextTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Body body{"x=5"}; std::string debug_body; Response response = Post(url, body, DebugCallback{[&](DebugCallback::InfoType type, const std::string& data, intptr_t /*userdata*/) { if (type == DebugCallback::InfoType::DATA_OUT) { debug_body = data; } }}); EXPECT_EQ(body.str(), debug_body); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/data/000077500000000000000000000000001475657722200142715ustar00rootroot00000000000000cpr-1.11.2/test/data/certificates/000077500000000000000000000000001475657722200167365ustar00rootroot00000000000000cpr-1.11.2/test/data/certificates/ca-bundle.crt000066400000000000000000000024101475657722200212770ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBrjCCAWCgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdMwBQYDK2VwMDExCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 MDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMDELMAkGA1UEBhMCR0IxEDAOBgNV BAoMB0V4YW1wbGUxDzANBgNVBAMMBlN1YiBDQTAqMAUGAytlcAMhAL9vKw+Jb0jc THPJj/0HKRBIusX9D0Xj4qZEvK3kqXX+o4GNMIGKMA8GA1UdEwEB/wQFMAMBAf8w DgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSbsZshYdxmKzqt7YTxBbbOmYLB/DBI BgNVHR4EQTA/oD0wC4IJbG9jYWxob3N0MAqHCH8AAAH/AAAAMCKHIAAAAAAAAAAA AAAAAAAAAAH/////////////////////MAUGAytlcANBACspVj23xQ46wvlIWimf ofVcl0Nlj1rW1CoTOoA4butJGfJJQoYMzW8Ui/sVokzPoTw7vdOw9u3Knps26c0T Ygk= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBrzCCAWGgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdIwBQYDK2VwMDExCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 MDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMTELMAkGA1UEBhMCR0IxEDAOBgNV BAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwKjAFBgMrZXADIQDI4HsQNDKN xwtOvL2FI7Q+VIoqWLHmsoLaOe1L+JvbyKOBjTCBijAPBgNVHRMBAf8EBTADAQH/ MA4GA1UdDwEB/wQEAwICBDAdBgNVHQ4EFgQUvMDvOfNgjMd7lZ6iDa/JCJcVLwkw SAYDVR0eBEEwP6A9MAuCCWxvY2FsaG9zdDAKhwh/AAAB/wAAADAihyAAAAAAAAAA AAAAAAAAAAAB/////////////////////zAFBgMrZXADQQBCMm6k6vanrNUO3vlc vsecQTSUVxsnl+bD6ANYhs10cuGafZ/lFRh1z4yBxz50b7EIePDeLP2pZlLmz8bm sN8M -----END CERTIFICATE----- cpr-1.11.2/test/data/certificates/client.crt000066400000000000000000000010761475657722200207320ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBejCCASygAwIBAgIRAKy+/CzeW5ALVVSDllVnZdUwBQYDK2VwMDAxCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw NTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LWNsaWVu dDAqMAUGAytlcAMhAPU88C62SwhVGcJhYDp97gf1x5pHjcVD/AZ3PqoQfU68o3Uw czAfBgNVHSMEGDAWgBSbsZshYdxmKzqt7YTxBbbOmYLB/DAMBgNVHRMBAf8EAjAA MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQU TU53uUDblDe4iFsDIV77hIwigPswBQYDK2VwA0EAX0aM10AEe8HxQNXcL2Qf1ryh StldRyLog/s1ZuGidfxwdr7xoZes0yjYaZYhkKLDIf+CR3BwEWik2ppNXE1bDw== -----END CERTIFICATE----- cpr-1.11.2/test/data/certificates/root-ca.crt000066400000000000000000000012041475657722200210110ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBrzCCAWGgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdIwBQYDK2VwMDExCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 MDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMTELMAkGA1UEBhMCR0IxEDAOBgNV BAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwKjAFBgMrZXADIQDI4HsQNDKN xwtOvL2FI7Q+VIoqWLHmsoLaOe1L+JvbyKOBjTCBijAPBgNVHRMBAf8EBTADAQH/ MA4GA1UdDwEB/wQEAwICBDAdBgNVHQ4EFgQUvMDvOfNgjMd7lZ6iDa/JCJcVLwkw SAYDVR0eBEEwP6A9MAuCCWxvY2FsaG9zdDAKhwh/AAAB/wAAADAihyAAAAAAAAAA AAAAAAAAAAAB/////////////////////zAFBgMrZXADQQBCMm6k6vanrNUO3vlc vsecQTSUVxsnl+bD6ANYhs10cuGafZ/lFRh1z4yBxz50b7EIePDeLP2pZlLmz8bm sN8M -----END CERTIFICATE----- cpr-1.11.2/test/data/certificates/server.crt000066400000000000000000000012141475657722200207540ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBtDCCAWagAwIBAgIRAKy+/CzeW5ALVVSDllVnZdQwBQYDK2VwMDAxCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw NTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl cjAqMAUGAytlcAMhACdLUqJFSyspgGKJiXNlnOLU2dO/TLV+b8aIZNAX7EuVo4Gu MIGrMB8GA1UdIwQYMBaAFJuxmyFh3GYrOq3thPEFts6ZgsH8MAwGA1UdEwEB/wQC MAAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIF oDAdBgNVHQ4EFgQUZkdU+CWXVppSVjW0p1JgDOdPMwkwLAYDVR0RBCUwI4IJbG9j YWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMAUGAytlcANBAG1j2RGjm8ef tiMSJ+k04KGjIL7734D+UwidjOSCQnbCVRPofIaDMwuan5IqP97pMnjAsbw/QukX +Z9sFTWjAQk= -----END CERTIFICATE----- cpr-1.11.2/test/data/certificates/sub-ca.crt000066400000000000000000000012041475657722200206170ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBrjCCAWCgAwIBAgIRAKy+/CzeW5ALVVSDllVnZdMwBQYDK2VwMDExCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 MDUwNzEwMTgyMloXDTM0MDUwNTEwMTgyMlowMDELMAkGA1UEBhMCR0IxEDAOBgNV BAoMB0V4YW1wbGUxDzANBgNVBAMMBlN1YiBDQTAqMAUGAytlcAMhAL9vKw+Jb0jc THPJj/0HKRBIusX9D0Xj4qZEvK3kqXX+o4GNMIGKMA8GA1UdEwEB/wQFMAMBAf8w DgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSbsZshYdxmKzqt7YTxBbbOmYLB/DBI BgNVHR4EQTA/oD0wC4IJbG9jYWxob3N0MAqHCH8AAAH/AAAAMCKHIAAAAAAAAAAA AAAAAAAAAAH/////////////////////MAUGAytlcANBACspVj23xQ46wvlIWimf ofVcl0Nlj1rW1CoTOoA4butJGfJJQoYMzW8Ui/sVokzPoTw7vdOw9u3Knps26c0T Ygk= -----END CERTIFICATE----- cpr-1.11.2/test/data/client.cnf000066400000000000000000000003001475657722200162300ustar00rootroot00000000000000# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html [req] prompt = no distinguished_name = dn [dn] CN = test-client cpr-1.11.2/test/data/generate-certificates.sh000077500000000000000000000054171475657722200210740ustar00rootroot00000000000000#!/bin/sh # Generate a CA with a self-signed root certificate that then signs the server certificate # Based on the OpenSSL Cookbook by Ivan Ristic: # https://www.feistyduck.com/library/openssl-cookbook/online/ # # Especially, see chapter 1.5. Creating a private Certification Authority: # https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca.html export KEY_PATH=keys export CRT_PATH=certificates export CA_PATH=ca # Create environment. # $CA_PATH is deleted in the end. # If new certificates need to be issued, this needs to be done before the cleanup in the end. mkdir -p $KEY_PATH $CRT_PATH $CA_PATH/db $CA_PATH/private $CA_PATH/certificates touch $CA_PATH/db/index openssl rand -hex 16 > $CA_PATH/db/serial # Generate all private keys openssl genpkey -algorithm ed25519 -out $KEY_PATH/root-ca.key openssl genpkey -algorithm ed25519 -out $KEY_PATH/sub-ca.key openssl genpkey -algorithm ed25519 -out $KEY_PATH/server.key openssl genpkey -algorithm ed25519 -out $KEY_PATH/client.key # For the server, we also need the public key openssl pkey -in $KEY_PATH/server.key -pubout -out $KEY_PATH/server.pub # Generate a Certificate Signing Request for the Root CA based on a config file openssl req -new \ -config root-ca.cnf -out root-ca.csr \ -key $KEY_PATH/root-ca.key # Self-sign the root certificate openssl ca -batch \ -selfsign -config root-ca.cnf \ -extensions ca_ext \ -in root-ca.csr -out $CRT_PATH/root-ca.crt -notext # Create a Certificate Signing request for the Sub CA openssl req -new \ -config sub-ca.cnf -out sub-ca.csr \ -key $KEY_PATH/sub-ca.key # Issue the Sub CA openssl ca -batch \ -config root-ca.cnf \ -extensions ca_ext \ -in sub-ca.csr -out $CRT_PATH/sub-ca.crt -notext # Create a Certificate Signing request for the server certificate openssl req -new \ -config server.cnf -out server.csr \ -key $KEY_PATH/server.key openssl req -text -in server.csr -noout # Issue the server certificate openssl ca -batch \ -config root-ca.cnf \ -name sub_ca \ -extensions server_ext \ -in server.csr -out $CRT_PATH/server.crt -notext \ -days 1825 # Create a Certificate Signing request for the client certificate openssl req -new \ -config client.cnf -out client.csr \ -key $KEY_PATH/client.key # Issue the client certificate openssl ca -batch \ -config root-ca.cnf \ -name sub_ca \ -extensions client_ext \ -in client.csr -out $CRT_PATH/client.crt -notext \ -days 1825 cp $CRT_PATH/sub-ca.crt $CRT_PATH/ca-bundle.crt cat $CRT_PATH/root-ca.crt >> $CRT_PATH/ca-bundle.crt # Clean up # IMPORTANT: If new certificates should be issued, $CA_PATH and its files MUST NOT be deleted! # New certificates can be created in this script before cleaning up. rm -rf *.csr $CA_PATH cpr-1.11.2/test/data/keys/000077500000000000000000000000001475657722200152445ustar00rootroot00000000000000cpr-1.11.2/test/data/keys/client.key000066400000000000000000000001671475657722200172400ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIIK4CYIlr3jGta1aSNICikX8V4CXv/i6IJTmj68CUQOU -----END PRIVATE KEY----- cpr-1.11.2/test/data/keys/root-ca.key000066400000000000000000000001671475657722200173260ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEICJbx2nPwG8L2S/EKvCHI2q4InmAFAaNVBqdVq13ZpJz -----END PRIVATE KEY----- cpr-1.11.2/test/data/keys/server.key000066400000000000000000000001671475657722200172700ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIGqt/stoQYkwb24d3EUC0LpH2QwKuh+0tftML+wk/N1P -----END PRIVATE KEY----- cpr-1.11.2/test/data/keys/server.pub000066400000000000000000000001611475657722200172600ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAJ0tSokVLKymAYomJc2Wc4tTZ079MtX5vxohk0BfsS5U= -----END PUBLIC KEY----- cpr-1.11.2/test/data/keys/sub-ca.key000066400000000000000000000001671475657722200171340ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIASqWiXeb8UOEbwjVVq/2j49JvbBX2aLAiqjUtHQK2qV -----END PRIVATE KEY----- cpr-1.11.2/test/data/root-ca.cnf000066400000000000000000000054051475657722200163310ustar00rootroot00000000000000# Based on: https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-creating-root.html [default] name = root-ca default_ca = ca_default name_opt = utf8,esc_ctrl,multiline,lname,align [ca_dn] countryName = "GB" organizationName = "Example" commonName = "Root CA" [ca_default] home = ./${ENV::CA_PATH} database = $home/db/index serial = $home/db/serial certificate = ./${ENV::CRT_PATH}/$name.crt private_key = ./${ENV::KEY_PATH}/$name.key RANDFILE = $home/private/random new_certs_dir = $home/certificates unique_subject = no copy_extensions = none default_days = 3650 default_md = sha256 policy = policy_cn_supplied [sub_ca] name = sub-ca name_opt = utf8,esc_ctrl,multiline,lname,align home = ./${ENV::CA_PATH} database = $home/db/index serial = $home/db/serial certificate = ./${ENV::CRT_PATH}/$name.crt private_key = ./${ENV::KEY_PATH}/$name.key RANDFILE = $home/private/random new_certs_dir = $home/certificates unique_subject = no copy_extensions = none default_days = 3650 default_md = sha256 policy = policy_cn_supplied [policy_cn_supplied] countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [req] default_bits = 4096 encrypt_key = yes default_md = sha256 utf8 = yes string_mask = utf8only prompt = no distinguished_name = ca_dn req_extensions = ca_ext [ca_ext] basicConstraints = critical,CA:true keyUsage = critical,keyCertSign subjectKeyIdentifier = hash nameConstraints = @name_constraints [server_ext] authorityKeyIdentifier = keyid:always basicConstraints = critical,CA:false extendedKeyUsage = clientAuth,serverAuth keyUsage = critical,digitalSignature,keyEncipherment subjectKeyIdentifier = hash subjectAltName = DNS:localhost,IP:127.0.0.1,IP:::1 [client_ext] authorityKeyIdentifier = keyid:always basicConstraints = critical,CA:false extendedKeyUsage = clientAuth keyUsage = critical,digitalSignature subjectKeyIdentifier = hash [name_constraints] permitted;DNS.0=localhost permitted;IP.0=127.0.0.1/255.0.0.0 permitted;IP.1=::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff cpr-1.11.2/test/data/server.cnf000066400000000000000000000004171475657722200162710ustar00rootroot00000000000000# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html [req] prompt = no distinguished_name = dn req_extensions = ext [dn] CN = test-server [ext] subjectAltName = DNS:localhost,IP:127.0.0.1,IP:::1 cpr-1.11.2/test/data/sub-ca.cnf000066400000000000000000000012751475657722200161400ustar00rootroot00000000000000[req] default_bits = 4096 encrypt_key = yes default_md = sha256 utf8 = yes string_mask = utf8only prompt = no distinguished_name = sub_ca_dn req_extensions = sub_ca_ext [sub_ca_dn] countryName = "GB" organizationName = "Example" commonName = "Sub CA" [sub_ca_ext] basicConstraints = critical,CA:true keyUsage = critical,keyCertSign subjectKeyIdentifier = hash nameConstraints = @name_constraints [name_constraints] permitted;DNS.0=localhost permitted;IP.0=127.0.0.1/255.0.0.0 permitted;IP.1=::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff cpr-1.11.2/test/data/test_file.txt000066400000000000000000000000471475657722200170110ustar00rootroot00000000000000test content: hello_äüöp_2585_你好cpr-1.11.2/test/data/test_file_hello_äüöp_2585.txt000066400000000000000000000000471475657722200233350ustar00rootroot00000000000000test content: hello_äüöp_2585_你好cpr-1.11.2/test/data/test_file_hello_äüöp_2585_你好.txt000066400000000000000000000000471475657722200257040ustar00rootroot00000000000000test content: hello_äüöp_2585_你好cpr-1.11.2/test/delete_tests.cpp000066400000000000000000000226301475657722200165530ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(DeleteTests, DeleteTest) { Url url{server->GetBaseUrl() + "/delete.html"}; Response response = cpr::Delete(url); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, DeleteUnallowedTest) { Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; Response response = cpr::Delete(url); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, DeleteJsonBodyTest) { Url url{server->GetBaseUrl() + "/delete.html"}; Response response = cpr::Delete(url, cpr::Body{"'foo': 'bar'"}, cpr::Header{{"Content-Type", "application/json"}}); std::string expected_text{"'foo': 'bar'"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteTest) { Url url{server->GetBaseUrl() + "/delete.html"}; Session session; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteUnallowedTest) { Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; Session session; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteJsonBodyTest) { Url url{server->GetBaseUrl() + "/delete.html"}; Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Content-Type", "application/json"}}); session.SetBody(cpr::Body{"{'foo': 'bar'}"}); Response response = session.Delete(); std::string expected_text{"{'foo': 'bar'}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/delete.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteUnallowedAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/delete.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteUnallowedAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, SessionDeleteUnallowedAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, AsyncDeleteTest) { Url url{server->GetBaseUrl() + "/delete.html"}; cpr::AsyncResponse future_response = cpr::DeleteAsync(url); cpr::Response response = future_response.get(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, AsyncDeleteUnallowedTest) { Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; cpr::AsyncResponse future_response = cpr::DeleteAsync(url); cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DeleteTests, AsyncMultipleDeleteTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::DeleteAsync(url)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(DeleteTests, AsyncMultipleDeleteUnallowedTest) { Url url{server->GetBaseUrl() + "/delete_unallowed.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::DeleteAsync(url)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/download_tests.cpp000066400000000000000000000137331475657722200171240ustar00rootroot00000000000000#include #include #include #include "cpr/accept_encoding.h" #include "cpr/cpr.h" #include "cpr/api.h" #include "cpr/callback.h" #include "cpr/cprtypes.h" #include "cpr/session.h" #include "httpServer.hpp" static cpr::HttpServer* server = new cpr::HttpServer(); bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } TEST(DownloadTests, DownloadHeaderGzip) { cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetUrl(url); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(DownloadTests, DownloadAcceptEncodingGzip) { cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetAcceptEncoding(cpr::AcceptEncoding{cpr::AcceptEncodingMethods::gzip}); session.SetUrl(url); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(DownloadTests, RangeTestWholeFile) { const int64_t download_size = 9; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetRange(cpr::Range{std::nullopt, std::nullopt}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } TEST(DownloadTests, RangeTestLowerLimit) { const int64_t download_size = 8; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetRange(cpr::Range{1, std::nullopt}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(206, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } TEST(DownloadTests, RangeTestUpperLimit) { const int64_t download_size = 6; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetRange(cpr::Range{std::nullopt, download_size - 1}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(206, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } TEST(DownloadTests, RangeTestLowerAndUpperLimit) { const int64_t download_size = 2; const int64_t start_from = 2; const int64_t finish_at = start_from + download_size - 1; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetRange(cpr::Range{start_from, finish_at}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(206, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } TEST(DownloadTests, RangeTestMultipleRangesSet) { const int64_t num_parts = 2; const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 6 /*data*/; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetMultiRange(cpr::MultiRange{cpr::Range{std::nullopt, 3}, cpr::Range{5, 6}}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(206, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } TEST(DownloadTests, RangeTestMultipleRangesOption) { const int64_t num_parts = 3; const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 7 /*data*/; cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetOption(cpr::MultiRange{cpr::Range{std::nullopt, 2}, cpr::Range{4, 5}, cpr::Range{7, 8}}); cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(206, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(download_size, response.downloaded_bytes); } bool real_write_data(const std::string_view& data, intptr_t userdata) { // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) std::string* dst = reinterpret_cast(userdata); *dst += data; return true; } TEST(DownloadTests, GetDownloadFileLength) { cpr::Url url{server->GetBaseUrl() + "/get_download_file_length.html"}; cpr::Session session; session.SetUrl(url); auto len = session.GetDownloadFileLength(); EXPECT_EQ(len, -1); std::string strFileData; cpr::Response response = session.Download(cpr::WriteCallback{real_write_data, (intptr_t) &strFileData}); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); EXPECT_EQ(strFileData, "this is a file content."); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/encoded_auth_tests.cpp000066400000000000000000000007511475657722200177330ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" using namespace cpr; TEST(EncodedAuthenticationTests, UnicodeEncoderTest) { std::string user = "一二三"; std::string pass = "Hello World!"; EncodedAuthentication pa{user, pass}; EXPECT_EQ(pa.GetUsername(), "%E4%B8%80%E4%BA%8C%E4%B8%89"); EXPECT_EQ(pa.GetPassword(), "Hello%20World%21"); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/error_tests.cpp000066400000000000000000000067701475657722200164510ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include #include "httpServer.hpp" #include "httpsServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(ErrorTests, UnsupportedProtocolFailure) { Url url{"urk://wat.is.this"}; Response response = cpr::Get(url); EXPECT_EQ(0, response.status_code); EXPECT_EQ(ErrorCode::UNSUPPORTED_PROTOCOL, response.error.code); } TEST(ErrorTests, InvalidURLFailure) { Url url{"???"}; Response response = cpr::Get(url); EXPECT_EQ(0, response.status_code); EXPECT_EQ(ErrorCode::URL_MALFORMAT, response.error.code); } TEST(ErrorTests, TimeoutFailure) { Url url{server->GetBaseUrl() + "/timeout.html"}; Response response = cpr::Get(url, cpr::Timeout{1}); EXPECT_EQ(0, response.status_code); EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(ErrorTests, ChronoTimeoutFailure) { Url url{server->GetBaseUrl() + "/timeout.html"}; Response response = cpr::Get(url, cpr::Timeout{std::chrono::milliseconds{1}}); EXPECT_EQ(0, response.status_code); EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(ErrorTests, ConnectTimeoutFailure) { Url url{"http://localhost:67"}; Response response = cpr::Get(url, cpr::ConnectTimeout{1}); EXPECT_EQ(0, response.status_code); // Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT: EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(ErrorTests, ChronoConnectTimeoutFailure) { Url url{"http://localhost:67"}; Response response = cpr::Get(url, cpr::ConnectTimeout{std::chrono::milliseconds{1}}); EXPECT_EQ(0, response.status_code); // Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT: EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(ErrorTests, LowSpeedTimeFailure) { Url url{server->GetBaseUrl() + "/low_speed.html"}; Response response = cpr::Get(url, cpr::LowSpeed{1000, 1}); // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(ErrorTests, LowSpeedBytesFailure) { Url url{server->GetBaseUrl() + "/low_speed_bytes.html"}; Response response = cpr::Get(url, cpr::LowSpeed{1000, 1}); // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(ErrorTests, ProxyFailure) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, cpr::Proxies{{"http", "http://bad_host.libcpr.org"}}); EXPECT_EQ(url, response.url); EXPECT_EQ(0, response.status_code); // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT. EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_PROXY || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(ErrorTests, BoolFalseTest) { Error error; EXPECT_FALSE(error); } TEST(ErrorTests, BoolTrueTest) { Error error; error.code = ErrorCode::UNSUPPORTED_PROTOCOL; EXPECT_TRUE(error); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/file_upload_tests.cpp000066400000000000000000000100271475657722200175710ustar00rootroot00000000000000#include #include #include #include #include #include #include "cpr/api.h" #include "cpr/file.h" #include "cpr/multipart.h" #include "httpServer.hpp" namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp) cpr::HttpServer* server = new cpr::HttpServer(); // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::optional baseDirPath{std::nullopt}; } // namespace cpr::fs::path GetBasePath(const std::string& execPath) { return cpr::fs::path(cpr::fs::path{execPath}.parent_path().string() + "/").make_preferred(); } TEST(FileUploadTests, AsciiFileName) { // Ensure 'baseDirPath' has been set EXPECT_NE(baseDirPath, std::nullopt); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) cpr::fs::path filePath = *baseDirPath / "test_file.txt"; cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; cpr::Response response = cpr::Post(url, mp); // Expected file content std::ifstream ifs(filePath.string()); std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(FileUploadTests, NonAsciiFileName) { // Ensure 'baseDirPath' has been set EXPECT_NE(baseDirPath, std::nullopt); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) cpr::fs::path filePath = *baseDirPath / "test_file_hello_äüöp_2585.txt"; cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; cpr::Response response = cpr::Post(url, mp); // Expected file content std::ifstream ifs(filePath.string()); std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(FileUploadTests, ChineseFileName) { // Ensure 'baseDirPath' has been set EXPECT_NE(baseDirPath, std::nullopt); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) cpr::fs::path filePath = *baseDirPath / "test_file_hello_äüöp_2585.txt"; cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; cpr::Response response = cpr::Post(url, mp); // Expected file content std::ifstream ifs(filePath.string()); std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) baseDirPath = std::make_optional(cpr::fs::path{GetBasePath(argv[0]).string() + "data/"}); ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/get_tests.cpp000066400000000000000000002011561475657722200160720ustar00rootroot00000000000000#include #include #include #include #include #include "cpr/cpr.h" #include "cpr/cprtypes.h" #include "cpr/redirect.h" #include "cpr/session.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(BasicTests, HelloWorldTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, HelloWorldStringViewUrlTest) { Url url{static_cast(server->GetBaseUrl() + "/hello.html")}; Response response = cpr::Get(url); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, HelloWorldNoInterfaceTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Interface iface{""}; // Do not specify any specific interface Response response = cpr::Get(url, iface); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, HelloWorldNoInterfaceStringViewTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Interface iface{std::string_view{}}; // Do not specify any specific interface Response response = cpr::Get(url, iface); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, TimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, Timeout{0L}); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, BasicJsonTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Response response = cpr::Get(url); std::string expected_text{ "[\n" " {\n" " \"first_key\": \"first_value\",\n" " \"second_key\": \"second_value\"\n" " }\n" "]"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, ResourceNotFoundTest) { Url url{server->GetBaseUrl() + "/error.html"}; Response response = cpr::Get(url); std::string expected_text{"Not Found"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(404, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, BadHostTest) { Url url{"http://bad_host/"}; Response response = cpr::Get(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(0, response.status_code); // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT. EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(CookiesTests, BasicCookiesTest) { Url url{server->GetBaseUrl() + "/basic_cookies.html"}; Response response = cpr::Get(url); cpr::Cookies res_cookies{response.cookies}; std::string expected_text{"Basic Cookies"}; cpr::Cookies expectedCookies{ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "en-US", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) { EXPECT_EQ(expectedCookie->GetName(), cookie->GetName()); EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue()); EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain()); EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains()); EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath()); EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly()); EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires()); } } TEST(CookiesTests, EmptyCookieTest) { Url url{server->GetBaseUrl() + "/empty_cookies.html"}; Response response = cpr::Get(url); cpr::Cookies res_cookies{response.cookies}; std::string expected_text{"Empty Cookies"}; cpr::Cookies expectedCookies{ {"SID", "", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) { EXPECT_EQ(expectedCookie->GetName(), cookie->GetName()); EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue()); EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain()); EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains()); EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath()); EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly()); EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires()); } } TEST(CookiesTests, ClientSetCookiesTest) { Url url{server->GetBaseUrl() + "/cookies_reflect.html"}; Cookies cookies{ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "en-US", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; Response response = cpr::Get(url, cookies); std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CookiesTests, UnencodedCookiesTest) { Url url{server->GetBaseUrl() + "/cookies_reflect.html"}; Cookies cookies{ {"SID", "31d4d %$ 96e407aad42", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "en-US", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; cookies.encode = false; Response response = cpr::Get(url, cookies); std::string expected_text{"SID=31d4d %$ 96e407aad42; lang=en-US;"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterTests, SingleParameterTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Parameters parameters{{"key", "value"}}; Response response = cpr::Get(url, parameters); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterTests, SingleParameterOnlyKeyTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Parameters parameters{{"key", ""}}; Response response = cpr::Get(url, parameters); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } TEST(ParameterTests, MultipleParametersTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}}); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterTests, MultipleDynamicParametersTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Parameters parameters{{"key", "value"}}; parameters.Add({"hello", "world"}); parameters.Add({"test", "case"}); Response response = cpr::Get(url, parameters); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationTests, BasicAuthenticationSuccessTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationTests, BasicBearerSuccessTest) { Url url{server->GetBaseUrl() + "/bearer_token.html"}; #if CPR_LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0 Response response = cpr::Get(url, Bearer{"the_token"}); #else Response response = cpr::Get(url, Header{{"Authorization", "Bearer the_token"}}); #endif std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationTests, BasicDigestSuccessTest) { Url url{server->GetBaseUrl() + "/digest_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::DIGEST}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAthenticationParameterTests, BasicAuthenticationSuccessSingleParameterTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessSingleParameterReverseTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersReverseTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"key", "value"}, {"hello", "world"}, {"test", "case"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"case"}, response.header["test"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderReverseTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersReverseTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"key", "value"}, {"hello", "world"}, {"test", "case"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"case"}, response.header["test"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationTests, BasicAuthenticationNullFailureTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationTests, BasicAuthenticationFailureTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureSingleParameterTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"hello", "world"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureMultipleParametersTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderJsonTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Response response = cpr::Get(url, Header{{"content-type", "application/json"}}); std::string expected_text{ "[\n" " {\n" " \"first_key\": \"first_value\",\n" " \"second_key\": \"second_value\"\n" " }\n" "]"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectNoneTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectUpdateHeaderAddSessionTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetHeader(Header{{"Header1", "Value1"}}); session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]); EXPECT_EQ(std::string{}, response.header["Header2"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); session.UpdateHeader(Header{{"Header2", "Value2"}}); response = session.Get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]); EXPECT_EQ(std::string{"Value2"}, response.header["Header2"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } /** * Test case for #532 * https://github.com/whoshuu/cpr/issues/532 **/ TEST(HeaderTests, SessionHeaderReflectTest) { std::unique_ptr session(new cpr::Session()); session->SetUrl({server->GetBaseUrl() + "/header_reflect.html"}); session->SetBody("Some Body to post"); session->SetHeader({{"Content-Type", "application/json"}}); cpr::Response response = session->Post(); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(std::string{"Header reflect POST"}, response.text); EXPECT_EQ(std::string{"application/json"}, response.header["Content-Type"]); } TEST(HeaderTests, HeaderReflectUpdateHeaderUpdateSessionTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetHeader(Header{{"Header1", "Value1"}}); session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]); EXPECT_EQ(std::string{}, response.header["Header2"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); session.UpdateHeader(Header{{"Header1", "Value2"}}); response = session.Get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"Value2"}, response.header["Header1"]); EXPECT_EQ(std::string{}, response.header["Header2"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectEmptyTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectSingleTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectMultipleTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"case"}, response.header["test"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, HeaderReflectCaseInsensitiveTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"HeLlO", "wOrLd"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeaderTests, SetEmptyHeaderTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"hello", ""}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectNoneParametersTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectEmptyParametersTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectSingleParametersTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectMultipleParametersTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"case"}, response.header["test"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Header{{"HeLlO", "wOrLd"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectEmptyParametersReverseTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectSingleParametersReverseTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectMultipleParametersReverseTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"case"}, response.header["test"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersReverseTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"HeLlO", "wOrLd"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]); EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderABTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}, Header{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderACTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderADTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}, Header{{"hello", "world"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{{"hello", "world"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBBTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBCTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBDTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCBTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCCTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCDTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{}, Parameters{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDBTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}, Parameters{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDCTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{}, Parameters{{"one", "two"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDDTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}, Parameters{{"one", "two"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{{"one", "two"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{{"one", "two"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEBTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderECTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEDTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFATest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFBTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFCTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}, Header{}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFDTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFETest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFFTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFGTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFHTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}); EXPECT_EQ("Unauthorized", response.text); EXPECT_EQ(Url{url + "?one=two"}, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(GetRedirectTests, RedirectTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Response response = cpr::Get(url, Redirect(false)); std::string expected_text{"Moved Temporarily"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(302, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(GetRedirectTests, ZeroMaxRedirectsSuccessTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, Redirect(0L)); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(GetRedirectTests, ZeroMaxRedirectsFailureTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Response response = cpr::Get(url, Redirect(0L)); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(301, response.status_code); EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code); } TEST(GetRedirectTests, BasicAuthenticationRedirectSuccessTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"RedirectLocation", "basic_auth.html"}}, Redirect(true, true)); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); std::string resultUrl = "http://user:password@127.0.0.1:" + std::to_string(server->GetPort()) + "/basic_auth.html"; EXPECT_EQ(response.url, resultUrl); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, RequestBodyTest) { Url url{server->GetBaseUrl() + "/body_get.html"}; Body body{"message=abc123"}; Response response = cpr::Get(url, body); std::string expected_text{"abc123"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BasicTests, RequestBodyStringViewTest) { Url url{server->GetBaseUrl() + "/body_get.html"}; Body body{static_cast("message=abc123")}; Response response = cpr::Get(url, body); std::string expected_text{"abc123"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(LimitRateTests, HelloWorldTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, LimitRate(1024, 1024)); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/head_tests.cpp000066400000000000000000000222341475657722200162120ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(HeadTests, BasicHeadTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, ComplexHeadTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, ResourceNotFoundHeadTest) { Url url{server->GetBaseUrl() + "/error.html"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(404, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, BadHostHeadTest) { Url url{"http://bad_host/"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(0, response.status_code); // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT. EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(HeadTests, CookieHeadTest) { Url url{server->GetBaseUrl() + "/basic_cookies.html"}; Response response = cpr::Head(url); cpr::Cookies expectedCookies{ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "en-US", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; cpr::Cookies res_cookies{response.cookies}; EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) { EXPECT_EQ(expectedCookie->GetName(), cookie->GetName()); EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue()); EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain()); EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains()); EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath()); EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly()); EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires()); } } TEST(HeadTests, ParameterHeadTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Parameters parameters{{"key", "value"}}; Response response = cpr::Head(url, parameters); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(Url{url + "?key=value"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, AuthenticationSuccessHeadTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Head(url, Authentication{"user", "password", AuthMode::BASIC}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, AuthenticationNullFailureHeadTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, AuthenticationFailureHeadTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Response response = cpr::Head(url, Authentication{"user", "bad_password", AuthMode::BASIC}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ("text/plain", response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, BearerSuccessHeadTest) { Url url{server->GetBaseUrl() + "/bearer_token.html"}; #if CPR_LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0 Response response = cpr::Get(url, Bearer{"the_token"}); #else Response response = cpr::Get(url, Header{{"Authorization", "Bearer the_token"}}); #endif EXPECT_EQ(std::string{"Header reflect GET"}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, DigestSuccessHeadTest) { Url url{server->GetBaseUrl() + "/digest_auth.html"}; Response response = cpr::Head(url, Authentication{"user", "password", AuthMode::DIGEST}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, HeaderReflectNoneHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Head(url); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, HeaderReflectEmptyHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Head(url, Header{}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, HeaderReflectHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Head(url, Header{{"hello", "world"}}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, SetEmptyHeaderHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Head(url, Header{{"hello", ""}}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, RedirectHeadTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Response response = cpr::Head(url, Redirect(false)); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(302, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, ZeroMaxRedirectsHeadTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Head(url, Redirect(0L)); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(HeadTests, BasicHeadAsyncTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::HeadAsync(url)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/httpServer.cpp000066400000000000000000001150671475657722200162440ustar00rootroot00000000000000#include "httpServer.hpp" #include #include #include #include #include #include #include #include namespace cpr { std::string HttpServer::GetBaseUrl() { return "http://127.0.0.1:" + std::to_string(GetPort()); } uint16_t HttpServer::GetPort() { // Unassigned port number in the ephemeral range return 61936; } mg_connection* HttpServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) { // Based on: https://mongoose.ws/tutorials/http-server/ mg_mgr_init(mgr); std::string port = std::to_string(GetPort()); mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this); if (!c) { throw std::system_error(errno, std::system_category(), "Failed to listen at port " + port); } return c; } void HttpServer::acceptConnection(mg_connection* /* conn */) {} void HttpServer::OnRequestHello(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) { OnRequestOptions(conn, msg); } else { std::string response{"Hello world!"}; std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } void HttpServer::OnRequestRoot(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) { OnRequestOptions(conn, msg); } else { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } } void HttpServer::OnRequestNotFound(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) { OnRequestOptions(conn, msg); } else { std::string errorMessage{"Not Found"}; SendError(conn, 404, errorMessage); } } void HttpServer::OnRequestOptions(mg_connection* conn, mg_http_message* /*msg*/) { std::string headers = "Content-Type: text/plain\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Credentials: true\r\n" "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS\r\n" "Access-Control-Max-Age: 3600\r\n"; std::string response; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestTimeout(mg_connection* conn, mg_http_message* msg) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); OnRequestHello(conn, msg); } void HttpServer::OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg) { std::this_thread::sleep_for(std::chrono::seconds(2)); OnRequestHello(conn, msg); } // Send the header, then send "Hello world!" every 100ms // For this, we use a mongoose timer void HttpServer::OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* /* msg */, TimerArg* timer_arg) { std::string response{"Hello world!"}; mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length() * 20); timer_arg->connection_id = conn->id; mg_timer_init( &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT, // The following lambda function gets executed each time the timer is called. // It sends "Hello world!" to the client each 100ms at most 20 times. [](void* arg) { TimerArg* timer_arg = static_cast(arg); if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) { std::string response{"Hello world!"}; mg_send(timer_arg->connection, response.c_str(), response.length()); ++timer_arg->counter; } else { timer_arg->counter = 20; // Make sure that this timer is never called again } }, timer_arg); } // Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed // The nested call to mg_mgr_poll can lead to problems otherwise void HttpServer::OnRequestLowSpeed(mg_connection* conn, mg_http_message* /*msg*/, mg_mgr* mgr) { std::string response{"Hello world!"}; mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length()); mg_timer_add( mgr, 2000, MG_TIMER_ONCE, [](void* connection) { std::string response{"Hello world!"}; mg_send(static_cast(connection), response.c_str(), response.length()); }, conn); } // Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed // The nested call to mg_mgr_poll can lead to problems otherwise void HttpServer::OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* /*msg*/, TimerArg* timer_arg) { std::string response{'a'}; mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length() * 20); mg_timer_init( &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT, // The following lambda function gets executed each time the timer is called. // It first waits for 2 seconds, then sends "a" to the client each 100ms at most 20 times. [](void* arg) { TimerArg* timer_arg = static_cast(arg); if (timer_arg->counter == 0) { std::this_thread::sleep_for(std::chrono::seconds(2)); } if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) { std::string response{'a'}; mg_send(timer_arg->connection, response.c_str(), response.length()); ++timer_arg->counter; } else { timer_arg->counter = 20; // Make sure that this timer is never called again } }, timer_arg); } std::string HttpServer::GetCookieExpiresIn100HoursString() { static const std::chrono::system_clock::time_point expires = GetCookieExpiresIn100HoursTimePoint(); const std::time_t timeT = std::chrono::system_clock::to_time_t(expires); const std::tm* utcTimeT = std::gmtime(&timeT); // NOLINT (concurrency-mt-unsafe) not relevant here std::stringstream ss; ss << std::put_time(utcTimeT, "%a, %d %b %Y %T GMT"); return ss.str(); } std::chrono::system_clock::time_point HttpServer::GetCookieExpiresIn100HoursTimePoint() { // Cookie timepoints have a maximum resolution of seconds so floor it to that. static const std::chrono::system_clock::time_point expires = std::chrono::floor(std::chrono::system_clock::now() + std::chrono::hours(100)); return expires; } void HttpServer::OnRequestBasicCookies(mg_connection* conn, mg_http_message* /*msg*/) { const std::string expires = GetCookieExpiresIn100HoursString(); std::string cookie1{"SID=31d4d96e407aad42; Expires=" + expires + "; Secure"}; std::string cookie2{"lang=en-US; Expires=" + expires + "; Secure"}; std::string headers = "Content-Type: text/html\r\n" "Set-Cookie: " + cookie1 + "\r\n" "Set-Cookie: " + cookie2 + "\r\n"; std::string response{"Basic Cookies"}; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestEmptyCookies(mg_connection* conn, mg_http_message* /*msg*/) { const std::string expires = GetCookieExpiresIn100HoursString(); std::string cookie1{"SID=; Expires=" + expires + "; Secure"}; std::string cookie2{"lang=; Expires=" + expires + "; Secure"}; std::string headers = "Content-Type: text/html\r\n" "Set-Cookie: " + cookie1 + "\r\n" "Set-Cookie: " + cookie2 + "\r\n"; std::string response{"Empty Cookies"}; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg) { mg_str* request_cookies{nullptr}; if ((request_cookies = mg_http_get_header(msg, "Cookie")) == nullptr) { std::string errorMessage{"Cookie not found"}; SendError(conn, 400, errorMessage); return; } std::string cookie_str{request_cookies->ptr, request_cookies->len}; std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str()); } void HttpServer::OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg) { const std::string expires = GetCookieExpiresIn100HoursString(); mg_str* request_cookies{nullptr}; std::string cookie_str; if ((request_cookies = mg_http_get_header(msg, "Cookie")) != nullptr) { cookie_str = std::string{request_cookies->ptr, request_cookies->len}; } if (cookie_str.find("SID=31d4d96e407aad42") == std::string::npos) { std::string cookie1{"SID=31d4d96e407aad42; Expires=" + expires + "; Secure"}; std::string cookie2{"lang=en-US; Expires=" + expires + "; Secure"}; std::string headers = "Content-Type: text/html\r\n" "Location: http://127.0.0.1:61936/redirection_with_changing_cookies.html\r\n" "Set-Cookie: " + cookie1 + "\r\n" "Set-Cookie: " + cookie2 + "\r\n"; mg_http_reply(conn, 302, headers.c_str(), ""); } else { cookie_str = "Received cookies are: " + cookie_str; std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str()); } } void HttpServer::OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg) { mg_str* requested_auth; std::string auth{"Basic"}; if ((requested_auth = mg_http_get_header(msg, "Authorization")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) { std::string errorMessage{"Unauthorized"}; SendError(conn, 401, errorMessage); return; } std::string auth_string{requested_auth->ptr, requested_auth->len}; size_t basic_token = auth_string.find(' ') + 1; auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token); auth_string = Base64Decode(auth_string); size_t colon = auth_string.find(':'); std::string username = auth_string.substr(0, colon); std::string password = auth_string.substr(colon + 1, auth_string.length() - colon - 1); if (username == "user" && password == "password") { OnRequestHeaderReflect(conn, msg); } else { std::string errorMessage{"Unauthorized"}; SendError(conn, 401, errorMessage); } } void HttpServer::OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg) { mg_str* requested_auth; std::string auth{"Bearer"}; if ((requested_auth = mg_http_get_header(msg, "Authorization")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) { std::string errorMessage{"Unauthorized"}; SendError(conn, 401, errorMessage); return; } std::string auth_string{requested_auth->ptr, requested_auth->len}; size_t basic_token = auth_string.find(' ') + 1; auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token); if (auth_string == "the_token") { OnRequestHeaderReflect(conn, msg); } else { std::string errorMessage{"Unauthorized"}; SendError(conn, 401, errorMessage); } } void HttpServer::OnRequestBasicJson(mg_connection* conn, mg_http_message* /*msg*/) { std::string response = "[\n" " {\n" " \"first_key\": \"first_value\",\n" " \"second_key\": \"second_value\"\n" " }\n" "]"; std::string headers = "Content-Type: application/json\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg) { if (std::string_view{msg->method.ptr, msg->method.len} == "GET") { if (msg->body.len > 0) { std::string errorMessage{"Bad Request: GET shouldn't contain a body."}; SendError(conn, 400, errorMessage); return; } else if (msg->chunk.len > 0) { std::string errorMessage{"Bad Request: GET shouldn't contain a body."}; SendError(conn, 400, errorMessage); return; } } std::string response = "Header reflect " + std::string{msg->method.ptr, msg->method.len}; std::string headers; bool hasContentTypeHeader = false; for (const mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"Content-Type"} == name) { hasContentTypeHeader = true; } if (std::string{"Host"} != name && std::string{"Accept"} != name) { if (header.value.ptr) { headers.append(name + ": " + std::string(header.value.ptr, header.value.len) + "\r\n"); } } } if (!hasContentTypeHeader) { headers.append("Content-Type: text/html\r\n"); } mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg) { // Get the requested target location: std::string location; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"RedirectLocation"} == name) { location = std::string(header.value.ptr, header.value.len); break; } } // Check if the request contains a valid location, else default to 'hello.html': if (location.empty()) { location = "hello.html"; } std::string headers = "Location: " + location + "\r\n"; std::string response = "Moved Temporarily"; mg_http_reply(conn, 302, headers.c_str(), response.c_str()); } void HttpServer::OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg) { // Get the requested target location: std::string location; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"RedirectLocation"} == name) { location = std::string(header.value.ptr, header.value.len); break; } } // Check if the request contains a valid location, else default to 'hello.html': if (location.empty()) { location = "hello.html"; } std::string headers = "Location: " + location + "\r\n"; std::string response = "Moved Permanently"; mg_http_reply(conn, 301, headers.c_str(), response.c_str()); } void HttpServer::OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg) { // Get the requested target location: std::string location; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"RedirectLocation"} == name) { location = std::string(header.value.ptr, header.value.len); break; } } if (location.empty()) { std::string errorMessage{"Redirect location missing"}; SendError(conn, 405, errorMessage); return; } std::string headers = "Location: " + location + "\r\n"; std::string response = "Moved Permanently"; mg_http_reply(conn, 301, headers.c_str(), response.c_str()); } void HttpServer::OnRequestTwoRedirects(mg_connection* conn, mg_http_message* /*msg*/) { std::string response = "Moved Permanently"; std::string headers = "Location: permanent_redirect.html\r\n"; mg_http_reply(conn, 301, headers.c_str(), response.c_str()); } void HttpServer::OnRequestUrlPost(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} != std::string{"POST"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); return; } std::string headers = "Content-Type: application/json\r\n"; std::array x{0}; std::array y{0}; mg_http_get_var(&(msg->body), "x", x.data(), x.size()); mg_http_get_var(&(msg->body), "y", y.data(), y.size()); std::string x_string{x.data()}; std::string y_string{y.data()}; std::string response; if (y_string.empty()) { response = std::string{ "{\n" " \"x\": " + x_string + "\n" "}"}; } else { response = std::string{ "{\n" " \"x\": " + x_string + ",\n" " \"y\": " + y_string + ",\n" " \"sum\": " + std::to_string(atoi(x.data()) + atoi(y.data())) + "\n" "}"}; } mg_http_reply(conn, 201, headers.c_str(), response.c_str()); } void HttpServer::OnRequestBodyGet(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} != std::string{"GET"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); return; } std::array message{}; mg_http_get_var(&(msg->body), "message", message.data(), message.size()); if (msg->body.len <= 0) { std::string errorMessage{"No Content"}; SendError(conn, 405, errorMessage); return; } std::string response = message.data(); std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestJsonPost(mg_connection* conn, mg_http_message* msg) { mg_str* content_type{nullptr}; if ((content_type = mg_http_get_header(msg, "Content-Type")) == nullptr || std::string{content_type->ptr, content_type->len} != "application/json") { std::string errorMessage{"Unsupported Media Type"}; SendError(conn, 415, errorMessage); return; } std::string headers = "Content-Type: application/json\r\n"; mg_http_reply(conn, 201, headers.c_str(), msg->body.ptr); } void HttpServer::OnRequestFormPost(mg_connection* conn, mg_http_message* msg) { size_t pos{0}; mg_http_part part{}; std::string headers = "Content-Type: application/json\r\n"; std::string response{}; response += "{\n"; while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) { response += " \"" + std::string(part.name.ptr, part.name.len) + "\": \""; if (!std::string(part.filename.ptr, part.filename.len).empty()) { response += std::string(part.filename.ptr, part.filename.len) + "="; } response += std::string(part.body.ptr, part.body.len) + "\",\n"; } response.erase(response.find_last_not_of(",\n") + 1); response += "\n}"; mg_http_reply(conn, 201, headers.c_str(), response.c_str()); } void HttpServer::OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg) { size_t pos{0}; mg_http_part part{}; std::string headers = "Content-Type: application/json\r\n"; std::string response{}; response += "{\n"; while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) { response += " \"" + std::string(part.name.ptr, part.name.len) + "\": \""; if (!std::string(part.filename.ptr, part.filename.len).empty()) { response += std::string(part.filename.ptr, part.filename.len) + "="; } response += std::string(part.body.ptr, part.body.len) + "\",\n"; } response.erase(response.find_last_not_of(",\n") + 1); response += "\n}"; mg_http_reply(conn, 201, headers.c_str(), response.c_str()); } void HttpServer::OnRequestDelete(mg_connection* conn, mg_http_message* msg) { bool has_json_header = false; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); std::string value = std::string(header.value.ptr, header.value.len); if (std::string{"Content-Type"} == name && std::string{"application/json"} == value) { has_json_header = true; break; } } if (std::string{msg->method.ptr, msg->method.len} == std::string{"DELETE"}) { std::string headers; std::string response = "Patch success"; if (!has_json_header) { headers = "Content-Type: text/html\r\n"; response = "Delete success"; } else { headers = "Content-Type: application/json\r\n"; response = std::string{msg->body.ptr, msg->body.len}; } mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } else { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } } void HttpServer::OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"DELETE"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } else { std::string headers = "Content-Type: text/html\r\n"; std::string response = "Delete success"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } void HttpServer::OnRequestPut(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"PUT"}) { std::array x{0}; std::array y{0}; mg_http_get_var(&(msg->body), "x", x.data(), x.size()); mg_http_get_var(&(msg->body), "y", y.data(), y.size()); std::string x_string{x.data()}; std::string y_string{y.data()}; std::string headers = "Content-Type: application/json\r\n"; std::string response; if (y_string.empty()) { response = std::string{ "{\n" " \"x\": " + x_string + "\n" "}"}; } else { response = std::string{ "{\n" " \"x\": " + x_string + ",\n" " \"y\": " + y_string + ",\n" " \"sum\": " + std::to_string(atoi(x.data()) + atoi(y.data())) + "\n" "}"}; } mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } else { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } } void HttpServer::OnRequestPostReflect(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} != std::string{"POST"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } std::string response = std::string{msg->body.ptr, msg->body.len}; std::string headers; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name{header.name.ptr, header.name.len}; if (std::string{"Host"} != name && std::string{"Accept"} != name) { if (header.value.ptr) { headers.append(name + ": " + std::string(header.value.ptr, header.value.len) + "\r\n"); } } } mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"PUT"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } else { std::string headers = "Content-Type: text/html\r\n"; std::string response = "Delete success"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } void HttpServer::OnRequestPatch(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"PATCH"}) { std::array x{0}; std::array y{0}; mg_http_get_var(&(msg->body), "x", x.data(), x.size()); mg_http_get_var(&(msg->body), "y", y.data(), y.size()); std::string x_string{x.data()}; std::string y_string{y.data()}; std::string headers = "Content-Type: application/json\r\n"; std::string response; if (y_string.empty()) { response = std::string{ "{\n" " \"x\": " + x_string + "\n" "}"}; } else { response = std::string{ "{\n" " \"x\": " + x_string + ",\n" " \"y\": " + y_string + ",\n" " \"sum\": " + std::to_string(atoi(x.data()) + atoi(y.data())) + "\n" "}"}; } mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } else { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } } void HttpServer::OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"PATCH"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } else { std::string headers = "Content-Type: text/html\r\n"; std::string response = "Delete success"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } void HttpServer::OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg) { if (std::string{msg->method.ptr, msg->method.len} == std::string{"DOWNLOAD"}) { std::string errorMessage{"Method Not Allowed"}; SendError(conn, 405, errorMessage); } else { std::string encoding; std::string range; std::vector> ranges; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"Accept-Encoding"} == name) { encoding = std::string(header.value.ptr, header.value.len); } else if (std::string{"Range"} == name) { range = std::string(header.value.ptr, header.value.len); } } if (encoding.find("gzip") == std::string::npos) { std::string errorMessage{"Invalid encoding: " + encoding}; SendError(conn, 405, errorMessage); return; } if (!range.empty()) { std::string::size_type eq_pos = range.find('='); if (eq_pos == std::string::npos) { std::string errorMessage{"Invalid range header: " + range}; SendError(conn, 405, errorMessage); return; } int64_t current_start_index = eq_pos + 1; int64_t current_end_index; std::string::size_type range_len = range.length(); std::string::size_type com_pos; std::string::size_type sep_pos; bool more_ranges_exists; do { com_pos = range.find(',', current_start_index); if (com_pos < range_len) { current_end_index = com_pos - 1; } else { current_end_index = range_len - 1; } std::pair current_range{0, -1}; sep_pos = range.find('-', current_start_index); if (sep_pos == std::string::npos) { std::string errorMessage{"Invalid range format " + range.substr(current_start_index, current_end_index)}; SendError(conn, 405, errorMessage); return; } if (sep_pos == eq_pos + 1) { std::string errorMessage{"Suffix ranage not supported: " + range.substr(current_start_index, current_end_index)}; SendError(conn, 405, errorMessage); return; } current_range.first = std::strtoll(range.substr(current_start_index, sep_pos - 1).c_str(), nullptr, 10); if (current_range.first == LLONG_MAX || current_range.first == LLONG_MIN) { std::string errorMessage{"Start range is invalid number: " + range.substr(current_start_index, current_end_index)}; SendError(conn, 405, errorMessage); return; } std::string er_str = range.substr(sep_pos + 1, current_end_index); if (!er_str.empty()) { current_range.second = std::strtoll(er_str.c_str(), nullptr, 10); if (current_range.second == 0 || current_range.second == LLONG_MAX || current_range.second == LLONG_MIN) { std::string errorMessage{"End range is invalid number: " + range.substr(current_start_index, current_end_index)}; SendError(conn, 405, errorMessage); return; } } ranges.push_back(current_range); if (current_end_index >= static_cast(range.length() - 1)) { more_ranges_exists = false; } else { // Multiple ranges are separated by ', ' more_ranges_exists = true; current_start_index = current_end_index + 3; } } while (more_ranges_exists); } std::string response = "Download!"; int status_code = 200; std::string headers; if (!ranges.empty()) { // Create response parts std::vector responses; for (std::pair local_range : ranges) { if (local_range.first >= 0) { if (local_range.first >= (int64_t) response.length()) { responses.push_back(""); } else if (local_range.second == -1 || local_range.second >= (int64_t) response.length()) { responses.push_back(response.substr(local_range.first)); } else { responses.push_back(response.substr(local_range.first, local_range.second - local_range.first + 1)); } } } if (responses.size() > 1) { // Create mime multipart response std::string boundary = "3d6b6a416f9b5"; status_code = 206; response.clear(); for (size_t i{0}; i < responses.size(); ++i) { response += "--" + boundary + "\n"; response += "Content-Range: bytes " + std::to_string(ranges.at(i).first) + "-"; if (ranges.at(i).second > 0) { response += std::to_string(ranges.at(i).second); } else { response += std::to_string(responses.at(i).length()); } response += "/" + std::to_string(responses.at(i).length()) + "\n\n"; response += responses.at(i) + "\n"; } response += "--" + boundary + "--"; } else { if (ranges.at(0).second == -1 || ranges.at(0).second >= (int64_t) response.length()) { status_code = ranges.at(0).first > 0 ? 206 : 200; } else { status_code = 206; } response = responses.at(0); if (status_code == 206) { headers = "Content-Range: bytes " + std::to_string(ranges.at(0).first) + "-"; if (ranges.at(0).second > 0) { headers += std::to_string(ranges.at(0).second); } else { headers += std::to_string(response.length()); } headers += "/" + std::to_string(response.length()); } } } if (!headers.empty()) { headers += "\r\n"; } mg_http_reply(conn, status_code, headers.c_str(), response.c_str()); } } void HttpServer::OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg) { std::string response; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"Accept-Encoding"} == name) { response = std::string(header.value.ptr, header.value.len); } } std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg) { std::string response; for (mg_http_header& header : msg->headers) { if (!header.name.ptr) { continue; } std::string name = std::string(header.name.ptr, header.name.len); if (std::string{"Expect"} == name) { response = std::string(header.value.ptr, header.value.len); } } std::string headers = "Content-Type: text/html\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } void HttpServer::OnRequestGetDownloadFileLength(mg_connection* conn, mg_http_message* msg) { auto method = std::string{msg->method.ptr, msg->method.len}; if (method == std::string{"HEAD"}) { mg_http_reply(conn, 405, nullptr, ""); } else { std::string response("this is a file content."); std::string headers = "Content-Type: text/plain\r\n"; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } void HttpServer::OnRequest(mg_connection* conn, mg_http_message* msg) { std::string uri = std::string(msg->uri.ptr, msg->uri.len); if (uri == "/") { OnRequestRoot(conn, msg); } else if (uri == "/hello.html") { OnRequestHello(conn, msg); } else if (uri == "/timeout.html") { OnRequestTimeout(conn, msg); } else if (uri == "/long_timeout.html") { OnRequestLongTimeout(conn, msg); } else if (uri == "/low_speed_timeout.html") { timer_args.emplace_back(std::make_unique(&mgr, conn, mg_timer{})); OnRequestLowSpeedTimeout(conn, msg, timer_args.back().get()); } else if (uri == "/low_speed.html") { OnRequestLowSpeed(conn, msg, &mgr); } else if (uri == "/low_speed_bytes.html") { timer_args.emplace_back(std::make_unique(&mgr, conn, mg_timer{})); OnRequestLowSpeedBytes(conn, msg, timer_args.back().get()); } else if (uri == "/basic_cookies.html") { OnRequestBasicCookies(conn, msg); } else if (uri == "/empty_cookies.html") { OnRequestEmptyCookies(conn, msg); } else if (uri == "/cookies_reflect.html") { OnRequestCookiesReflect(conn, msg); } else if (uri == "/redirection_with_changing_cookies.html") { OnRequestRedirectionWithChangingCookies(conn, msg); } else if (uri == "/basic_auth.html") { OnRequestBasicAuth(conn, msg); } else if (uri == "/bearer_token.html") { OnRequestBearerAuth(conn, msg); } else if (uri == "/digest_auth.html") { OnRequestHeaderReflect(conn, msg); } else if (uri == "/basic.json") { OnRequestBasicJson(conn, msg); } else if (uri == "/header_reflect.html") { OnRequestHeaderReflect(conn, msg); } else if (uri == "/temporary_redirect.html") { OnRequestTempRedirect(conn, msg); } else if (uri == "/permanent_redirect.html") { OnRequestPermRedirect(conn, msg); } else if (uri == "/resolve_permanent_redirect.html") { OnRequestResolvePermRedirect(conn, msg); } else if (uri == "/two_redirects.html") { OnRequestTwoRedirects(conn, msg); } else if (uri == "/url_post.html") { OnRequestUrlPost(conn, msg); } else if (uri == "/body_get.html") { OnRequestBodyGet(conn, msg); } else if (uri == "/json_post.html") { OnRequestJsonPost(conn, msg); } else if (uri == "/form_post.html") { OnRequestFormPost(conn, msg); } else if (uri == "/post_file_upload.html") { OnRequestFileUploadPost(conn, msg); } else if (uri == "/post_reflect.html") { OnRequestPostReflect(conn, msg); } else if (uri == "/delete.html") { OnRequestDelete(conn, msg); } else if (uri == "/delete_unallowed.html") { OnRequestDeleteNotAllowed(conn, msg); } else if (uri == "/put.html") { OnRequestPut(conn, msg); } else if (uri == "/put_unallowed.html") { OnRequestPutNotAllowed(conn, msg); } else if (uri == "/patch.html") { OnRequestPatch(conn, msg); } else if (uri == "/patch_unallowed.html") { OnRequestPatchNotAllowed(conn, msg); } else if (uri == "/download_gzip.html") { OnRequestDownloadGzip(conn, msg); } else if (uri == "/local_port.html") { OnRequestLocalPort(conn, msg); } else if (uri == "/check_accept_encoding.html") { OnRequestCheckAcceptEncoding(conn, msg); } else if (uri == "/check_expect_100_continue.html") { OnRequestCheckExpect100Continue(conn, msg); } else if (uri == "/get_download_file_length.html") { OnRequestGetDownloadFileLength(conn, msg); } else { OnRequestNotFound(conn, msg); } } void HttpServer::OnRequestLocalPort(mg_connection* conn, mg_http_message* /*msg*/) { // send source port number as response for checking SetLocalPort/SetLocalPortRange std::string headers = "Content-Type: text/plain\r\n"; // Convert from big endian to little endian std::string response = std::to_string(AbstractServer::GetRemotePort(conn)); mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } } // namespace cpr cpr-1.11.2/test/httpServer.hpp000066400000000000000000000102401475657722200162340ustar00rootroot00000000000000#ifndef CPR_TEST_HTTP_SERVER_H #define CPR_TEST_HTTP_SERVER_H #include #include "abstractServer.hpp" #include "mongoose.h" namespace cpr { class HttpServer : public AbstractServer { public: ~HttpServer() override = default; std::string GetBaseUrl() override; uint16_t GetPort() override; void OnRequest(mg_connection* conn, mg_http_message* msg) override; /** * Returns the current date and time + 100 hours from when this function was invoked for the first time. **/ static std::chrono::system_clock::time_point GetCookieExpiresIn100HoursTimePoint(); /** * 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. * For example: Wed, 30 Sep 2093 03:18:00 GMT **/ static std::string GetCookieExpiresIn100HoursString(); private: static void OnRequestHello(mg_connection* conn, mg_http_message* msg); static void OnRequestRoot(mg_connection* conn, mg_http_message* msg); static void OnRequestOptions(mg_connection* conn, mg_http_message* msg); static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg); static void OnRequestTimeout(mg_connection* conn, mg_http_message* msg); static void OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg); static void OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* msg, TimerArg* arg); static void OnRequestLowSpeed(mg_connection* conn, mg_http_message* msg, mg_mgr* mgr); static void OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* msg, TimerArg* arg); static void OnRequestBasicCookies(mg_connection* conn, mg_http_message* msg); static void OnRequestEmptyCookies(mg_connection* conn, mg_http_message* msg); static void OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg); static void OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg); static void OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg); static void OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg); static void OnRequestBasicJson(mg_connection* conn, mg_http_message* msg); static void OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg); static void OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg); static void OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg); static void OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg); static void OnRequestTwoRedirects(mg_connection* conn, mg_http_message* msg); static void OnRequestUrlPost(mg_connection* conn, mg_http_message* msg); static void OnRequestPostReflect(mg_connection* conn, mg_http_message* msg); static void OnRequestBodyGet(mg_connection* conn, mg_http_message* msg); static void OnRequestJsonPost(mg_connection* conn, mg_http_message* msg); static void OnRequestFormPost(mg_connection* conn, mg_http_message* msg); static void OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg); static void OnRequestDelete(mg_connection* conn, mg_http_message* msg); static void OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg); static void OnRequestPut(mg_connection* conn, mg_http_message* msg); static void OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg); static void OnRequestPatch(mg_connection* conn, mg_http_message* msg); static void OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg); static void OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg); static void OnRequestLocalPort(mg_connection* conn, mg_http_message* msg); static void OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg); static void OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg); static void OnRequestGetDownloadFileLength(mg_connection* conn, mg_http_message* msg); protected: mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override; void acceptConnection(mg_connection* conn) override; }; } // namespace cpr #endif cpr-1.11.2/test/httpsServer.cpp000066400000000000000000000040701475657722200164160ustar00rootroot00000000000000#include "httpsServer.hpp" #include namespace cpr { HttpsServer::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()) { // See https://mongoose.ws/tutorials/tls/ memset(static_cast(&tlsOpts), 0, sizeof(tlsOpts)); tlsOpts.cert = this->sslCertFileName.c_str(); tlsOpts.certkey = this->sslKeyFileName.c_str(); } std::string HttpsServer::GetBaseUrl() { return "https://127.0.0.1:" + std::to_string(GetPort()); } uint16_t HttpsServer::GetPort() { // Unassigned port in the ephemeral range return 61937; } mg_connection* HttpsServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) { mg_mgr_init(mgr); std::string port = std::to_string(GetPort()); mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this); return c; } void HttpsServer::acceptConnection(mg_connection* conn) { // See https://mongoose.ws/tutorials/tls/ mg_tls_init(conn, &tlsOpts); } void HttpsServer::OnRequest(mg_connection* conn, mg_http_message* msg) { std::string uri = std::string(msg->uri.ptr, msg->uri.len); if (uri == "/hello.html") { OnRequestHello(conn, msg); } else { OnRequestNotFound(conn, msg); } } void HttpsServer::OnRequestNotFound(mg_connection* conn, mg_http_message* /*msg*/) { mg_http_reply(conn, 404, nullptr, "Not Found"); } void HttpsServer::OnRequestHello(mg_connection* conn, mg_http_message* /*msg*/) { std::string response{"Hello world!"}; std::string headers{"Content-Type: text/html\r\n"}; mg_http_reply(conn, 200, headers.c_str(), response.c_str()); } const std::string& HttpsServer::getBaseDirPath() const { return baseDirPath; } const std::string& HttpsServer::getSslCertFileName() const { return sslCertFileName; } const std::string& HttpsServer::getSslKeyFileName() const { return sslKeyFileName; } } // namespace cpr cpr-1.11.2/test/httpsServer.hpp000066400000000000000000000024051475657722200164230ustar00rootroot00000000000000#ifndef CPR_TEST_HTTPS_SERVER_H #define CPR_TEST_HTTPS_SERVER_H #include #include #include "abstractServer.hpp" #include "cpr/cpr.h" #include "cpr/filesystem.h" #include "mongoose.h" namespace cpr { class HttpsServer : public AbstractServer { private: // We don't use fs::path here, as this leads to problems using windows const std::string baseDirPath; const std::string sslCertFileName; const std::string sslKeyFileName; struct mg_tls_opts tlsOpts; public: explicit HttpsServer(fs::path&& baseDirPath, fs::path&& sslCertFileName, fs::path&& sslKeyFileName); ~HttpsServer() override = default; std::string GetBaseUrl() override; uint16_t GetPort() override; void OnRequest(mg_connection* conn, mg_http_message* msg) override; static void OnRequestHello(mg_connection* conn, mg_http_message* msg); static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg); const std::string& getBaseDirPath() const; const std::string& getSslCertFileName() const; const std::string& getSslKeyFileName() const; protected: mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override; void acceptConnection(mg_connection* conn) override; }; } // namespace cpr #endif cpr-1.11.2/test/interceptor_multi_tests.cpp000066400000000000000000000445521475657722200210700ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include "cpr/interceptor.h" #include "cpr/response.h" #include "cpr/session.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); class HiddenHelloWorldRedirectInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); std::shared_ptr session = multi.GetSessions().front().first; // Save original url Url old_url = session->GetFullRequestUrl(); // Rewrite the url Url url{server->GetBaseUrl() + "/hello.html"}; session->SetUrl(url); // Proceed the chain std::vector response = proceed(multi); EXPECT_FALSE(response.empty()); // Restore the url again response.front().url = old_url; return response; } }; class ChangeStatusCodeInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); std::shared_ptr session = multi.GetSessions().front().first; // Proceed the chain std::vector response = proceed(multi); EXPECT_FALSE(response.empty()); // Change the status code response.front().status_code = 12345; return response; } }; class SetBasicAuthInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); std::shared_ptr session = multi.GetSessions().front().first; // Set authentication session->SetAuth(Authentication{"user", "password", AuthMode::BASIC}); // Proceed the chain return proceed(multi); } }; class SetUnsupportedProtocolErrorInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); std::shared_ptr session = multi.GetSessions().front().first; // Proceed the chain std::vector response = proceed(multi); EXPECT_FALSE(response.empty()); // Set unsupported protocol error response.front().error = Error{CURLE_UNSUPPORTED_PROTOCOL, "SetErrorInterceptorMulti"}; // Return response return response; } }; class ChangeRequestMethodToGetInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::GET_REQUEST; return proceed(multi); } }; class ChangeRequestMethodToPostInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::POST_REQUEST; multi.GetSessions().front().first->SetOption(Payload{{"x", "5"}}); return proceed(multi); } }; class ChangeRequestMethodToPutInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::PUT_REQUEST; multi.GetSessions().front().first->SetOption(Payload{{"x", "5"}}); return proceed(multi); } }; class ChangeRequestMethodToDeleteInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::DELETE_REQUEST; return proceed(multi); } }; bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } class ChangeRequestMethodToDownloadCallbackInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::DOWNLOAD_REQUEST; PrepareDownloadSession(multi, 0, WriteCallback{write_data, 0}); return proceed(multi); } }; class ChangeRequestMethodToHeadInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::HEAD_REQUEST; return proceed(multi); } }; class ChangeRequestMethodToOptionsInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::OPTIONS_REQUEST; return proceed(multi); } }; class ChangeRequestMethodToPatchInterceptorMulti : public InterceptorMulti { public: std::vector intercept(MultiPerform& multi) override { EXPECT_FALSE(multi.GetSessions().empty()); multi.GetSessions().front().second = MultiPerform::HttpMethod::PATCH_REQUEST; multi.GetSessions().front().first->SetOption(Payload{{"x", "5"}}); return proceed(multi); } }; TEST(InterceptorMultiTest, HiddenUrlRewriteInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/basic.json"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeStatusCodeInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); long expected_status_code{12345}; EXPECT_EQ(url, response.front().url); EXPECT_EQ(expected_status_code, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, DownloadChangeStatusCodeInterceptorMultiTest) { cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Download(cpr::WriteCallback{write_data, 0}); EXPECT_EQ(response.size(), 1); long expected_status_code{12345}; EXPECT_EQ(url, response.front().url); EXPECT_EQ(expected_status_code, response.front().status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, SetBasicAuthInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"text/html"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, SetUnsupportedProtocolErrorInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_error_message{"SetErrorInterceptorMulti"}; ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL}; EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"text/html"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(expected_error_message, response.front().error.message); EXPECT_EQ(expected_error_code, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToGetInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Head(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"text/html"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToPostInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Head(); EXPECT_EQ(response.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"application/json"}, response.front().header["content-type"]); EXPECT_EQ(201, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToPutInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/put.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"application/json"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToPatchInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/patch.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"application/json"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToOptionsInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{""}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.front().header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToHeadInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); EXPECT_EQ(std::string{}, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"text/html"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToDownloadCallbackInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/download_gzip.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session->SetTimeout(Timeout{2000}); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Put(); EXPECT_EQ(response.size(), 1); EXPECT_EQ(url, response.front().url); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ChangeRequestMethodToDownloadCallbackInterceptorMultiMixTest) { Url url{server->GetBaseUrl() + "/download_gzip.html"}; std::shared_ptr session1 = std::make_shared(); session1->SetUrl(url); session1->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session1->SetTimeout(Timeout{2000}); std::shared_ptr session2 = std::make_shared(); session2->SetUrl(url); session2->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session2->SetTimeout(Timeout{2000}); MultiPerform multi; multi.AddSession(session1); multi.AddSession(session2); // Changes only one of two sessions to download, so it is expected to throw an exception here since we can not mix them. multi.AddInterceptor(std::make_shared()); EXPECT_THROW(multi.Put(), std::invalid_argument); } TEST(InterceptorMultiTest, ChangeRequestMethodToDeleteInterceptorMultiTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(std::string{"text/html"}, response.front().header["content-type"]); EXPECT_EQ(200, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, TwoInterceptorMultisTest) { Url url{server->GetBaseUrl() + "/basic.json"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Hello world!"}; long expected_status_code{12345}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(expected_status_code, response.front().status_code); EXPECT_EQ(ErrorCode::OK, response.front().error.code); } TEST(InterceptorMultiTest, ThreeInterceptorMultisTest) { Url url{server->GetBaseUrl() + "/basic.json"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multi; multi.AddSession(session); multi.AddInterceptor(std::make_shared()); multi.AddInterceptor(std::make_shared()); multi.AddInterceptor(std::make_shared()); std::vector response = multi.Get(); EXPECT_EQ(response.size(), 1); std::string expected_text{"Hello world!"}; long expected_status_code{12345}; std::string expected_error_message{"SetErrorInterceptorMulti"}; ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL}; EXPECT_EQ(expected_text, response.front().text); EXPECT_EQ(url, response.front().url); EXPECT_EQ(expected_status_code, response.front().status_code); EXPECT_EQ(expected_error_message, response.front().error.message); EXPECT_EQ(expected_error_code, response.front().error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); }cpr-1.11.2/test/interceptor_tests.cpp000066400000000000000000000335031475657722200176500ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); class HiddenHelloWorldRedirectInterceptor : public Interceptor { public: Response intercept(Session& session) override { // Save original url Url old_url = session.GetFullRequestUrl(); // Rewrite the url Url url{server->GetBaseUrl() + "/hello.html"}; session.SetUrl(url); // Proceed the chain Response response = proceed(session); // Restore the url again response.url = old_url; return response; } }; class ChangeStatusCodeInterceptor : public Interceptor { public: Response intercept(Session& session) override { // Proceed the chain Response response = proceed(session); // Change the status code response.status_code = 12345; return response; } }; class SetBasicAuthInterceptor : public Interceptor { public: Response intercept(Session& session) override { // Set authentication session.SetAuth(Authentication{"user", "password", AuthMode::BASIC}); // Proceed the chain return proceed(session); } }; class SetUnsupportedProtocolErrorInterceptor : public Interceptor { public: Response intercept(Session& session) override { // Proceed the chain Response response = proceed(session); // Set unsupported protocol error response.error = Error{CURLE_UNSUPPORTED_PROTOCOL, "SetErrorInterceptor"}; // Return response return response; } }; class ChangeRequestMethodToGetInterceptor : public Interceptor { public: Response intercept(Session& session) override { return proceed(session, Interceptor::ProceedHttpMethod::GET_REQUEST); } }; class ChangeRequestMethodToPostInterceptor : public Interceptor { public: Response intercept(Session& session) override { session.SetOption(Payload{{"x", "5"}}); return proceed(session, Interceptor::ProceedHttpMethod::POST_REQUEST); } }; class ChangeRequestMethodToPutInterceptor : public Interceptor { public: Response intercept(Session& session) override { session.SetOption(Payload{{"x", "5"}}); return proceed(session, Interceptor::ProceedHttpMethod::PUT_REQUEST); } }; class ChangeRequestMethodToDeleteInterceptor : public Interceptor { public: Response intercept(Session& session) override { return proceed(session, Interceptor::ProceedHttpMethod::DELETE_REQUEST); } }; bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } class ChangeRequestMethodToDownloadCallbackInterceptor : public Interceptor { public: Response intercept(Session& session) override { return proceed(session, Interceptor::ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST, WriteCallback{write_data, 0}); } }; class ChangeRequestMethodToHeadInterceptor : public Interceptor { public: Response intercept(Session& session) override { return proceed(session, Interceptor::ProceedHttpMethod::HEAD_REQUEST); } }; class ChangeRequestMethodToOptionsInterceptor : public Interceptor { public: Response intercept(Session& session) override { return proceed(session, Interceptor::ProceedHttpMethod::OPTIONS_REQUEST); } }; class ChangeRequestMethodToPatchInterceptor : public Interceptor { public: Response intercept(Session& session) override { session.SetOption(Payload{{"x", "5"}}); return proceed(session, Interceptor::ProceedHttpMethod::PATCH_REQUEST); } }; class RetryInterceptor : public Interceptor { public: Response intercept(Session& session) override { // Proceed the chain Response response = proceed(session); // retried request response = proceed(session); return response; } }; TEST(InterceptorTest, HiddenUrlRewriteInterceptorTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeStatusCodeInterceptorTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); long expected_status_code{12345}; EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); // second request response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, RetryInterceptorTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); session.AddInterceptor(std::make_shared()); Response response = session.Get(); long expected_status_code{12345}; EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); // second request response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, DownloadChangeStatusCodeInterceptorTest) { cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; cpr::Session session; session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Download(cpr::WriteCallback{write_data, 0}); long expected_status_code{12345}; EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(InterceptorTest, SetBasicAuthInterceptorTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, SetUnsupportedProtocolErrorInterceptorTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_error_message{"SetErrorInterceptor"}; ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL}; EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(expected_error_message, response.error.message); EXPECT_EQ(expected_error_code, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToGetInterceptorTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Head(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToPostInterceptorTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Head(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToPutInterceptorTest) { Url url{server->GetBaseUrl() + "/put.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToPatchInterceptorTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToOptionsInterceptorTest) { Url url{server->GetBaseUrl() + "/"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToHeadInterceptorTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToDownloadCallbackInterceptorTest) { Url url{server->GetBaseUrl() + "/download_gzip.html"}; Session session; session.SetUrl(url); session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); session.SetTimeout(Timeout{2000}); session.AddInterceptor(std::make_shared()); Response response = session.Put(); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ChangeRequestMethodToDeleteInterceptorTest) { Url url{server->GetBaseUrl() + "/delete.html"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, TwoInterceptorsTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{"Hello world!"}; long expected_status_code{12345}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(InterceptorTest, ThreeInterceptorsTest) { Url url{server->GetBaseUrl() + "/basic.json"}; Session session; session.SetUrl(url); session.AddInterceptor(std::make_shared()); session.AddInterceptor(std::make_shared()); session.AddInterceptor(std::make_shared()); Response response = session.Get(); std::string expected_text{"Hello world!"}; long expected_status_code{12345}; std::string expected_error_message{"SetErrorInterceptor"}; ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(expected_status_code, response.status_code); EXPECT_EQ(expected_error_message, response.error.message); EXPECT_EQ(expected_error_code, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/multiasync_tests.cpp000066400000000000000000000470551475657722200175110ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include "httpServer.hpp" #include "multiasync_tests.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); // A cancellable AsyncResponse using AsyncResponseC = AsyncWrapper; /** 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. */ TEST(AsyncWrapperTests, TestConstructorDeductions) { auto wrapper_non_cancellable{AsyncWrapper{std::future{}}}; auto wrapper_cancellable{AsyncWrapper{std::future{}, std::make_shared(false)}}; static_assert(std::is_same_v, decltype(wrapper_non_cancellable)>); static_assert(std::is_same_v, decltype(wrapper_cancellable)>); SUCCEED(); } /** These tests aim to set a point of reference for AsyncWrapper behavior. * Those functions that replicate std::future member functions should behave in a way that is in all ways compatible. * Others should behave as expected by the below test set. */ TEST(AsyncWrapperNonCancellableTests, TestGetNoError) { const Url hello_url{server->GetBaseUrl() + "/hello.html"}; const std::string expected_hello{"Hello world!"}; const Response resp{GetAsync(hello_url).get()}; EXPECT_EQ(expected_hello, resp.text); } TEST(AsyncWrapperNonCancellableTests, TestExceptionsNoSharedState) { const std::chrono::duration five_secs{std::chrono::seconds(1)}; const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs}; // We create an AsyncWrapper for a future without a shared state (default-initialized) AsyncWrapper test_wrapper{std::future{}}; ASSERT_FALSE(test_wrapper.valid()); ASSERT_FALSE(test_wrapper.IsCancelled()); // Trying to get or wait for a future that doesn't have a shared state should result to an exception // It should be noted that there is a divergence from std::future behavior here: calling wait* on the original std::future is undefined behaviour, according to cppreference.com . We find it preferrable to throw an exception. EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception); EXPECT_THROW(test_wrapper.wait(), std::exception); EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception); EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception); } TEST(AsyncWrapperCancellableTests, TestExceptionsNoSharedState) { const std::chrono::duration five_secs{std::chrono::seconds(5)}; const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs}; AsyncWrapper test_wrapper{std::future{}, std::make_shared(false)}; static_assert(std::is_same, decltype(test_wrapper)>::value); ASSERT_FALSE(test_wrapper.valid()); ASSERT_FALSE(test_wrapper.IsCancelled()); EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception); EXPECT_THROW(test_wrapper.wait(), std::exception); EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception); EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception); } TEST(AsyncWrapperCancellableTests, TestExceptionsCancelledRequest) { const Url call_url{server->GetBaseUrl() + "/low_speed_bytes.html"}; const std::chrono::duration five_secs{std::chrono::seconds(5)}; const std::chrono::time_point in_five_s{std::chrono::steady_clock::now() + five_secs}; AsyncResponseC test_wrapper{std::move(MultiGetAsync(std::tuple{call_url}).at(0))}; EXPECT_EQ(CancellationResult::success, test_wrapper.Cancel()); EXPECT_EQ(CancellationResult::invalid_operation, test_wrapper.Cancel()); ASSERT_TRUE(test_wrapper.IsCancelled()); EXPECT_THROW(std::ignore = test_wrapper.get(), std::exception); EXPECT_THROW(test_wrapper.wait(), std::exception); EXPECT_THROW(test_wrapper.wait_for(five_secs), std::exception); EXPECT_THROW(test_wrapper.wait_until(in_five_s), std::exception); } TEST(AsyncWrapperCancellableTests, TestWaitFor) { constexpr std::chrono::duration wait_for_time{std::chrono::milliseconds(100)}; constexpr std::chrono::duration teardown_time{std::chrono::milliseconds(10)}; const Url call_url{server->GetBaseUrl() + "/low_speed_bytes.html"}; AsyncResponseC test_wrapper{std::move(MultiGetAsync(std::tuple{call_url}).at(0))}; EXPECT_EQ(std::future_status::timeout, test_wrapper.wait_for(wait_for_time)); ASSERT_TRUE(test_wrapper.valid()); ASSERT_FALSE(test_wrapper.IsCancelled()); EXPECT_EQ(CancellationResult::success, test_wrapper.Cancel()); std::this_thread::sleep_for(teardown_time); } /** The group MultiAsyncBasicTests executes multiple tests from the test sources associated with every Http action in parallel. * 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. */ TEST(MultiAsyncBasicTests, MultiAsyncGetTest) { const Url hello_url{server->GetBaseUrl() + "/hello.html"}; const std::string expected_hello{"Hello world!"}; std::vector resps{MultiGetAsync(std::tuple{hello_url}, std::tuple{hello_url}, std::tuple{hello_url})}; for (AsyncResponseC& resp : resps) { EXPECT_EQ(expected_hello, resp.get().text); } } TEST(MultiAsyncBasicTests, MultiAsyncDeleteTest) { const std::string server_base{server->GetBaseUrl()}; const Url delete_allowed{server_base + "/delete.html"}; const Url delete_unallowed{server_base + "/delete_unallowed.html"}; const std::tuple del_json_params{delete_allowed, Body{"'foo':'bar'"}, Header{{"Content-Type", "application/json"}}}; const std::string expected_text_success{"Delete success"}; const std::string expected_text_fail{"Method Not Allowed"}; const std::string expected_text_json{"'foo':'bar'"}; std::vector resps{MultiDeleteAsync(std::tuple{delete_allowed}, std::tuple{delete_unallowed}, del_json_params)}; Response del_success{resps.at(0).get()}; Response del_fail{resps.at(1).get()}; Response del_json{resps.at(2).get()}; EXPECT_EQ(expected_text_success, del_success.text); EXPECT_EQ(delete_allowed, del_success.url); EXPECT_EQ(std::string{"text/html"}, del_success.header["content-type"]); EXPECT_EQ(200, del_success.status_code); EXPECT_EQ(ErrorCode::OK, del_success.error.code); EXPECT_EQ(expected_text_fail, del_fail.text); EXPECT_EQ(delete_unallowed, del_fail.url); EXPECT_EQ(std::string{"text/plain"}, del_fail.header["content-type"]); EXPECT_EQ(405, del_fail.status_code); EXPECT_EQ(ErrorCode::OK, del_fail.error.code); EXPECT_EQ(expected_text_json, del_json.text); EXPECT_EQ(delete_allowed, del_json.url); EXPECT_EQ(std::string{"application/json"}, del_json.header["content-type"]); EXPECT_EQ(200, del_json.status_code); EXPECT_EQ(ErrorCode::OK, del_json.error.code); } TEST(MultiAsyncBasicTests, MultiAsyncHeadTest) { const std::string server_base{server->GetBaseUrl()}; const Url hello_url{server_base + "/hello.html"}; const Url json_url{server_base + "/basic.json"}; const Url notfound_url{server_base + "/error.html"}; const Url digest_url{server_base + "/digest_auth.html"}; const Authentication digest_auth{"user", "password", AuthMode::DIGEST}; std::vector resps{MultiHeadAsync(std::tuple{hello_url}, std::tuple{json_url}, std::tuple{notfound_url}, std::tuple{digest_url, digest_auth})}; Response hello_resp{resps.at(0).get()}; Response json_resp{resps.at(1).get()}; Response notfound_resp{resps.at(2).get()}; Response digest_resp{resps.at(3).get()}; EXPECT_EQ(std::string{}, hello_resp.text); EXPECT_EQ(hello_url, hello_resp.url); EXPECT_EQ(std::string{"text/html"}, hello_resp.header["content-type"]); EXPECT_EQ(200, hello_resp.status_code); EXPECT_EQ(ErrorCode::OK, hello_resp.error.code); EXPECT_EQ(std::string{}, json_resp.text); EXPECT_EQ(json_url, json_resp.url); EXPECT_EQ(std::string{"application/json"}, json_resp.header["content-type"]); EXPECT_EQ(200, json_resp.status_code); EXPECT_EQ(ErrorCode::OK, json_resp.error.code); EXPECT_EQ(std::string{}, notfound_resp.text); EXPECT_EQ(notfound_url, notfound_resp.url); EXPECT_EQ(std::string{"text/plain"}, notfound_resp.header["content-type"]); EXPECT_EQ(404, notfound_resp.status_code); EXPECT_EQ(ErrorCode::OK, notfound_resp.error.code); EXPECT_EQ(std::string{}, digest_resp.text); EXPECT_EQ(digest_url, digest_resp.url); EXPECT_EQ(std::string{"text/html"}, digest_resp.header["content-type"]); EXPECT_EQ(200, digest_resp.status_code); EXPECT_EQ(ErrorCode::OK, digest_resp.error.code); } TEST(MultiAsyncBasicTests, MultiAsyncOptionsTest) { const std::string server_base{server->GetBaseUrl()}; const Url root_url{server_base + "/"}; const Url hello_url{server_base + "/hello.html"}; std::vector resps{MultiOptionsAsync(std::tuple{root_url}, std::tuple{hello_url})}; Response root_resp{resps.at(0).get()}; Response hello_resp{resps.at(1).get()}; EXPECT_EQ(std::string{}, root_resp.text); EXPECT_EQ(root_url, root_resp.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, root_resp.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, root_resp.status_code); EXPECT_EQ(ErrorCode::OK, root_resp.error.code); EXPECT_EQ(std::string{}, hello_resp.text); EXPECT_EQ(hello_url, hello_resp.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, hello_resp.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, hello_resp.status_code); EXPECT_EQ(ErrorCode::OK, hello_resp.error.code); } TEST(MultiAsyncBasicTests, MultiAsyncPatchTest) { const std::string server_base{server->GetBaseUrl()}; const Url patch_url{server_base + "/patch.html"}; const Url patch_not_allowed_url{server_base + "/patch_unallowed.html"}; const Payload pl{{"x", "10"}, {"y", "1"}}; const std::string expected_text{ "{\n" " \"x\": 10,\n" " \"y\": 1,\n" " \"sum\": 11\n" "}"}; const std::string notallowed_text{"Method Not Allowed"}; std::vector resps{MultiPatchAsync(std::tuple{patch_url, pl}, std::tuple{patch_not_allowed_url, pl})}; const Response success{resps.at(0).get()}; const Response fail{resps.at(1).get()}; EXPECT_EQ(expected_text, success.text); EXPECT_EQ(200, success.status_code); EXPECT_EQ(patch_url, success.url); EXPECT_EQ(notallowed_text, fail.text); EXPECT_EQ(405, fail.status_code); EXPECT_EQ(ErrorCode::OK, fail.error.code); } TEST(MultiAsyncBasicTests, MultiAsyncPostTest) { const std::string server_base{server->GetBaseUrl()}; const Url post_url{server_base + "/url_post.html"}; const Url form_post_url{server_base + "/form_post.html"}; const Payload post_data{{"x", "5"}, {"y", "15"}}; const Multipart form_data{{"x", 5}}; const std::string post_text{ "{\n" " \"x\": 5,\n" " \"y\": 15,\n" " \"sum\": 20\n" "}"}; const std::string form_text{ "{\n" " \"x\": \"5\"\n" "}"}; std::vector resps{MultiPostAsync(std::tuple{post_url, post_data}, std::tuple{form_post_url, form_data})}; Response post_resp{resps.at(0).get()}; Response form_resp{resps.at(1).get()}; EXPECT_EQ(post_text, post_resp.text); EXPECT_EQ(post_url, post_resp.url); EXPECT_EQ(std::string{"application/json"}, post_resp.header["content-type"]); EXPECT_EQ(201, post_resp.status_code); EXPECT_EQ(ErrorCode::OK, post_resp.error.code); EXPECT_EQ(form_text, form_resp.text); EXPECT_EQ(form_post_url, form_resp.url); EXPECT_EQ(std::string{"application/json"}, form_resp.header["content-type"]); EXPECT_EQ(201, form_resp.status_code); EXPECT_EQ(ErrorCode::OK, form_resp.error.code); } TEST(MultiAsyncBasicTests, MultiAsyncPutTest) { const std::string server_base{server->GetBaseUrl()}; const Url put_url{server_base + "/put.html"}; const Url put_failure_url{server_base + "/put_unallowed.html"}; const Payload pl{{"x", "7"}}; const std::string success_text{ "{\n" " \"x\": 7\n" "}"}; const std::string failure_text{"Method Not Allowed"}; std::vector resps{MultiPutAsync(std::tuple{put_url, pl}, std::tuple{put_failure_url, pl})}; Response success_resp{resps.at(0).get()}; Response failure_resp{resps.at(1).get()}; EXPECT_EQ(success_text, success_resp.text); EXPECT_EQ(put_url, success_resp.url); EXPECT_EQ(std::string{"application/json"}, success_resp.header["content-type"]); EXPECT_EQ(200, success_resp.status_code); EXPECT_EQ(ErrorCode::OK, success_resp.error.code); EXPECT_EQ(failure_text, failure_resp.text); EXPECT_EQ(put_failure_url, failure_resp.url); EXPECT_EQ(std::string{"text/plain"}, failure_resp.header["content-type"]); EXPECT_EQ(405, failure_resp.status_code); EXPECT_EQ(ErrorCode::OK, failure_resp.error.code); } static TestSynchronizationEnv* synchro_env = new TestSynchronizationEnv(); /** * We test that cancellation on queue, works, ie libcurl does not get engaged at all * To do this, we plant an observer function in the progress call sequence, which * will set an atomic boolean to true. The objective is to verify that within 500ms, * the function is never called. */ TEST(MultiAsyncCancelTests, CancellationOnQueue) { synchro_env->Reset(); const Url hello_url{server->GetBaseUrl() + "/hello.html"}; const std::function observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool { synchro_env->fn_called.store(true); return true; }}; GlobalThreadPool::GetInstance()->Pause(); std::vector resps{MultiGetAsync(std::tuple{hello_url, ProgressCallback{observer_fn}})}; EXPECT_EQ(CancellationResult::success, resps.at(0).Cancel()); GlobalThreadPool::GetInstance()->Resume(); const bool was_called{synchro_env->fn_called}; EXPECT_EQ(false, was_called); } /** * We test that cancellation works as intended while the request is being processed by the server. * To achieve this we use a condition variable to ensure that the observer function, wrapped in a * cpr::ProgressCallback, is called at least once, and then no further calls are made for half a * second after cancellation. * * 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 * We use the condition variable in our synchronization environment to ensure that the transfer has * started at the time of cancellation, ie the observer function has been called at least once. */ TEST(MultiAsyncCancelTests, TestCancellationInTransit) { const Url call_url{server->GetBaseUrl() + "/low_speed_bytes.html"}; synchro_env->Reset(); // 1. Thread running the test acquires the condition variable's mutex std::unique_lock setup_lock{synchro_env->test_cv_mutex}; const std::function observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool { if (synchro_env->counter == 0) { // 3. in Threadpool, the cv mutex is obtained by the worker thread const std::unique_lock l{synchro_env->test_cv_mutex}; synchro_env->counter++; // 4. the cv is notified synchro_env->test_cv.notify_all(); } else { synchro_env->counter++; } return true; }}; std::vector res{cpr::MultiGetAsync(std::tuple{call_url, cpr::ProgressCallback{observer_fn}})}; // 2. cv mutex is released, thread waits for notification on cv // see https://en.cppreference.com/w/cpp/thread/condition_variable/wait synchro_env->test_cv.wait(setup_lock); // 5. execution continues after notification const size_t init_calls{synchro_env->counter}; EXPECT_LT(0, init_calls); EXPECT_EQ(cpr::CancellationResult::success, res.at(0).Cancel()); const size_t calls{synchro_env->counter}; std::this_thread::sleep_for(std::chrono::milliseconds{101}); const size_t calls_post{synchro_env->counter}; EXPECT_LT(calls_post, calls + 2); } /** Checks that the request is cancelled when the corresponding AsyncResponseC is desturcted */ TEST(MultiAsyncCancelTests, TestCancellationOnResponseWrapperDestruction) { const Url call_url{server->GetBaseUrl() + "/hello.html"}; synchro_env->Reset(); std::unique_lock setup_lock{synchro_env->test_cv_mutex}; const std::function observer_fn{[](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool { const std::unique_lock l{synchro_env->test_cv_mutex}; synchro_env->counter++; synchro_env->test_cv.notify_all(); return true; }}; // We construct a Request that will not terminate, wait until it is being processed by a thread, and destruct the AsyncResponseC { AsyncResponseC resp{std::move(MultiGetAsync(std::tuple{call_url, ProgressCallback{observer_fn}}).at(0))}; synchro_env->test_cv.wait(setup_lock); const size_t init_calls{synchro_env->counter}; EXPECT_LT(0, init_calls); } const size_t calls{synchro_env->counter}; std::this_thread::sleep_for(std::chrono::milliseconds(100)); const size_t post_calls{synchro_env->counter}; EXPECT_EQ(calls, post_calls); } /** * This test checks if the interval of calls to the progress function is * acceptable during a low-speed transaction. The server's low_speed_bytes * uri sends 1 Byte/second, and we aim to evaluate that 15 calls to the * progress function happen within 5 seconds. This would indicate that * the user can realistically expect to have their request cancelled within * ~1s on a bad case (low network speed). * INFO this test is not, strictly speaking, deterministic. It depends at the * least on scheduler behaviour. We have tried, however, to set a boundary that * is permissive enough to ensure consistency. */ TEST(MultiAsyncCancelTests, TestIntervalOfProgressCallsLowSpeed) { const Url call_url{server->GetBaseUrl() + "/low_speed_bytes.html"}; synchro_env->Reset(); size_t N{15}; // This variable will be used to cancel the transaction at the point of the Nth call. const std::chrono::time_point start{std::chrono::steady_clock::now()}; const std::function observer_fn{[N](cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, cpr_pf_arg_t, intptr_t) -> bool { const size_t current_iteration{++(synchro_env->counter)}; return current_iteration <= N; }}; const ProgressCallback pcall{observer_fn}; std::vector resp{MultiGetAsync(std::tuple{call_url, pcall})}; resp.at(0).wait(); const std::chrono::duration elapsed_time{std::chrono::steady_clock::now() - start}; EXPECT_GT(std::chrono::seconds(N), elapsed_time); std::this_thread::sleep_for(std::chrono::milliseconds{101}); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); ::testing::AddGlobalTestEnvironment(synchro_env); return RUN_ALL_TESTS(); } cpr-1.11.2/test/multiasync_tests.hpp000066400000000000000000000005751475657722200175120ustar00rootroot00000000000000#pragma once #include #include #include class TestSynchronizationEnv : public testing::Environment { public: std::atomic_size_t counter{0}; std::atomic_bool fn_called{false}; std::condition_variable test_cv{}; std::mutex test_cv_mutex{}; void Reset() { counter = 0; fn_called = false; } }; cpr-1.11.2/test/multiperform_tests.cpp000066400000000000000000000665341475657722200200510ustar00rootroot00000000000000#include #include #include #include #include "cpr/api.h" #include "cpr/cpr.h" #include #include "httpServer.hpp" #include "httpsServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } TEST(MultiperformAddSessionTests, MultiperformAddSingleSessionTest) { std::shared_ptr session = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session); EXPECT_EQ(2, session.use_count()); } TEST(MultiperformAddSessionTests, MultiperformAddMultipleSessionsTest) { MultiPerform multiperform; for (int i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); multiperform.AddSession(session); EXPECT_EQ(2, session.use_count()); } } TEST(MultiperformAddSessionTests, MultiperformAddMultipleNonDownloadSessionsTest) { MultiPerform multiperform; for (int i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST); EXPECT_EQ(2, session.use_count()); } } TEST(MultiperformAddSessionTests, MultiperformAddMultipleDownloadSessionsTest) { MultiPerform multiperform; for (int i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST); EXPECT_EQ(2, session.use_count()); } } TEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsTest) { std::shared_ptr session_1 = std::make_shared(); std::shared_ptr session_2 = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session_1, MultiPerform::HttpMethod::GET_REQUEST); EXPECT_EQ(2, session_1.use_count()); EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::DOWNLOAD_REQUEST), std::invalid_argument); } TEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsReversTest) { std::shared_ptr session_1 = std::make_shared(); std::shared_ptr session_2 = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session_1, MultiPerform::HttpMethod::DOWNLOAD_REQUEST); EXPECT_EQ(2, session_1.use_count()); EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::GET_REQUEST), std::invalid_argument); } TEST(MultiperformRemoveSessionTests, MultiperformRemoveSingleSessionTest) { std::shared_ptr session = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session); EXPECT_EQ(2, session.use_count()); multiperform.RemoveSession(session); EXPECT_EQ(1, session.use_count()); } TEST(MultiperformRemoveSessionTests, MultiperformRemoveMultipleSessionsTest) { MultiPerform multiperform; for (int i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); multiperform.AddSession(session); EXPECT_EQ(2, session.use_count()); multiperform.RemoveSession(session); EXPECT_EQ(1, session.use_count()); } } TEST(MultiperformRemoveSessionTests, MultiperformRemoveNonExistingSessionEmptyTest) { std::shared_ptr session = std::make_shared(); MultiPerform multiperform; EXPECT_THROW(multiperform.RemoveSession(session), std::invalid_argument); } TEST(MultiperformRemoveSessionTests, MultiperformRemoveNonExistingSessionTest) { MultiPerform multiperform; std::shared_ptr session = std::make_shared(); multiperform.AddSession(session); std::shared_ptr session2 = std::make_shared(); EXPECT_THROW(multiperform.RemoveSession(session2), std::invalid_argument); } TEST(MultiperformGetTests, MultiperformSingleSessionGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Get(); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformGetTests, MultiperformTwoSessionsGetTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/hello.html"}); urls.push_back({server->GetBaseUrl() + "/error.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); multiperform.AddSession(sessions.at(i)); } std::vector responses = multiperform.Get(); EXPECT_EQ(responses.size(), sessions.size()); std::vector expected_texts; expected_texts.emplace_back("Hello world!"); expected_texts.emplace_back("Not Found"); std::vector expected_content_types; expected_content_types.emplace_back("text/html"); expected_content_types.emplace_back("text/plain"); std::vector expected_status_codes; expected_status_codes.push_back(200); expected_status_codes.push_back(404); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(expected_texts.at(i), responses.at(i).text); EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]); EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code); } } TEST(MultiperformGetTests, MultiperformRemoveSessionGetTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/hello.html"}); urls.push_back({server->GetBaseUrl() + "/hello.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); multiperform.AddSession(sessions.at(i)); } multiperform.RemoveSession(sessions.at(0)); std::vector responses = multiperform.Get(); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(urls.at(0), responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } #ifndef __APPLE__ /** * This test case is currently disabled for macOS/Apple systems since it fails in an nondeterministic manner. * It is probably caused by a bug inside curl_multi_perform on macOS. * Needs further investigation. * Issue: https://github.com/libcpr/cpr/issues/841 **/ TEST(MultiperformGetTests, MultiperformTenSessionsGetTest) { const size_t sessionCount = 10; MultiPerform multiperform; Url url{server->GetBaseUrl() + "/hello.html"}; for (size_t i = 0; i < sessionCount; ++i) { std::shared_ptr session = std::make_shared(); session->SetUrl(url); multiperform.AddSession(session); } std::vector responses = multiperform.Get(); EXPECT_EQ(responses.size(), sessionCount); for (Response& response : responses) { EXPECT_EQ(std::string{"Hello world!"}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } #endif TEST(MultiperformDeleteTests, MultiperformSingleSessionDeleteTest) { Url url{server->GetBaseUrl() + "/delete.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Delete(); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadTest) { Url url{server->GetBaseUrl() + "/download_gzip.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Download(WriteCallback{write_data, 0}); EXPECT_EQ(responses.size(), 1); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadNonMatchingArgumentsNumberTest) { std::shared_ptr session = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session); EXPECT_THROW(std::vector responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument); } TEST(MultiperformDownloadTests, MultiperformTwoSessionsDownloadTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/download_gzip.html"}); urls.push_back({server->GetBaseUrl() + "/download_gzip.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); sessions.at(i)->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); multiperform.AddSession(sessions.at(i)); } std::vector responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}); EXPECT_EQ(responses.size(), sessions.size()); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(200, responses.at(i).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code); } } TEST(MultiperformPutTests, MultiperformSingleSessionPutTest) { Url url{server->GetBaseUrl() + "/put.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload(Payload{{"x", "5"}}); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Put(); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformHeadTests, MultiperformSingleSessionHeadTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Head(); EXPECT_EQ(responses.size(), 1); std::string expected_text; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformOptionsTests, MultiperformSingleSessionOptionsTest) { Url url{server->GetBaseUrl() + "/"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Options(); EXPECT_EQ(responses.size(), 1); std::string expected_text; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, responses.at(0).header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformPatchTests, MultiperformSingleSessionPatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload(Payload{{"x", "5"}}); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Patch(); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformPostTests, MultiperformSingleSessionPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload(Payload{{"x", "5"}}); MultiPerform multiperform; multiperform.AddSession(session); std::vector responses = multiperform.Post(); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(201, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformPerformTests, MultiperformSingleGetPerformTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); MultiPerform multiperform; multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST); std::vector responses = multiperform.Perform(); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformPerformTests, MultiperformTwoGetPerformTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/hello.html"}); urls.push_back({server->GetBaseUrl() + "/error.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::GET_REQUEST); } std::vector responses = multiperform.Perform(); EXPECT_EQ(responses.size(), sessions.size()); std::vector expected_texts; expected_texts.emplace_back("Hello world!"); expected_texts.emplace_back("Not Found"); std::vector expected_content_types; expected_content_types.emplace_back("text/html"); expected_content_types.emplace_back("text/plain"); std::vector expected_status_codes; expected_status_codes.push_back(200); expected_status_codes.push_back(404); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(expected_texts.at(i), responses.at(i).text); EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]); EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code); } } TEST(MultiperformPerformTests, MultiperformMixedMethodsPerformTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/hello.html"}); urls.push_back({server->GetBaseUrl() + "/delete.html"}); urls.push_back({server->GetBaseUrl() + "/error.html"}); urls.push_back({server->GetBaseUrl() + "/url_post.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); std::vector methods; methods.push_back(MultiPerform::HttpMethod::GET_REQUEST); methods.push_back(MultiPerform::HttpMethod::DELETE_REQUEST); methods.push_back(MultiPerform::HttpMethod::GET_REQUEST); methods.push_back(MultiPerform::HttpMethod::POST_REQUEST); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); if (methods.at(i) == MultiPerform::HttpMethod::POST_REQUEST) { sessions.at(i)->SetPayload(Payload{{"x", "5"}}); } multiperform.AddSession(sessions.at(i), methods.at(i)); } std::vector responses = multiperform.Perform(); EXPECT_EQ(responses.size(), sessions.size()); std::vector expected_texts; expected_texts.emplace_back("Hello world!"); expected_texts.emplace_back("Delete success"); expected_texts.emplace_back("Not Found"); expected_texts.emplace_back( "{\n" " \"x\": 5\n" "}"); std::vector expected_content_types; expected_content_types.emplace_back("text/html"); expected_content_types.emplace_back("text/html"); expected_content_types.emplace_back("text/plain"); expected_content_types.emplace_back("application/json"); std::vector expected_status_codes; expected_status_codes.push_back(200); expected_status_codes.push_back(200); expected_status_codes.push_back(404); expected_status_codes.push_back(201); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(expected_texts.at(i), responses.at(i).text); EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]); EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code); } } TEST(MultiperformPerformDownloadTests, MultiperformSinglePerformDownloadTest) { Url url{server->GetBaseUrl() + "/download_gzip.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); MultiPerform multiperform; multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST); std::vector responses = multiperform.PerformDownload(WriteCallback{write_data, 0}); EXPECT_EQ(responses.size(), 1); EXPECT_EQ(url, responses.at(0).url); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformDownloadTests, MultiperformSinglePerformDownloadNonMatchingArgumentsNumberTest) { std::shared_ptr session = std::make_shared(); MultiPerform multiperform; multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST); EXPECT_THROW(std::vector responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument); } TEST(MultiperformPerformDownloadTests, MultiperformTwoPerformDownloadTest) { MultiPerform multiperform; std::vector urls; urls.push_back({server->GetBaseUrl() + "/download_gzip.html"}); urls.push_back({server->GetBaseUrl() + "/download_gzip.html"}); std::vector> sessions; sessions.push_back(std::make_shared()); sessions.push_back(std::make_shared()); for (size_t i = 0; i < sessions.size(); ++i) { sessions.at(i)->SetUrl(urls.at(i)); sessions.at(i)->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::DOWNLOAD_REQUEST); } std::vector responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}); EXPECT_EQ(responses.size(), sessions.size()); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(200, responses.at(i).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code); } } TEST(MultiperformAPITests, MultiperformApiSingleGetTest) { std::vector responses = MultiGet(std::tuple{Url{server->GetBaseUrl() + "/hello.html"}}); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiTwoGetsTest) { std::vector responses = MultiGet(std::tuple{Url{server->GetBaseUrl() + "/long_timeout.html"}, Timeout{1000}}, std::tuple{Url{server->GetBaseUrl() + "/error.html"}}); EXPECT_EQ(responses.size(), 2); std::vector urls; urls.push_back({server->GetBaseUrl() + "/long_timeout.html"}); urls.push_back({server->GetBaseUrl() + "/error.html"}); std::vector expected_texts; expected_texts.emplace_back(""); expected_texts.emplace_back("Not Found"); std::vector expected_content_types; expected_content_types.emplace_back(""); expected_content_types.emplace_back("text/plain"); std::vector expected_status_codes; expected_status_codes.push_back(0); expected_status_codes.push_back(404); std::vector expected_error_codes; expected_error_codes.push_back(ErrorCode::OPERATION_TIMEDOUT); expected_error_codes.push_back(ErrorCode::OK); for (size_t i = 0; i < responses.size(); ++i) { EXPECT_EQ(expected_texts.at(i), responses.at(i).text); EXPECT_EQ(urls.at(i), responses.at(i).url); EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]); EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code); EXPECT_EQ(expected_error_codes.at(i), responses.at(i).error.code); } } TEST(MultiperformAPITests, MultiperformApiSingleDeleteTest) { std::vector responses = MultiDelete(std::tuple{Url{server->GetBaseUrl() + "/delete.html"}}); EXPECT_EQ(responses.size(), 1); std::string expected_text{"Delete success"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/delete.html"}, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiSinglePutTest) { std::vector responses = MultiPut(std::tuple{Url{server->GetBaseUrl() + "/put.html"}, Payload{{"x", "5"}}}); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/put.html"}, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiSingleHeadTest) { std::vector responses = MultiHead(std::tuple{Url{server->GetBaseUrl() + "/hello.html"}}); EXPECT_EQ(responses.size(), 1); std::string expected_text; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, responses.at(0).url); EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiSingleOptionsTest) { std::vector responses = MultiOptions(std::tuple{Url{server->GetBaseUrl() + "/"}}); EXPECT_EQ(responses.size(), 1); std::string expected_text; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/"}, responses.at(0).url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, responses.at(0).header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiSinglePatchTest) { std::vector responses = MultiPatch(std::tuple{Url{server->GetBaseUrl() + "/patch.html"}, Payload{{"x", "5"}}}); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/patch.html"}, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(200, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } TEST(MultiperformAPITests, MultiperformApiSinglePostTest) { std::vector responses = MultiPost(std::tuple{Url{server->GetBaseUrl() + "/url_post.html"}, Payload{{"x", "5"}}}); EXPECT_EQ(responses.size(), 1); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, responses.at(0).text); EXPECT_EQ(Url{server->GetBaseUrl() + "/url_post.html"}, responses.at(0).url); EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]); EXPECT_EQ(201, responses.at(0).status_code); EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/options_tests.cpp000066400000000000000000000051141475657722200170020ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(OptionsTests, BaseUrlTest) { Url url{server->GetBaseUrl() + "/"}; Response response = cpr::Options(url); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(OptionsTests, SpecificUrlTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Options(url); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(OptionsTests, AsyncBaseUrlTest) { Url url{server->GetBaseUrl() + "/"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::OptionsAsync(url)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(OptionsTests, AsyncSpecificUrlTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::OptionsAsync(url)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/patch_tests.cpp000066400000000000000000000225601475657722200164120ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(PatchTests, PatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; Response response = cpr::Patch(url, payload); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, PatchUnallowedTest) { Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; Response response = cpr::Patch(url, payload); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; Session session; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchUnallowedTest) { Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; Session session; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchUnallowedAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchUnallowedAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, SessionPatchUnallowedAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Patch(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, AsyncPatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload); cpr::Response response = future_response.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, AsyncPatchUnallowedTest) { Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload); cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PatchTests, AsyncMultiplePatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::PatchAsync(url, payload)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(PatchTests, AsyncMultiplePatchUnallowedTest) { Url url{server->GetBaseUrl() + "/patch_unallowed.html"}; Payload payload{{"x", "5"}}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::PatchAsync(url, payload)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/post_tests.cpp000066400000000000000000000737031475657722200163050ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "cpr/cookies.h" #include "cpr/cpr.h" #include "cpr/multipart.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(UrlEncodedPostTests, UrlPostSingleTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, UrlPostAddPayloadPair) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "1"}}; payload.Add({"y", "2"}); Response response = cpr::Post(url, payload); std::string expected_text{ "{\n" " \"x\": 1,\n" " \"y\": 2,\n" " \"sum\": 3\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); } TEST(UrlEncodedPostTests, UrlPostPayloadIteratorTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::vector payloadData; payloadData.emplace_back("x", "1"); payloadData.emplace_back("y", "2"); Response response = cpr::Post(url, Payload(payloadData.begin(), payloadData.end())); std::string expected_text{ "{\n" " \"x\": 1,\n" " \"y\": 2,\n" " \"sum\": 3\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); } TEST(UrlEncodedPostTests, UrlPostEncodeTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Payload{{"x", "hello world!!~"}}); std::string expected_text{ "{\n" " \"x\": hello world!!~\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, UrlPostEncodeNoCopyTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "hello world!!~"}}; // payload lives through the lifetime of Post, so it doesn't need to be copied Response response = cpr::Post(url, payload); std::string expected_text{ "{\n" " \"x\": hello world!!~\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, UrlPostManyTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}, {"y", "13"}}); std::string expected_text{ "{\n" " \"x\": 5,\n" " \"y\": 13,\n" " \"sum\": 18\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, UrlPostBadHostTest) { Url url{"http://bad_host/"}; Response response = cpr::Post(url, Payload{{"hello", "world"}}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(0, response.status_code); // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT. EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(UrlEncodedPostTests, FormPostSingleTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", 5}}); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileTest) { std::string filename{"test_file"}; std::string content{"hello world"}; std::ofstream test_file; test_file.open(filename); test_file << content; test_file.close(); Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", File{filename}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=" + content + "\"\n" "}"}; std::remove(filename.c_str()); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostMultipleFilesTestLvalue) { Url url{server->GetBaseUrl() + "/form_post.html"}; std::string filename1{"file1"}; std::string content1{"apple"}; std::ofstream file1; file1.open(filename1); file1 << content1; file1.close(); std::string filename2{"file2"}; std::string content2{"banana"}; std::ofstream file2; file2.open(filename2); file2 << content2; file2.close(); File singleFile{"file1"}; File singleFileWithOverridenFilename{"file1", "applefile"}; Files multipleFiles{"file1", "file2"}; Files multipleFilesWithOverridenFilename{ File{"file1", "applefile"}, File{"file2", "bananafile"}, }; { Response response = cpr::Post(url, Multipart{{"files", singleFile}}); std::string expected_text{ "{\n" " \"files\": \"file1=" + content1 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"singleFile", singleFileWithOverridenFilename}}); std::string expected_text{ "{\n" " \"singleFile\": \"applefile=" + content1 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"files", multipleFiles}}); std::string expected_text{ "{\n" " \"files\": \"file1=" + content1 + "\",\n" " \"files\": \"file2=" + content2 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"files", multipleFilesWithOverridenFilename}}); std::string expected_text{ "{\n" " \"files\": \"applefile=" + content1 + "\",\n" " \"files\": \"bananafile=" + content2 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } std::remove(filename1.c_str()); std::remove(filename2.c_str()); } TEST(UrlEncodedPostTests, FormPostMultipleFilesTestRvalue) { Url url{server->GetBaseUrl() + "/form_post.html"}; std::string filename1{"file1"}; std::string content1{"apple"}; std::ofstream file1; file1.open(filename1); file1 << content1; file1.close(); std::string filename2{"file2"}; std::string content2{"banana"}; std::ofstream file2; file2.open(filename2); file2 << content2; file2.close(); { Response response = cpr::Post(url, Multipart{{"files", File{"file1"}}}); std::string expected_text{ "{\n" " \"files\": \"file1=" + content1 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"files", File{"file1", "applefile"}}}); std::string expected_text{ "{\n" " \"files\": \"applefile=" + content1 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"files", Files{"file1", "file2"}}}); std::string expected_text{ "{\n" " \"files\": \"file1=" + content1 + "\",\n" " \"files\": \"file2=" + content2 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = cpr::Post(url, Multipart{{"files", Files{ File{"file1", "applefile"}, File{"file2", "bananafile"}, }}}); std::string expected_text{ "{\n" " \"files\": \"applefile=" + content1 + "\",\n" " \"files\": \"bananafile=" + content2 + "\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } std::remove(filename1.c_str()); std::remove(filename2.c_str()); } TEST(UrlEncodedPostTests, FormPostFileTestWithOverridenFilename) { std::string filename{"test_file"}; std::string overided_filename{"overided_filename"}; std::string content{"hello world"}; std::ofstream test_file; test_file.open(filename); test_file << content; test_file.close(); Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", File{filename, overided_filename}}}); std::string expected_text{ "{\n" " \"x\": \"" + overided_filename + "=" + content + "\"\n" "}"}; std::remove(filename.c_str()); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileNoCopyTest) { std::string filename{"./test_file"}; std::string content{"hello world"}; std::ofstream test_file; test_file.open(filename); test_file << content; test_file.close(); Url url{server->GetBaseUrl() + "/form_post.html"}; Multipart multipart{{"x", File{filename}}}; Response response = cpr::Post(url, multipart); std::string expected_text{ "{\n" " \"x\": \"test_file=" + content + "\"\n" "}"}; std::remove(filename.c_str()); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileNoCopyTestWithOverridenFilename) { std::string filename{"test_file"}; std::string overriden_filename{"overided_filename"}; std::string content{"hello world"}; std::ofstream test_file; test_file.open(filename); test_file << content; test_file.close(); Url url{server->GetBaseUrl() + "/form_post.html"}; Multipart multipart{{"x", File{filename, overriden_filename}}}; Response response = cpr::Post(url, multipart); std::string expected_text{ "{\n" " \"x\": \"" + overriden_filename + "=" + content + "\"\n" "}"}; std::remove(filename.c_str()); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, TimeoutPostTest) { Url url{server->GetBaseUrl() + "/json_post.html"}; std::string body{R"({"RegisterObject": {"DeviceID": "65010000005030000001"}})"}; cpr::Response response = cpr::Post(url, cpr::Header{{"Content-Type", "application/json"}}, cpr::Body{body}, cpr::ConnectTimeout{3000}, cpr::Timeout{3000}); std::string expected_text{R"({"RegisterObject": {"DeviceID": "65010000005030000001"}})"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferTest) { std::string content{"hello world"}; Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=" + content + "\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferNoCopyTest) { std::string content{"hello world"}; Url url{server->GetBaseUrl() + "/form_post.html"}; Multipart multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}}; Response response = cpr::Post(url, multipart); std::string expected_text{ "{\n" " \"x\": \"test_file=" + content + "\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferPointerTest) { const char* content = "hello world"; Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", Buffer{content, 11 + content, "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=" + std::string(content) + "\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferArrayTest) { const char content[] = "hello world"; Url url{server->GetBaseUrl() + "/form_post.html"}; // We subtract 1 from std::end() because we don't want to include the terminating null Response response = cpr::Post(url, Multipart{{"x", Buffer{std::begin(content), std::end(content) - 1, "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=" + std::string(content) + "\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferVectorTest) { std::vector content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=hello world\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostFileBufferStdArrayTest) { std::array content{{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}}; Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=hello world\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostBufferRvalueTest) { std::vector content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}}); std::string expected_text{ "{\n" " \"x\": \"test_file=hello world\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, ReflectPostBufferLvalueTest) { std::vector content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; Url url{server->GetBaseUrl() + "/form_post.html"}; Buffer buff{content.begin(), content.end(), "test_file"}; Response response = cpr::Post(url, Multipart{{"x", buff}}); std::string expected_text{ "{\n" " \"x\": \"test_file=hello world\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostManyTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", 5}, {"y", 13}}); std::string expected_text{ "{\n" " \"x\": \"5\",\n" " \"y\": \"13\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostManyNoCopyTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Multipart multipart{{"x", 5}, {"y", 13}}; Response response = cpr::Post(url, multipart); std::string expected_text{ "{\n" " \"x\": \"5\",\n" " \"y\": \"13\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostContentTypeTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url, Multipart{{"x", 5, "application/number"}}); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, FormPostContentTypeLValueTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Multipart multipart{{"x", 5, "application/number"}}; Response response = cpr::Post(url, multipart); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, UrlPostAsyncSingleTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::PostAsync(url, payload)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(UrlEncodedPostTests, UrlReflectTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UrlEncodedPostTests, PostWithNoBodyTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Response response = cpr::Post(url); std::string expected_text{"{\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } static std::string getTimestamp() { const std::chrono::system_clock::time_point tp = std::chrono::system_clock::now(); const time_t timeT = std::chrono::system_clock::to_time_t(tp); // NOLINTNEXTLINE(concurrency-mt-unsafe) struct tm* tm = gmtime(&timeT); std::string buf; buf.resize(EXPIRES_STRING_SIZE); const size_t s = strftime(buf.data(), buf.size(), "%a, %d %b %Y %H:%M:%S GMT", tm); EXPECT_GT(s, 0); buf.resize(s); return buf; } TEST(UrlEncodedPostTests, PostReflectTest) { std::string uri = server->GetBaseUrl() + "/post_reflect.html"; std::string body = R"({"property1": "value1"})"; std::string contentType = "application/json"; std::string signature = "x-ms-date: something"; std::string logType = "LoggingTest"; std::string date = getTimestamp(); 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)); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(200, response.status_code); EXPECT_EQ(body, response.text); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(signature, response.header["Authorization"]); EXPECT_EQ(logType, response.header["log-type"]); EXPECT_EQ(date, response.header["x-ms-date"]); EXPECT_EQ(std::to_string(body.length()), response.header["content-length"]); } TEST(UrlEncodedPostTests, PostReflectPayloadTest) { std::string uri = server->GetBaseUrl() + "/header_reflect.html"; cpr::Payload payload = cpr::Payload{{"email", ""}, {"password", ""}, {"devicetoken", ""}}; cpr::Response response = cpr::Post(cpr::Url(uri), cpr::Timeout{10000}, payload); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(200, response.status_code); } TEST(UrlEncodedPostTests, InjectMultipleHeadersTest) { std::string uri = server->GetBaseUrl() + "/post_reflect.html"; std::string key_1 = "key_1"; std::string val_1 = "value_1"; std::string key_2 = "key_2"; std::string val_2 = "value_2"; cpr::Response response = cpr::Post(cpr::Url{uri}, cpr::Header{{key_1, val_1}}, cpr::Header{{key_2, val_2}}); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(200, response.status_code); EXPECT_EQ(val_1, response.header[key_1]); EXPECT_EQ(val_2, response.header[key_2]); } TEST(UrlEncodedPostTests, PostBodyWithFile) { std::string filename{"test_file"}; std::string expected_text(R"({"property1": "value1"})"); std::ofstream test_file; test_file.open(filename); test_file << expected_text; test_file.close(); Url url{server->GetBaseUrl() + "/post_reflect.html"}; cpr::Response response = Post(url, cpr::Header({{"Content-Type", "application/octet-stream"}}), cpr::Body(File("test_file"))); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(std::string{"application/octet-stream"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } TEST(UrlEncodedPostTests, PostBodyWithBuffer) { Url url{server->GetBaseUrl() + "/post_reflect.html"}; std::string expected_text(R"({"property1": "value1"})"); cpr::Response response = Post(url, cpr::Header({{"Content-Type", "application/octet-stream"}}), cpr::Body(Buffer{expected_text.begin(), expected_text.end(), "test_file"})); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/octet-stream"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PostRedirectTests, TempRedirectTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html"); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PostRedirectTests, TempRedirectNoneTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}, Redirect(PostRedirectFlags::NONE)); EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html"); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PostRedirectTests, PermRedirectTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html"); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PostRedirectTests, PermRedirectNoneTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}, Redirect(PostRedirectFlags::NONE)); EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html"); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/prepare_tests.cpp000066400000000000000000000123651475657722200167530ustar00rootroot00000000000000#include #include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(PrepareTests, GetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.PrepareGet(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PrepareTests, OptionsTests) { Url url{server->GetBaseUrl() + "/"}; Session session; session.SetUrl(url); session.PrepareOptions(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PrepareTests, PatchTest) { Url url{server->GetBaseUrl() + "/patch.html"}; Payload payload{{"x", "5"}}; Session session; session.SetUrl(url); session.SetPayload(payload); session.PreparePatch(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Url urlPost{server->GetBaseUrl() + "/post_reflect.html"}; Url urlPut{server->GetBaseUrl() + "/put.html"}; Session session; for (size_t i = 0; i < 3; ++i) { { session.RemoveContent(); session.SetUrl(url); session.PrepareDelete(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{"Header reflect DELETE"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.SetUrl(urlPost); std::string expectedBody = "a1b2c3Post"; session.SetBody(expectedBody); session.PreparePost(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); EXPECT_EQ(expectedBody, response.text); EXPECT_EQ(urlPost, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(url); session.PrepareGet(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.SetUrl(urlPut); session.SetPayload({{"x", "5"}}); session.PreparePut(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(urlPut, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(url); session.PrepareHead(); CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle); Response response = session.Complete(curl_result); std::string expected_text{"Header reflect HEAD"}; EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/proxy_auth_tests.cpp000066400000000000000000000100531475657722200175070ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" // TODO: This requires a local proxy server (squid). This should be replaced with a source // code implementation. #define HTTP_PROXY "127.0.0.1:3128" #define HTTPS_PROXY "127.0.0.1:3128" #define PROXY_USER "u$er" #define PROXY_PASS "p@ss" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(ProxyAuthTests, SetProxyCredentials) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetProxies(Proxies{{"http", HTTP_PROXY}, {"https", HTTPS_PROXY}}); session.SetProxyAuth({{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}, {"https", EncodedAuthentication{PROXY_USER, PROXY_PASS}}}); session.PrepareGet(); EXPECT_TRUE(true); } // TODO: These should be fixed after a source code implementation of a proxy #if defined(false) TEST(ProxyAuthTests, SingleProxyTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}}, ProxyAuthentication{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}}); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyAuthTests, MultipleProxyHttpTest) { Url url{server->GetBaseUrl() + "/hello.html"}; 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}}}); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyAuthTests, CopyProxyTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Proxies proxies{{"http", HTTP_PROXY}}; ProxyAuthentication proxy_auth{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}}; Response response = cpr::Get(url, proxies, proxy_auth); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyAuthTests, ProxySessionTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetProxies(Proxies{{"http", HTTP_PROXY}}); session.SetProxyAuth(ProxyAuthentication{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyAuthTests, ReferenceProxySessionTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Proxies proxies{{"http", HTTP_PROXY}}; ProxyAuthentication proxy_auth{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}}; Session session; session.SetUrl(url); session.SetProxies(proxies); session.SetProxyAuth(proxy_auth); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } #endif int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/proxy_tests.cpp000066400000000000000000000063451475657722200164770ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" // TODO: This uses public servers for proxies and endpoints. This should be replaced with a source // code implementation inside server.cpp #define HTTP_PROXY "51.159.4.98:80" #define HTTPS_PROXY "51.104.53.182:8000" using namespace cpr; TEST(ProxyTests, SingleProxyTest) { Url url{"http://www.httpbin.org/get"}; Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}}); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyTests, MultipleProxyHttpTest) { Url url{"http://www.httpbin.org/get"}; Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}, {"https", HTTPS_PROXY}}); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } // TODO: These should be fixed after a source code implementation of an HTTPS proxy #if defined(false) TEST(ProxyTests, ProxyHttpsTest) { Url url{"https://www.httpbin.org/get"}; Response response = cpr::Get(url, Proxies{{"https", HTTPS_PROXY}}); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyTests, MultipleProxyHttpsTest) { Url url{"https://www.httpbin.org/get"}; Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}, {"https", HTTPS_PROXY}}); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } #endif TEST(ProxyTests, CopyProxyTest) { Url url{"http://www.httpbin.org/get"}; Proxies proxies{{"http", HTTP_PROXY}}; Response response = cpr::Get(url, proxies); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyTests, ProxySessionTest) { Url url{"http://www.httpbin.org/get"}; Session session; session.SetUrl(url); session.SetProxies(Proxies{{"http", HTTP_PROXY}}); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ProxyTests, ReferenceProxySessionTest) { Url url{"http://www.httpbin.org/get"}; Proxies proxies{{"http", HTTP_PROXY}}; Session session; session.SetUrl(url); session.SetProxies(proxies); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/put_tests.cpp000066400000000000000000000224001475657722200161140ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(PutTests, PutTest) { Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, PutUnallowedTest) { Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; Response response = cpr::Put(url, payload); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutTest) { Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; Session session; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutUnallowedTest) { Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; Session session; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutUnallowedAfterGetTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Get(); } Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutUnallowedAfterHeadTest) { Session session; { Url url{server->GetBaseUrl() + "/get.html"}; session.SetUrl(url); Response response = session.Head(); } Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, SessionPutUnallowedAfterPostTest) { Session session; { Url url{server->GetBaseUrl() + "/url_post.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); Response response = session.Post(); } Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; session.SetUrl(url); session.SetPayload(payload); Response response = session.Put(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, AsyncPutTest) { Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; cpr::AsyncResponse future_response = cpr::PutAsync(url, payload); cpr::Response response = future_response.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, AsyncPutUnallowedTest) { Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; cpr::AsyncResponse future_response = cpr::PutAsync(url, payload); cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PutTests, AsyncMultiplePutTest) { Url url{server->GetBaseUrl() + "/put.html"}; Payload payload{{"x", "5"}}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::PutAsync(url, payload)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(PutTests, AsyncMultiplePutUnallowedTest) { Url url{server->GetBaseUrl() + "/put_unallowed.html"}; Payload payload{{"x", "5"}}; std::vector responses; for (size_t i = 0; i < 10; ++i) { responses.emplace_back(cpr::PutAsync(url, payload)); } for (cpr::AsyncResponse& future_response : responses) { cpr::Response response = future_response.get(); std::string expected_text{"Method Not Allowed"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(405, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/raw_body_tests.cpp000066400000000000000000000121341475657722200171150ustar00rootroot00000000000000#include #include #include #include #include "cpr/cpr.h" #include "cpr/multipart.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(BodyPostTests, DefaultUrlEncodedPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Body{"x=5"}); std::string expected_text = "{\n \"x\": 5\n}"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, TextUrlEncodedPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Body{"x=hello world!!~"}); std::string expected_text{ "{\n" " \"x\": hello world!!~\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, TextUrlEncodedNoCopyPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Body body{"x=hello world!!~"}; // body lives through the lifetime of Post, so it doesn't need to be copied Response response = cpr::Post(url, body); std::string expected_text{ "{\n" " \"x\": hello world!!~\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, UrlEncodedManyPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Body{"x=5&y=13"}); std::string expected_text{ "{\n" " \"x\": 5,\n" " \"y\": 13,\n" " \"sum\": 18\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, CustomHeaderNumberPostTest) { Url url{server->GetBaseUrl() + "/json_post.html"}; Response response = cpr::Post(url, Body{"{\"x\":5}"}, Header{{"Content-Type", "application/json"}}); std::string expected_text{"{\"x\":5}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, CustomHeaderTextPostTest) { Url url{server->GetBaseUrl() + "/json_post.html"}; Response response = cpr::Post(url, Body{"{\"x\":\"hello world!!~\"}"}, Header{{"Content-Type", "application/json"}}); std::string expected_text{"{\"x\":\"hello world!!~\"}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, CustomWrongHeaderPostTest) { Url url{server->GetBaseUrl() + "/json_post.html"}; Response response = cpr::Post(url, Body{"{\"x\":5}"}, Header{{"Content-Type", "text/plain"}}); std::string expected_text{"Unsupported Media Type"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(415, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyPostTests, UrlPostBadHostTest) { Url url{"http://bad_host/"}; Response response = cpr::Post(url, Body{"hello=world"}); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(0, response.status_code); // Sometimes the DNS server returns a fake address instead of an NXDOMAIN response, leading to COULDNT_CONNECT. EXPECT_TRUE(response.error.code == ErrorCode::COULDNT_RESOLVE_HOST || response.error.code == ErrorCode::COULDNT_CONNECT); } TEST(BodyPostTests, StringMoveBodyTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Response response = cpr::Post(url, Body{std::string{"x=5"}}); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/resolve_tests.cpp000066400000000000000000000032311475657722200167640ustar00rootroot00000000000000#include #include #include "cpr/cpr.h" #include "httpServer.hpp" using namespace cpr; static HttpServer* server = new HttpServer(); TEST(ResolveTests, HelloWorldTest) { Url url{"http://www.example.com:" + std::to_string(server->GetPort()) + "/hello.html"}; Resolve resolve{"www.example.com", "127.0.0.1", {server->GetPort()}}; Response response = cpr::Get(url, resolve); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ResolveTests, RedirectMultiple) { Url url1{"http://www.example0.com:" + std::to_string(server->GetPort()) + "/resolve_permanent_redirect.html"}; Url url2{"http://www.example1.com:" + std::to_string(server->GetPort()) + "/hello.html"}; Resolve resolve1{"www.example0.com", "127.0.0.1", {server->GetPort()}}; Resolve resolve2{"www.example1.com", "127.0.0.1", {server->GetPort()}}; Response response = cpr::Get(url1, std::vector{resolve1, resolve2}, Header{{"RedirectLocation", url2.str()}}); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url2, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/session_tests.cpp000066400000000000000000001744601475657722200170050ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "cpr/cpr.h" #include #include #include "cpr/accept_encoding.h" #include "httpServer.hpp" using namespace cpr; using namespace std::chrono_literals; static HttpServer* server = new HttpServer(); std::chrono::milliseconds sleep_time{50}; std::chrono::seconds zero{0}; bool write_data(const std::string_view& /*data*/, intptr_t /*userdata*/) { return true; } TEST(SessionGetTests, GetMultipleTimes) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); std::string expected_text{"Hello world!"}; for (size_t i = 0; i < 100; i++) { Response response = session.Get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(SessionPostTests, PostMultipleTimes) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); session.SetPayload({{"x", "5"}}); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; for (size_t i = 0; i < 100; i++) { Response response = session.Post(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(RedirectTests, TemporaryDefaultRedirectTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Session session; session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(RedirectTests, NoTemporaryRedirectTest) { Url url{server->GetBaseUrl() + "/temporary_redirect.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(false)); Response response = session.Get(); std::string expected_text{"Moved Temporarily"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(302, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(RedirectTests, PermanentDefaultRedirectTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Session session; session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(RedirectTests, NoPermanentRedirectTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(false)); Response response = session.Get(); std::string expected_text{"Moved Permanently"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(301, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MaxRedirectsTests, ZeroMaxRedirectsSuccessTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(0L)); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MaxRedirectsTests, ZeroMaxRedirectsFailureTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(0L)); Response response = session.Get(); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(301, response.status_code); EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code); } TEST(MaxRedirectsTests, OneMaxRedirectsSuccessTest) { Url url{server->GetBaseUrl() + "/permanent_redirect.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(1L)); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MaxRedirectsTests, OneMaxRedirectsFailureTest) { Url url{server->GetBaseUrl() + "/two_redirects.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(1L)); Response response = session.Get(); EXPECT_EQ(std::string{}, response.text); EXPECT_EQ(Url{server->GetBaseUrl() + "/permanent_redirect.html"}, response.url); EXPECT_EQ(std::string{}, response.header["content-type"]); EXPECT_EQ(301, response.status_code); EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code); } TEST(MaxRedirectsTests, TwoMaxRedirectsSuccessTest) { Url url{server->GetBaseUrl() + "/two_redirects.html"}; Session session; session.SetUrl(url); session.SetRedirect(Redirect(2L)); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MultipleGetTests, BasicMultipleGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); for (size_t i = 0; i < 100; ++i) { Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, UrlChangeMultipleGetTest) { Session session; { Url url{server->GetBaseUrl() + "/hello.html"}; session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Url url{server->GetBaseUrl() + "/basic.json"}; session.SetUrl(url); Response response = session.Get(); std::string expected_text{ "[\n" " {\n" " \"first_key\": \"first_value\",\n" " \"second_key\": \"second_value\"\n" " }\n" "]"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, HeaderMultipleGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); session.SetHeader(Header{{"hello", "world"}}); for (size_t i = 0; i < 100; ++i) { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, HeaderChangeMultipleGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); session.SetHeader(Header{{"hello", "world"}}); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"world"}, response.header["hello"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } session.SetHeader(Header{{"key", "value"}, {"lorem", "ipsum"}}); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"value"}, response.header["key"]); EXPECT_EQ(std::string{"ipsum"}, response.header["lorem"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } Header& headerMap = session.GetHeader(); headerMap.erase("key"); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(std::string{"ipsum"}, response.header["lorem"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, ParameterMultipleGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetParameters({{"hello", "world"}}); for (size_t i = 0; i < 100; ++i) { Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, ParameterChangeMultipleGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetParameters({{"hello", "world"}}); { Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } session.SetUrl(url); session.SetParameters({{"key", "value"}}); { Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?key=value"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, BasicAuthenticationMultipleGetTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Session session; session.SetUrl(url); session.SetAuth(Authentication{"user", "password", AuthMode::BASIC}); for (size_t i = 0; i < 100; ++i) { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(MultipleGetTests, BasicAuthenticationChangeMultipleGetTest) { Url url{server->GetBaseUrl() + "/basic_auth.html"}; Session session; session.SetUrl(url); session.SetAuth(Authentication{"user", "password", AuthMode::BASIC}); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } session.SetAuth(Authentication{"user", "bad_password", AuthMode::BASIC}); { Response response = session.Get(); EXPECT_EQ(std::string{"Unauthorized"}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } session.SetAuth(Authentication{"bad_user", "password", AuthMode::BASIC}); { Response response = session.Get(); EXPECT_EQ(std::string{"Unauthorized"}, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]); EXPECT_EQ(401, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(ParameterTests, ParameterSingleTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); Parameters parameters{{"hello", "world"}}; session.SetParameters(parameters); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ParameterTests, ParameterMultipleTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); Parameters parameters{{"hello", "world"}, {"key", "value"}}; session.SetParameters(parameters); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(Url{url + "?hello=world&key=value"}, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(FullRequestUrlTest, GetFullRequestUrlNoParametersTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); std::string expected_text{server->GetBaseUrl() + "/hello.html"}; EXPECT_EQ(expected_text, session.GetFullRequestUrl()); } TEST(FullRequestUrlTest, GetFullRequestUrlOneParameterTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); Parameters parameters{{"hello", "world"}}; session.SetParameters(parameters); std::string expected_text{server->GetBaseUrl() + "/hello.html" + "?hello=world"}; EXPECT_EQ(expected_text, session.GetFullRequestUrl()); } TEST(FullRequestUrlTest, GetFullRequestUrlMultipleParametersTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); Parameters parameters{{"hello", "world"}, {"key", "value"}}; session.SetParameters(parameters); std::string expected_text{server->GetBaseUrl() + "/hello.html" + "?hello=world&key=value"}; EXPECT_EQ(expected_text, session.GetFullRequestUrl()); } TEST(TimeoutTests, SetTimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetTimeout(0L); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(TimeoutTests, SetTimeoutLongTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetTimeout(10000L); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(TimeoutTests, SetTimeoutLowSpeed) { Url url{server->GetBaseUrl() + "/low_speed_timeout.html"}; Session session; session.SetUrl(url); session.SetTimeout(1000); Response response = session.Get(); EXPECT_EQ(url, response.url); // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(TimeoutTests, SetChronoTimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetTimeout(std::chrono::milliseconds{0}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(TimeoutTests, SetChronoTimeoutLongTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetTimeout(std::chrono::milliseconds{10000}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(TimeoutTests, SetChronoLiteralTimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetTimeout(2s); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(TimeoutTests, SetChronoLiteralTimeoutLowSpeed) { Url url{server->GetBaseUrl() + "/low_speed_timeout.html"}; Session session; session.SetUrl(url); session.SetTimeout(1000ms); Response response = session.Get(); EXPECT_EQ(url, response.url); // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code); } TEST(ConnectTimeoutTests, SetConnectTimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetConnectTimeout(0L); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ConnectTimeoutTests, SetConnectTimeoutLongTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetConnectTimeout(10000L); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ConnectTimeoutTests, SetChronoConnectTimeoutTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetConnectTimeout(std::chrono::milliseconds{0}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(ConnectTimeoutTests, SetChronoConnectTimeoutLongTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetConnectTimeout(std::chrono::milliseconds{10000}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(LowSpeedTests, SetLowSpeedTest) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetLowSpeed({1, 1}); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PayloadTests, SetPayloadTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); session.SetPayload({{"x", "5"}}); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(PayloadTests, SetPayloadLValueTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); Payload payload{{"x", "5"}}; session.SetPayload(payload); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MultipartTests, SetMultipartTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Session session; session.SetUrl(url); session.SetMultipart({{"x", "5"}}); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MultipartTests, SetMultipartValueTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Session session; session.SetUrl(url); Multipart multipart{{"x", "5"}}; session.SetMultipart(multipart); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(MultipartTests, SetMultipartVectorPartsTest) { Url url{server->GetBaseUrl() + "/form_post.html"}; Session session; session.SetUrl(url); Multipart multipart{std::vector{Part{"x", "5"}}}; session.SetMultipart(multipart); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": \"5\"\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyTests, SetBodyTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); session.SetBody(Body{"x=5"}); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(BodyTests, SetBodyValueTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; Session session; session.SetUrl(url); Body body{"x=5"}; session.SetBody(body); Response response = session.Post(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(DigestTests, SetDigestTest) { Url url{server->GetBaseUrl() + "/digest_auth.html"}; Session session; session.SetUrl(url); session.SetAuth({"user", "password", AuthMode::DIGEST}); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UserAgentTests, SetUserAgentTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; UserAgent userAgent{"Test User Agent"}; Session session; session.SetUrl(url); session.SetUserAgent(userAgent); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(userAgent, response.header["User-Agent"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(UserAgentTests, SetUserAgentStringViewTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; UserAgent userAgent{std::string_view{"Test User Agent"}}; Session session; session.SetUrl(url); session.SetUserAgent(userAgent); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(userAgent, response.header["User-Agent"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CookiesTests, BasicCookiesTest) { Url url{server->GetBaseUrl() + "/basic_cookies.html"}; Session session{}; session.SetUrl(url); Response response = session.Get(); Cookies res_cookies{response.cookies}; std::string expected_text{"Basic Cookies"}; cpr::Cookies expectedCookies{ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, {"lang", "en-US", "127.0.0.1", false, "/", true, HttpServer::GetCookieExpiresIn100HoursTimePoint()}, }; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) { EXPECT_EQ(expectedCookie->GetName(), cookie->GetName()); EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue()); EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain()); EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains()); EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath()); EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly()); EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires()); } } TEST(CookiesTests, ClientSetCookiesTest) { Url url{server->GetBaseUrl() + "/cookies_reflect.html"}; { Session session{}; session.SetUrl(url); session.SetCookies(Cookies{ {"SID", "31d4d96e407aad42"}, {"lang", "en-US"}, }); Response response = session.Get(); std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"}; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); } { Session session{}; session.SetUrl(url); Cookies cookie{ {"SID", "31d4d96e407aad42"}, {"lang", "en-US"}, }; session.SetCookies(cookie); Response response = session.Get(); std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"}; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); } } TEST(CookiesTests, RedirectionWithChangingCookiesTest) { Url url{server->GetBaseUrl() + "/redirection_with_changing_cookies.html"}; { Session session{}; session.SetUrl(url); session.SetCookies(Cookies{ {"SID", "31d4d96e407aad42"}, {"lang", "en-US"}, }); session.SetRedirect(Redirect(0L)); Response response = session.Get(); std::string expected_text{"Received cookies are: SID=31d4d96e407aad42; lang=en-US;"}; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); } { Session session{}; session.SetUrl(url); session.SetRedirect(Redirect(1L)); Response response = session.Get(); std::string expected_text{"Received cookies are: lang=en-US; SID=31d4d96e407aad42"}; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); } { Session session{}; session.SetUrl(url); session.SetCookies(Cookies{ {"SID", "empty_sid"}, }); session.SetRedirect(Redirect(1L)); Response response = session.Get(); std::string expected_text{"Received cookies are: lang=en-US; SID=31d4d96e407aad42; SID=empty_sid;"}; EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); EXPECT_EQ(expected_text, response.text); } } TEST(DifferentMethodTests, GetPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(DifferentMethodTests, PostGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(DifferentMethodTests, GetPostGetTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(DifferentMethodTests, PostGetPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(DifferentMethodTests, MultipleGetPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); for (size_t i = 0; i < 100; ++i) { { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } } TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Url urlPost{server->GetBaseUrl() + "/post_reflect.html"}; Url urlPut{server->GetBaseUrl() + "/put.html"}; Url urlMultipartPost{server->GetBaseUrl() + "/post_file_upload.html"}; Session session; for (size_t i = 0; i < 10; ++i) { { session.RemoveContent(); session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(urlMultipartPost); std::string fileContentsBinary{"this is a binary payload"}; std::string fileExtension = ".myfile"; session.SetMultipart(cpr::Multipart{{"files", cpr::Buffer{fileContentsBinary.begin(), fileContentsBinary.end(), "myfile.jpg"}}, {"file_types", "[\"" + fileExtension + "\"]"}}); Response response = session.Post(); std::string expected_text{"{\n \"files\": \"myfile.jpg=this is a binary payload\",\n \"file_types\": \"[\".myfile\"]\"\n}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(urlMultipartPost, response.url); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(url); Response response = session.Delete(); std::string expected_text{"Header reflect DELETE"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.SetUrl(urlPost); std::string expectedBody = "a1b2c3Post"; session.SetBody(expectedBody); Response response = session.Post(); EXPECT_EQ(expectedBody, response.text); EXPECT_EQ(urlPost, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(url); Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.SetUrl(urlPut); session.SetPayload({{"x", "5"}}); Response response = session.Put(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(urlPut, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { session.RemoveContent(); session.SetUrl(url); Response response = session.Head(); std::string expected_text{"Header reflect HEAD"}; EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } } TEST(CurlHolderManipulateTests, CustomOptionTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); curl_easy_setopt(session.GetCurlHolder()->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE); { Response response = session.Get(); std::string expected_text{"Header reflect GET"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } { Response response = session.Post(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } } TEST(LocalPortTests, SetLocalPortTest) { Url url{server->GetBaseUrl() + "/local_port.html"}; Session session; uint16_t local_port{0}; uint16_t local_port_range{0}; Response response; // Try up to 10 times to get a free local port for (size_t i = 0; i < 10; i++) { session.SetUrl(url); local_port = 40252 + (i * 100); // beware of HttpServer::GetPort when changing local_port_range = 7000; session.SetLocalPort(local_port); session.SetLocalPortRange(local_port_range); // expected response: body contains port number in specified range // NOTE: even when trying up to 7000 ports there is the chance that all of them are occupied. // It would be possible to also check here for ErrorCode::UNKNOWN_ERROR but that somehow seems // wrong as then this test would pass in case SetLocalPort does not work at all // or in other words: we have to assume that at least one port in the specified range is free. response = session.Get(); if (response.error.code != ErrorCode::UNKNOWN_ERROR) { break; } } EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); // NOLINTNEXTLINE(google-runtime-int) unsigned long port_from_response = std::strtoul(response.text.c_str(), nullptr, 10); EXPECT_EQ(errno, 0); EXPECT_GE(port_from_response, local_port); EXPECT_LE(port_from_response, local_port + local_port_range); } TEST(LocalPortTests, SetOptionTest) { Url url{server->GetBaseUrl() + "/local_port.html"}; Session session; uint16_t local_port{0}; uint16_t local_port_range{0}; Response response; // Try up to 10 times to get a free local port for (size_t i = 0; i < 10; i++) { session.SetUrl(url); local_port = 30252 + (i * 100); // beware of HttpServer::GetPort when changing local_port_range = 7000; session.SetOption(LocalPort(local_port)); session.SetOption(LocalPortRange(local_port_range)); // expected response: body contains port number in specified range // NOTE: even when trying up to 7000 ports there is the chance that all of them are occupied. // It would be possible to also check here for ErrorCode::UNKNOWN_ERROR but that somehow seems // wrong as then this test would pass in case SetLocalPort does not work at all // or in other words: we have to assume that at least one port in the specified range is free. response = session.Get(); if (response.error.code != ErrorCode::UNKNOWN_ERROR) { break; } } EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); // NOLINTNEXTLINE(google-runtime-int) unsigned long port_from_response = std::strtoul(response.text.c_str(), nullptr, 10); EXPECT_EQ(errno, 0); EXPECT_GE(port_from_response, local_port); EXPECT_LE(port_from_response, local_port + local_port_range); } // The tests using the port of the server as a source port for curl fail for windows. // The reason probably is that Windows allows two sockets to bind to the same port if the full hostname is different. // In these tests, mongoose binds to http://127.0.0.1:61936, while libcurl binds to a different hostname, but still port 61936. // This seems to be okay for Windows, however, these tests expect an error like on Linux and MacOS // We therefore, simply skip the tests if Windows is used #ifndef _WIN32 TEST(LocalPortTests, SetLocalPortTestOccupied) { Url url{server->GetBaseUrl() + "/local_port.html"}; Session session; session.SetUrl(url); session.SetLocalPort(server->GetPort()); // expected response: request cannot be made as port is already occupied Response response = session.Get(); EXPECT_EQ(ErrorCode::INTERFACE_FAILED, response.error.code); } TEST(LocalPortTests, SetOptionTestOccupied) { Url url{server->GetBaseUrl() + "/local_port.html"}; Session session; session.SetUrl(url); session.SetOption(LocalPort(server->GetPort())); // expected response: request cannot be made as port is already occupied Response response = session.Get(); EXPECT_EQ(ErrorCode::INTERFACE_FAILED, response.error.code); } #endif // _WIN32 TEST(BasicTests, ReserveResponseString) { Url url{server->GetBaseUrl() + "/hello.html"}; Session session; session.SetUrl(url); session.SetReserveSize(4096); Response response = session.Get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_GE(response.text.capacity(), 4096); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } std::vector Split(const std::string& s) { std::vector encodings; std::stringstream ss(s); std::string encoding; while (std::getline(ss, encoding, ',')) { encoding.erase(std::remove_if(encoding.begin(), encoding.end(), isspace), encoding.end()); // Trim encodings.push_back(encoding); } return encodings; } void CompareEncodings(const std::string& response, const std::vector& expected) { const std::vector responseVec = Split(response); EXPECT_EQ(responseVec.size(), expected.size()); for (const std::string& encoding : expected) { EXPECT_TRUE(std::find(responseVec.begin(), responseVec.end(), encoding) != responseVec.end()); } } TEST(BasicTests, AcceptEncodingTestWithMethodsStringMap) { Url url{server->GetBaseUrl() + "/check_accept_encoding.html"}; Session session; session.SetUrl(url); session.SetAcceptEncoding({{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}}); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); CompareEncodings(response.text, std::vector{"deflate", "gzip", "zlib"}); } TEST(BasicTests, AcceptEncodingTestWithMethodsStringMapLValue) { Url url{server->GetBaseUrl() + "/check_accept_encoding.html"}; Session session; session.SetUrl(url); AcceptEncoding accept_encoding{{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}}; session.SetAcceptEncoding(accept_encoding); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); CompareEncodings(response.text, std::vector{"deflate", "gzip", "zlib"}); } TEST(BasicTests, AcceptEncodingTestWithCostomizedString) { Url url{server->GetBaseUrl() + "/check_accept_encoding.html"}; Session session; session.SetUrl(url); session.SetAcceptEncoding({{"deflate", "gzip", "zlib"}}); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); CompareEncodings(response.text, std::vector{"deflate", "gzip", "zlib"}); } TEST(BasicTests, AcceptEncodingTestWithCostomizedStringLValue) { Url url{server->GetBaseUrl() + "/check_accept_encoding.html"}; Session session; session.SetUrl(url); AcceptEncoding accept_encoding{{"deflate", "gzip", "zlib"}}; session.SetAcceptEncoding(accept_encoding); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); CompareEncodings(response.text, std::vector{"deflate", "gzip", "zlib"}); } TEST(BasicTests, AcceptEncodingTestDisabled) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); session.SetAcceptEncoding({AcceptEncodingMethods::disabled}); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); // Ensure no 'Accept-Encoding' header got added EXPECT_TRUE(response.header.find("Accept-Encoding") == response.header.end()); } TEST(BasicTests, AcceptEncodingTestDisabledMultipleThrow) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; Session session; session.SetUrl(url); session.SetAcceptEncoding({AcceptEncodingMethods::disabled, AcceptEncodingMethods::deflate}); EXPECT_THROW(session.Get(), std::invalid_argument); } TEST(BasicTests, DisableHeaderExpect100ContinueTest) { Url url{server->GetBaseUrl() + "/check_expect_100_continue.html"}; std::string filename{"test_file"}; std::string content{std::string(1024 * 1024, 'a')}; std::ofstream test_file; test_file.open(filename); test_file << content; test_file.close(); Session session{}; session.SetUrl(url); session.SetMultipart({{"file", File{"test_file"}}}); Response response = session.Post(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncGetTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); cpr::AsyncResponse future = session->GetAsync(); std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } TEST(AsyncRequestsTests, AsyncGetMultipleTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; std::vector> sessions; for (size_t i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); session->SetUrl(url); sessions.emplace_back(session); responses.emplace_back(session->GetAsync()); } for (cpr::AsyncResponse& future : responses) { std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } } TEST(AsyncRequestsTests, AsyncGetMultipleTemporarySessionTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); session->SetUrl(url); responses.emplace_back(session->GetAsync()); } for (cpr::AsyncResponse& future : responses) { std::string expected_text{"Hello world!"}; cpr::Response response = future.get(); EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); } } TEST(AsyncRequestsTests, AsyncGetMultipleReflectTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::vector responses; for (size_t i = 0; i < 10; ++i) { std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetParameters({{"key", std::to_string(i)}}); responses.emplace_back(session->GetAsync()); } int i = 0; for (cpr::AsyncResponse& future : responses) { cpr::Response response = future.get(); std::string expected_text{"Hello world!"}; Url expected_url{url + "?key=" + std::to_string(i)}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(expected_url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); ++i; } } TEST(AsyncRequestsTests, AsyncWritebackDownloadTest) { std::shared_ptr session = std::make_shared(); cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"}; session->SetUrl(url); session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}}); cpr::AsyncResponse future = session->DownloadAsync(cpr::WriteCallback{write_data, 0}); cpr::Response response = future.get(); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncPostTest) { Url url{server->GetBaseUrl() + "/url_post.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload({{"x", "5"}}); cpr::AsyncResponse future = session->PostAsync(); cpr::Response response = future.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(201, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncPutTest) { Url url{server->GetBaseUrl() + "/put.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload({{"x", "5"}}); cpr::AsyncResponse future = session->PutAsync(); cpr::Response response = future.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncHeadTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); cpr::AsyncResponse future = session->HeadAsync(); cpr::Response response = future.get(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncDeleteTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); cpr::AsyncResponse future = session->DeleteAsync(); cpr::Response response = future.get(); std::string expected_text{"Header reflect DELETE"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncOptionsTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); cpr::AsyncResponse future = session->OptionsAsync(); cpr::Response response = future.get(); std::string expected_text{"Header reflect OPTIONS"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(AsyncRequestsTests, AsyncPatchTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); cpr::AsyncResponse future = session->PatchAsync(); cpr::Response response = future.get(); std::string expected_text{"Header reflect PATCH"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, GetCallbackTest) { Url url{server->GetBaseUrl() + "/hello.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->GetCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{"Hello world!"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, PostCallbackTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->PostCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{"Header reflect POST"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, PutCallbackTest) { Url url{server->GetBaseUrl() + "/put.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); session->SetPayload({{"x", "5"}}); auto future = session->PutCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{ "{\n" " \"x\": 5\n" "}"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, HeadCallbackTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->HeadCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{""}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, DeleteCallbackTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->DeleteCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{"Header reflect DELETE"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, OptionsCallbackTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->OptionsCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{"Header reflect OPTIONS"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, PatchCallbackTest) { Url url{server->GetBaseUrl() + "/header_reflect.html"}; std::shared_ptr session = std::make_shared(); session->SetUrl(url); auto future = session->PatchCallback([](Response r) { return r; }); std::this_thread::sleep_for(sleep_time); cpr::Response response = future.get(); std::string expected_text{"Header reflect PATCH"}; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code); } TEST(CallbackTests, Move) { auto session = Session(); session.SetDebugCallback(DebugCallback([](auto, auto, auto) {})); auto use = +[](Session& s) { s.SetUrl(server->GetBaseUrl()); s.Get(); }; use(session); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/singleton_tests.cpp000066400000000000000000000011761475657722200173150ustar00rootroot00000000000000#include #include "singleton_tests.hpp" // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) CPR_SINGLETON_IMPL(TestSingleton) TEST(SingletonTests, GetInstanceTest) { const TestSingleton* singleton = TestSingleton::GetInstance(); EXPECT_NE(singleton, nullptr); } TEST(SingletonTests, ExitInstanceTest) { TestSingleton* singleton = TestSingleton::GetInstance(); TestSingleton::ExitInstance(); singleton = TestSingleton::GetInstance(); EXPECT_EQ(singleton, nullptr); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/singleton_tests.hpp000066400000000000000000000002151475657722200173130ustar00rootroot00000000000000#pragma once #include "cpr/cpr.h" class TestSingleton { CPR_SINGLETON_DECL(TestSingleton) private: TestSingleton() = default; }; cpr-1.11.2/test/ssl_tests.cpp000066400000000000000000000224631475657722200161160ustar00rootroot00000000000000#include #include #include #include #include #include "cpr/cprtypes.h" #include "cpr/filesystem.h" #include "cpr/ssl_options.h" #include "httpsServer.hpp" using namespace cpr; static HttpsServer* server; static std::string caCertPath; static std::string serverPubKeyPath; static std::string clientKeyPath; static std::string clientCertPath; std::string loadFileContent(const std::string filePath) { std::ifstream file(filePath); std::stringstream buffer; buffer << file.rdbuf(); return buffer.str(); } TEST(SslTests, HelloWorldTestSimpel) { std::this_thread::sleep_for(std::chrono::seconds(1)); Url url{server->GetBaseUrl() + "/hello.html"}; std::string baseDirPath{server->getBaseDirPath()}; std::string crtPath{baseDirPath + "certificates/"}; std::string keyPath{baseDirPath + "keys/"}; 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}); Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); std::string expected_text = "Hello world!"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message; } TEST(SslTests, HelloWorldTestFull) { std::this_thread::sleep_for(std::chrono::seconds(1)); Url url{server->GetBaseUrl() + "/hello.html"}; std::string baseDirPath{server->getBaseDirPath()}; std::string crtPath{baseDirPath + "certificates/"}; std::string keyPath{baseDirPath + "keys/"}; SslOptions sslOpts = Ssl(ssl::TLSv1{}, ssl::ALPN{false}, #if SUPPORT_NPN ssl::NPN{false}, #endif // DEBUG 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}); Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); std::string expected_text = "Hello world!"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message; } TEST(SslTests, GetCertInfos) { std::this_thread::sleep_for(std::chrono::seconds(1)); Url url{server->GetBaseUrl() + "/hello.html"}; std::string baseDirPath{server->getBaseDirPath()}; std::string crtPath{baseDirPath + "certificates/"}; std::string keyPath{baseDirPath + "keys/"}; 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}); Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); std::vector certInfos = response.GetCertInfos(); std::string expected_text = "Hello world!"; std::vector expectedCertInfos{ CertInfo{ "Subject:CN = test-server", "Issuer:C = GB, O = Example, CN = Sub CA", "Version:2", "Serial Number:acbefc2cde5b900b55548396556765d4", "Signature Algorithm:ED25519", "Public Key Algorithm:ED25519", "X509v3 Authority Key Identifier:9B:B1:9B:21:61:DC:66:2B:3A:AD:ED:84:F1:05:B6:CE:99:82:C1:FC", "X509v3 Basic Constraints:CA:FALSE", "X509v3 Extended Key Usage:TLS Web Client Authentication, TLS Web Server Authentication", "X509v3 Key Usage:Digital Signature, Key Encipherment", "X509v3 Subject Key Identifier:66:47:54:F8:25:97:56:9A:52:56:35:B4:A7:52:60:0C:E7:4F:33:09", "X509v3 Subject Alternative Name:DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1", "Start date:May 7 10:18:22 2024 GMT", "Expire date:May 6 10:18:22 2029 GMT", "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:", R"(Cert:-----BEGIN CERTIFICATE----- MIIBtDCCAWagAwIBAgIRAKy+/CzeW5ALVVSDllVnZdQwBQYDK2VwMDAxCzAJBgNV BAYTAkdCMRAwDgYDVQQKDAdFeGFtcGxlMQ8wDQYDVQQDDAZTdWIgQ0EwHhcNMjQw NTA3MTAxODIyWhcNMjkwNTA2MTAxODIyWjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl cjAqMAUGAytlcAMhACdLUqJFSyspgGKJiXNlnOLU2dO/TLV+b8aIZNAX7EuVo4Gu MIGrMB8GA1UdIwQYMBaAFJuxmyFh3GYrOq3thPEFts6ZgsH8MAwGA1UdEwEB/wQC MAAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIF oDAdBgNVHQ4EFgQUZkdU+CWXVppSVjW0p1JgDOdPMwkwLAYDVR0RBCUwI4IJbG9j YWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMAUGAytlcANBAG1j2RGjm8ef tiMSJ+k04KGjIL7734D+UwidjOSCQnbCVRPofIaDMwuan5IqP97pMnjAsbw/QukX +Z9sFTWjAQk= -----END CERTIFICATE----- )", }, }; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message; EXPECT_EQ(1, certInfos.size()); for (auto certInfo_it = certInfos.begin(), expectedCertInfo_it = expectedCertInfos.begin(); certInfo_it != certInfos.end() && expectedCertInfo_it != expectedCertInfos.end(); certInfo_it++, expectedCertInfo_it++) { 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++) { std::string search_string = "Identifier:keyid:"; std::size_t search_index = (*entry_it).find(search_string); if (search_index != std::string::npos) { (*entry_it).replace(search_index, search_string.length(), "Identifier:"); search_string = "\n"; search_index = (*entry_it).find(search_string); if (search_index != std::string::npos) { (*entry_it).replace(search_index, search_string.length(), ""); } } EXPECT_EQ(*expectedEntry_it, *entry_it); } std::cout << '\n'; } } #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION TEST(SslTests, LoadCertFromBufferTestSimpel) { std::this_thread::sleep_for(std::chrono::seconds(1)); Url url{server->GetBaseUrl() + "/hello.html"}; std::string baseDirPath{server->getBaseDirPath()}; std::string crtPath{baseDirPath + "certificates/"}; std::string keyPath{baseDirPath + "keys/"}; std::string certBuffer = loadFileContent(crtPath + "ca-bundle.crt"); 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}); Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); std::string expected_text = "Hello world!"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message; } #endif #if SUPPORT_CURLOPT_SSLKEY_BLOB TEST(SslTests, LoadKeyFromBlobTestSimpel) { std::this_thread::sleep_for(std::chrono::seconds(1)); Url url{server->GetBaseUrl() + "/hello.html"}; std::string baseDirPath{server->getBaseDirPath()}; std::string crtPath{baseDirPath + "certificates/"}; std::string keyPath{baseDirPath + "keys/"}; std::string keyBuffer = loadFileContent(keyPath + "client.key"); 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}); Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); std::string expected_text = "Hello world!"; EXPECT_EQ(expected_text, response.text); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]); EXPECT_EQ(200, response.status_code); EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message; } #endif fs::path GetBasePath(const std::string& execPath) { return fs::path(fs::path{execPath}.parent_path().string() + "/").make_preferred(); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); fs::path baseDirPath = fs::path{GetBasePath(argv[0]).string() + "data/"}; fs::path serverCertPath = fs::path{baseDirPath}.append("certificates/server.crt"); fs::path serverKeyPath = fs::path{baseDirPath}.append("keys/server.key"); server = new HttpsServer(std::move(baseDirPath), std::move(serverCertPath), std::move(serverKeyPath)); ::testing::AddGlobalTestEnvironment(server); return RUN_ALL_TESTS(); } cpr-1.11.2/test/structures_tests.cpp000066400000000000000000000033761475657722200175420ustar00rootroot00000000000000#include "cpr/cprtypes.h" #include #include #include "cpr/parameters.h" #include "cpr/payload.h" using namespace cpr; TEST(PayloadTests, UseStringVariableTest) { std::string value1 = "hello"; std::string key2 = "key2"; Payload payload{{"key1", value1}, {key2, "world"}}; std::string expected = "key1=hello&key2=world"; EXPECT_EQ(payload.GetContent(CurlHolder()), expected); } TEST(PayloadTests, DisableEncodingTest) { std::string key1 = "key1"; std::string key2 = "key2§$%&/"; std::string value1 = "hello.,.,"; std::string value2 = "hello"; Payload payload{{key1, value1}, {key2, value2}}; payload.encode = false; std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2; EXPECT_EQ(payload.GetContent(CurlHolder()), expected); } TEST(ParametersTests, UseStringVariableTest) { std::string value1 = "hello"; std::string key2 = "key2"; Parameters parameters{{"key1", value1}, {key2, "world"}}; std::string expected = "key1=hello&key2=world"; EXPECT_EQ(parameters.GetContent(CurlHolder()), expected); } TEST(ParametersTests, DisableEncodingTest) { std::string key1 = "key1"; std::string key2 = "key2§$%&/"; std::string value1 = "hello.,.,"; std::string value2 = "hello"; Parameters parameters{{key1, value1}, {key2, value2}}; parameters.encode = false; std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2; EXPECT_EQ(parameters.GetContent(CurlHolder()), expected); } TEST(UrlToAndFromString, UrlTests) { std::string s{"https://github.com/whoshuu/cpr"}; cpr::Url url = s; EXPECT_EQ(s, url.str()); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/threadpool_tests.cpp000066400000000000000000000047201475657722200174520ustar00rootroot00000000000000#include #include #include #include "cpr/threadpool.h" TEST(ThreadPoolTests, DISABLED_BasicWorkOneThread) { std::atomic_uint32_t invCount{0}; uint32_t invCountExpected{100}; { cpr::ThreadPool tp; tp.SetMinThreadNum(1); tp.SetMaxThreadNum(1); tp.Start(0); for (size_t i = 0; i < invCountExpected; ++i) { tp.Submit([&invCount]() -> void { invCount++; }); } // Wait for the thread pool to finish its work tp.Wait(); } EXPECT_EQ(invCount, invCountExpected); } TEST(ThreadPoolTests, DISABLED_BasicWorkMultipleThreads) { std::atomic_uint32_t invCount{0}; uint32_t invCountExpected{100}; { cpr::ThreadPool tp; tp.SetMinThreadNum(1); tp.SetMaxThreadNum(10); tp.Start(0); for (size_t i = 0; i < invCountExpected; ++i) { tp.Submit([&invCount]() -> void { invCount++; }); } // Wait for the thread pool to finish its work tp.Wait(); } EXPECT_EQ(invCount, invCountExpected); } TEST(ThreadPoolTests, DISABLED_PauseResumeSingleThread) { std::atomic_uint32_t invCount{0}; uint32_t repCount{100}; uint32_t invBunchSize{20}; cpr::ThreadPool tp; tp.SetMinThreadNum(1); tp.SetMaxThreadNum(10); tp.Start(0); for (size_t i = 0; i < repCount; ++i) { tp.Pause(); EXPECT_EQ(invCount, i * invBunchSize); for (size_t e = 0; e < invBunchSize; ++e) { tp.Submit([&invCount]() -> void { invCount++; }); } tp.Resume(); // Wait for the thread pool to finish its work tp.Wait(); EXPECT_EQ(invCount, (i + 1) * invBunchSize); } } TEST(ThreadPoolTests, DISABLED_PauseResumeMultipleThreads) { std::atomic_uint32_t invCount{0}; uint32_t repCount{100}; uint32_t invBunchSize{20}; cpr::ThreadPool tp; tp.SetMinThreadNum(1); tp.SetMaxThreadNum(10); tp.Start(0); for (size_t i = 0; i < repCount; ++i) { tp.Pause(); EXPECT_EQ(invCount, i * invBunchSize); for (size_t e = 0; e < invBunchSize; ++e) { tp.Submit([&invCount]() -> void { invCount++; }); } tp.Resume(); // Wait for the thread pool to finish its work tp.Wait(); EXPECT_EQ(invCount, (i + 1) * invBunchSize); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/util_tests.cpp000066400000000000000000000205561475657722200162730ustar00rootroot00000000000000#include #include #include "cpr/cprtypes.h" #include "cpr/util.h" using namespace cpr; TEST(UtilParseCookiesTests, BasicParseTest) { 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))}}; curl_slist* raw_cookies = new curl_slist{ (char*) "127.0.0.1\tFALSE\t/\tFALSE\t1656908640\tstatus\ton", new curl_slist{ (char*) "127.0.0.1\tFALSE\t/\tFALSE\t0\tname\tdebug", nullptr, }, }; Cookies cookies = util::parseCookies(raw_cookies); for (auto cookie = cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) { EXPECT_EQ(expectedCookie->GetName(), cookie->GetName()); EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue()); EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain()); EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains()); EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath()); EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly()); EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires()); } delete raw_cookies->next; delete raw_cookies; } TEST(UtilParseHeaderTests, BasicParseTest) { std::string header_string{ "HTTP/1.1 200 OK\r\n" "Server: nginx\r\n" "Date: Sun, 05 Mar 2017 00:34:54 GMT\r\n" "Content-Type: application/json\r\n" "Content-Length: 351\r\n" "Connection: keep-alive\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Credentials: true\r\n" "\r\n"}; Header header = util::parseHeader(header_string); EXPECT_EQ(std::string{"nginx"}, header["Server"]); EXPECT_EQ(std::string{"Sun, 05 Mar 2017 00:34:54 GMT"}, header["Date"]); EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]); EXPECT_EQ(std::string{"351"}, header["Content-Length"]); EXPECT_EQ(std::string{"keep-alive"}, header["Connection"]); EXPECT_EQ(std::string{"*"}, header["Access-Control-Allow-Origin"]); EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]); } TEST(UtilParseHeaderTests, NewlineTest) { std::string header_string{ "HTTP/1.1 200 OK\r\n" "Auth:\n" "Access-Control-Allow-Credentials: true\r\n" "\r\n"}; Header header = util::parseHeader(header_string); EXPECT_EQ(std::string{""}, header["Server"]); EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]); } TEST(UtilParseHeaderTests, SpaceNewlineTest) { std::string header_string{ "HTTP/1.1 200 OK\r\n" "Auth: \n" "Access-Control-Allow-Credentials: true\r\n" "\r\n"}; Header header = util::parseHeader(header_string); EXPECT_EQ(std::string{""}, header["Server"]); EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]); } TEST(UtilParseHeaderTests, CarriageReturnNewlineTest) { std::string header_string{ "HTTP/1.1 200 OK\n" "Auth:\r\n" "Access-Control-Allow-Credentials: true\r\n" "\r\n"}; Header header = util::parseHeader(header_string); EXPECT_EQ(std::string{""}, header["Server"]); EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]); } TEST(UtilParseHeaderTests, SpaceCarriageReturnNewlineTest) { std::string header_string{ "HTTP/1.1 200 OK\n" "Auth: \r\n" "Access-Control-Allow-Credentials: true\r\n" "\r\n"}; Header header = util::parseHeader(header_string); EXPECT_EQ(std::string{""}, header["Server"]); EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]); } TEST(UtilParseHeaderTests, BasicStatusLineTest) { std::string header_string{ "HTTP/1.1 200 OK\r\n" "Server: nginx\r\n" "Content-Type: application/json\r\n" "\r\n"}; std::string status_line; std::string reason; Header header = util::parseHeader(header_string, &status_line, &reason); EXPECT_EQ(std::string{"HTTP/1.1 200 OK"}, status_line); EXPECT_EQ(std::string{"OK"}, reason); EXPECT_EQ(std::string{"nginx"}, header["Server"]); EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]); } TEST(UtilParseHeaderTests, NewlineStatusLineTest) { std::string header_string{ "HTTP/1.1 407 Proxy Authentication Required\n" "Server: nginx\r\n" "Content-Type: application/json\r\n" "\r\n"}; std::string status_line; std::string reason; Header header = util::parseHeader(header_string, &status_line, &reason); EXPECT_EQ(std::string{"HTTP/1.1 407 Proxy Authentication Required"}, status_line); EXPECT_EQ(std::string{"Proxy Authentication Required"}, reason); EXPECT_EQ(std::string{"nginx"}, header["Server"]); EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]); } TEST(UtilParseHeaderTests, NoReasonSpaceTest) { std::string header_string{ "HTTP/1.1 200 \n" "Server: nginx\r\n" "Content-Type: application/json\r\n" "\r\n"}; std::string status_line; std::string reason; Header header = util::parseHeader(header_string, &status_line, &reason); EXPECT_EQ(std::string{"HTTP/1.1 200"}, status_line); EXPECT_EQ(std::string{""}, reason); EXPECT_EQ(std::string{"nginx"}, header["Server"]); EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]); } TEST(UtilParseHeaderTests, NoReasonTest) { std::string header_string{ "HTTP/1.1 200\n" "Server: nginx\r\n" "Content-Type: application/json\r\n" "\r\n"}; std::string status_line; std::string reason; Header header = util::parseHeader(header_string, &status_line, &reason); EXPECT_EQ(std::string{"HTTP/1.1 200"}, status_line); EXPECT_EQ(std::string{""}, reason); EXPECT_EQ(std::string{"nginx"}, header["Server"]); EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]); } TEST(UtilUrlEncodeTests, UnicodeEncoderTest) { std::string input = "一二三"; std::string result = util::urlEncode(input); std::string expected = "%E4%B8%80%E4%BA%8C%E4%B8%89"; EXPECT_EQ(result, expected); } TEST(UtilUrlEncodeTests, AsciiEncoderTest) { std::string input = "Hello World!"; std::string result = util::urlEncode(input); std::string expected = "Hello%20World%21"; EXPECT_EQ(result, expected); } TEST(UtilUrlDecodeTests, UnicodeDecoderTest) { std::string input = "%E4%B8%80%E4%BA%8C%E4%B8%89"; std::string result = util::urlDecode(input); std::string expected = "一二三"; EXPECT_EQ(result, expected); } TEST(UtilUrlDecodeTests, AsciiDecoderTest) { std::string input = "Hello%20World%21"; std::string result = util::urlDecode(input); std::string expected = "Hello World!"; EXPECT_EQ(result, expected); } TEST(UtilSecureStringClearTests, EmptyStringTest) { std::string input; util::secureStringClear(input); EXPECT_TRUE(input.empty()); } TEST(UtilSecureStringClearTests, NotEmptyStringTest) { std::string input = "Hello World!"; util::secureStringClear(input); EXPECT_TRUE(input.empty()); } TEST(UtilIsTrueTests, TrueTest) { { std::string input = "TRUE"; bool output = util::isTrue(input); EXPECT_TRUE(output); } { std::string input = "True"; bool output = util::isTrue(input); EXPECT_TRUE(output); } { std::string input = "true"; bool output = util::isTrue(input); EXPECT_TRUE(output); } } TEST(UtilIsTrueTests, FalseTest) { { std::string input = "FALSE"; bool output = util::isTrue(input); EXPECT_FALSE(output); } { std::string input = "False"; bool output = util::isTrue(input); EXPECT_FALSE(output); } { std::string input = "false"; bool output = util::isTrue(input); EXPECT_FALSE(output); } } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } cpr-1.11.2/test/version_tests.cpp000066400000000000000000000026311475657722200167750ustar00rootroot00000000000000#include "cpr/cpr.h" #include #include #include #include TEST(VersionTests, StringVersionExists) { #ifndef CPR_VERSION EXPECT_TRUE(false); #endif // CPR_VERSION } TEST(VersionTests, StringVersionValid) { EXPECT_TRUE(CPR_VERSION != nullptr); std::string version = CPR_VERSION; // Check if the version string is: '\d+\.\d+\.\d+' bool digit = true; size_t dotCount = 0; for (size_t i = 0; i < version.size(); i++) { if (i == 0) { EXPECT_TRUE(std::isdigit(version[i])); } else if (digit) { if (version[i] == '.') { digit = false; dotCount++; continue; } } EXPECT_TRUE(std::isdigit(version[i])); digit = true; } EXPECT_EQ(dotCount, 2); } TEST(VersionTests, VersionMajorExists) { #ifndef CPR_VERSION_MAJOR EXPECT_TRUE(false); #endif // CPR_VERSION_MAJOR } TEST(VersionTests, VersionMinorExists) { #ifndef CPR_VERSION_MINOR EXPECT_TRUE(false); #endif // CPR_VERSION_MINOR } TEST(VersionTests, VersionPatchExists) { #ifndef CPR_VERSION_PATCH EXPECT_TRUE(false); #endif // CPR_VERSION_PATCH } TEST(VersionTests, VersionNumExists) { #ifndef CPR_VERSION_NUM EXPECT_TRUE(false); #endif // CPR_VERSION_NUM } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }