Repository: libcpr/cpr
Branch: master
Commit: 8573f1d76657
Files: 197
Total size: 845.4 KB
Directory structure:
gitextract_di3av8n9/
├── .clang-format
├── .clang-tidy
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.yml
│ │ └── feature-request.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── build-deb.yml
│ ├── build-nuget.yml
│ ├── ci.yml
│ ├── clang-format.yml
│ ├── clang-tidy.yml
│ ├── cppcheck.yml
│ └── readme-updater.yml
├── .gitignore
├── .vscode/
│ └── tasks.json
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cmake/
│ ├── FindMbedTLS.cmake
│ ├── clang-tidy.cmake
│ ├── clear_variable.cmake
│ ├── code_coverage.cmake
│ ├── cppcheck.cmake
│ ├── cprConfig-ssl.cmake.in
│ ├── cprConfig.cmake.in
│ ├── cprver.h.in
│ ├── libpsl.cmake
│ ├── mongoose.CMakeLists.txt
│ ├── sanitizer.cmake
│ ├── std_fs_support_test.cpp
│ └── zlib_external.cmake
├── cppcheck-suppressions.xml
├── cpr/
│ ├── CMakeLists.txt
│ ├── accept_encoding.cpp
│ ├── async.cpp
│ ├── auth.cpp
│ ├── callback.cpp
│ ├── cert_info.cpp
│ ├── connection_pool.cpp
│ ├── cookies.cpp
│ ├── cprtypes.cpp
│ ├── curl_container.cpp
│ ├── curlholder.cpp
│ ├── curlmultiholder.cpp
│ ├── error.cpp
│ ├── file.cpp
│ ├── interceptor.cpp
│ ├── multipart.cpp
│ ├── multiperform.cpp
│ ├── parameters.cpp
│ ├── payload.cpp
│ ├── proxies.cpp
│ ├── proxyauth.cpp
│ ├── redirect.cpp
│ ├── response.cpp
│ ├── session.cpp
│ ├── sse.cpp
│ ├── ssl_ctx.cpp
│ ├── threadpool.cpp
│ ├── timeout.cpp
│ ├── unix_socket.cpp
│ └── util.cpp
├── cpr-config.cmake
├── include/
│ ├── CMakeLists.txt
│ └── cpr/
│ ├── accept_encoding.h
│ ├── api.h
│ ├── async.h
│ ├── async_wrapper.h
│ ├── auth.h
│ ├── bearer.h
│ ├── body.h
│ ├── body_view.h
│ ├── buffer.h
│ ├── callback.h
│ ├── cert_info.h
│ ├── connect_timeout.h
│ ├── connection_pool.h
│ ├── cookies.h
│ ├── cpr.h
│ ├── cprtypes.h
│ ├── curl_container.h
│ ├── curlholder.h
│ ├── curlmultiholder.h
│ ├── error.h
│ ├── file.h
│ ├── filesystem.h
│ ├── http_version.h
│ ├── interceptor.h
│ ├── interface.h
│ ├── limit_rate.h
│ ├── local_port.h
│ ├── local_port_range.h
│ ├── low_speed.h
│ ├── multipart.h
│ ├── multiperform.h
│ ├── parameters.h
│ ├── payload.h
│ ├── proxies.h
│ ├── proxyauth.h
│ ├── range.h
│ ├── redirect.h
│ ├── reserve_size.h
│ ├── resolve.h
│ ├── response.h
│ ├── secure_string.h
│ ├── session.h
│ ├── singleton.h
│ ├── sse.h
│ ├── ssl_ctx.h
│ ├── ssl_options.h
│ ├── status_codes.h
│ ├── threadpool.h
│ ├── timeout.h
│ ├── unix_socket.h
│ ├── user_agent.h
│ ├── util.h
│ └── verbose.h
├── nuget/
│ ├── build/
│ │ └── native/
│ │ ├── libcpr.props
│ │ └── libcpr.targets
│ └── libcpr.nuspec
├── package-build/
│ ├── build-package.sh
│ └── debian-libcpr/
│ ├── README.Debian
│ ├── changelog
│ ├── control
│ ├── copyright
│ ├── libcpr-dev.install
│ ├── libcpr1.install
│ ├── rules
│ └── source/
│ └── format
├── scripts/
│ ├── check_clang_format.sh
│ ├── delete_build_dir.sh
│ └── run_clang_format.sh
└── test/
├── CMakeLists.txt
├── LICENSE
├── abstractServer.cpp
├── abstractServer.hpp
├── alternating_tests.cpp
├── async_tests.cpp
├── callback_tests.cpp
├── connection_pool_tests.cpp
├── curlholder_tests.cpp
├── data/
│ ├── certificates/
│ │ ├── ca-bundle.crt
│ │ ├── client.crt
│ │ ├── root-ca.crt
│ │ ├── server.crt
│ │ └── sub-ca.crt
│ ├── client.cnf
│ ├── generate-certificates.sh
│ ├── keys/
│ │ ├── client.key
│ │ ├── root-ca.key
│ │ ├── server.key
│ │ ├── server.pub
│ │ └── sub-ca.key
│ ├── root-ca.cnf
│ ├── server.cnf
│ ├── sub-ca.cnf
│ ├── test_file.txt
│ ├── test_file_hello_äüöp_2585.txt
│ └── test_file_hello_äüöp_2585_你好.txt
├── delete_tests.cpp
├── download_tests.cpp
├── encoded_auth_tests.cpp
├── error_tests.cpp
├── file_upload_tests.cpp
├── get_tests.cpp
├── head_tests.cpp
├── httpServer.cpp
├── httpServer.hpp
├── httpsServer.cpp
├── httpsServer.hpp
├── interceptor_multi_tests.cpp
├── interceptor_tests.cpp
├── multiasync_tests.cpp
├── multiasync_tests.hpp
├── multiperform_tests.cpp
├── options_tests.cpp
├── patch_tests.cpp
├── post_tests.cpp
├── prepare_tests.cpp
├── proxy_auth_tests.cpp
├── proxy_tests.cpp
├── put_tests.cpp
├── raw_body_tests.cpp
├── resolve_tests.cpp
├── session_tests.cpp
├── singleton_tests.cpp
├── singleton_tests.hpp
├── sse_tests.cpp
├── ssl_tests.cpp
├── structures_tests.cpp
├── testUtils.cpp
├── testUtils.hpp
├── testUtils_tests.cpp
├── threadpool_tests.cpp
├── util_tests.cpp
└── version_tests.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .clang-format
================================================
---
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
...
================================================
FILE: .clang-tidy
================================================
---
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,
-abseil-string-find-str-contains,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-cppcoreguidelines-avoid-do-while,
-llvmlibc-inline-function-decl,
-altera-struct-pack-align,
-boost-use-ranges,
-cppcoreguidelines-special-member-functions,
-hicpp-special-member-functions,
-misc-header-include-cycle,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-google-explicit-constructor,
-hicpp-explicit-conversions
'
WarningsAsErrors: '*'
HeaderFilterRegex: 'include\/cpr\/.*\.h(pp)?'
FormatStyle: file
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
name: "🐛 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
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: "✨ 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.
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
================================================
FILE: .github/workflows/build-deb.yml
================================================
name: 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 version from the main cpr CMake project will be used.'
type: string
jobs:
package-ubuntu-latest-amd64:
runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: actions/checkout@v6
# 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 libpsl-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 build
pushd build
cmake .. -DCPR_BUILD_VERSION_OUTPUT_ONLY=ON -DCPR_USE_SYSTEM_LIB_PSL=ON -DCPR_USE_SYSTEM_CURL=ON
echo "RELEASE_VERSION=$(cat version.txt)" >> $GITHUB_ENV
popd
rm -rf 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 package-build/build-package.sh $(pwd)
- name: "Upload deb-packages"
uses: actions/upload-artifact@v6
with:
name: artifact-deb
path: ./*.deb
================================================
FILE: .github/workflows/build-nuget.yml
================================================
name: 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-nuget:
runs-on: windows-2022
# Use PowerShell everywhere unless a step overrides it.
defaults:
run:
shell: pwsh
steps:
# ────────────────────────────── Version handling ─────────────────────────────
- name: Set version from manual input
if: ${{ github.event_name == 'workflow_dispatch' && inputs.version != '' }}
run: |
"RELEASE_VERSION=${{ inputs.version }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Set version from Git ref
if: ${{ env.RELEASE_VERSION == '' }}
run: |
$ref = "${{ github.ref }}".Split('/')[-1]
"RELEASE_VERSION=$ref" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Print version
run: Write-Host "NuGet package version will be '$env:RELEASE_VERSION'."
# ───────────────────────────── Repository & tools ────────────────────────────
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- name: Install meson
run: pip install meson
# Prepare output folder paths
- name: Prepare NuGet output layout
shell: bash
run: |
set -eux
mkdir -p "$GITHUB_WORKSPACE/nuget/build/native/{x86,x64}/{Debug,Release}"
cp README.md "$GITHUB_WORKSPACE/nuget"
# ─────────────────────────────── Build x86 ─────────────────────────────────
- name: Enable MSVC x86 toolchain
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: Configure & build Release x86
run: |
cmake -S . -B build-release-x86 -G "Visual Studio 17 2022" -A Win32 -DCMAKE_INSTALL_PREFIX="$env:GITHUB_WORKSPACE/nuget/build/native/x86/Release" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build-release-x86 --config Release --target install
- name: Configure & build Debug x86
run: |
cmake -S . -B build-debug-x86 -G "Visual Studio 17 2022" -A Win32 -DCMAKE_INSTALL_PREFIX="$env:GITHUB_WORKSPACE/nuget/build/native/x86/Debug" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Debug
cmake --build build-debug-x86 --config Debug --target install
# ─────────────────────────────── Build x64 ─────────────────────────────────
- name: Enable MSVC x64 toolchain
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Configure & build Release x64
run: |
cmake -S . -B build-release-x64 -G "Visual Studio 17 2022" -A x64 -DCMAKE_INSTALL_PREFIX="$env:GITHUB_WORKSPACE/nuget/build/native/x64/Release" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build-release-x64 --config Release --target install
- name: Configure & build Debug x64
run: |
cmake -S . -B build-debug-x64 -G "Visual Studio 17 2022" -A x64 -DCMAKE_INSTALL_PREFIX="$env:GITHUB_WORKSPACE/nuget/build/native/x64/Debug" -DBUILD_SHARED_LIBS=ON -DCURL_ZLIB=OFF -DCMAKE_BUILD_TYPE=Debug
cmake --build build-debug-x64 --config Debug --target install
# ────────────────────────── Pack, push, artefact ─────────────────────────────
- name: Create NuGet package
env:
COMMIT_HASH: ${{ github.sha }}
run: nuget pack "$env:GITHUB_WORKSPACE/nuget/libcpr.nuspec" -OutputDirectory "$env:GITHUB_WORKSPACE" -Properties "VERSION=$env:RELEASE_VERSION;COMMIT_HASH=$env:COMMIT_HASH"
- name: Publish package to NuGet.org
if: ${{ !inputs.no_publish }}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: nuget push "$env:GITHUB_WORKSPACE\*.nupkg" $env:NUGET_API_KEY -Source https://api.nuget.org/v3/index.json
- name: Upload built .nupkg as workflow artefact
uses: actions/upload-artifact@v6
with:
name: artifact-nuget
path: '*.nupkg'
================================================
FILE: .github/workflows/ci.yml
================================================
name: 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:latest", "ubuntu:rolling"]
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 libpsl-dev meson libunistring-dev
env:
DEBIAN_FRONTEND: noninteractive
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v2.1
with:
cmake-version: '3.22.x'
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_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:latest", "ubuntu:rolling"]
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 libpsl-dev meson libunistring-dev
env:
DEBIAN_FRONTEND: noninteractive
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v2.1
with:
cmake-version: '3.22.x'
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_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 libpsl-dev meson libunistring-dev
env:
DEBIAN_FRONTEND: noninteractive
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v2.1
with:
cmake-version: '3.22.x'
- name: Checkout
uses: actions/checkout@v6
- 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:
runs-on: ubuntu-latest
container: "fedora:latest"
steps:
- name: Update package list
run: dnf update -y
- name: Install Dependencies
run: dnf install -y gcc g++ clang git make openssl-devel libcurl-devel cmake libpsl-devel libunistring-devel meson
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_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:
systemCurl: [ON, OFF]
buildType: [Debug, Release]
runs-on: ubuntu-latest
container: "fedora:latest"
steps:
- name: Update package list
run: dnf update -y
- name: Install Dependencies
run: dnf install -y gcc g++ clang git make openssl-devel libcurl-devel cmake libpsl-devel libunistring-devel meson
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_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-openssl-no-psl:
runs-on: ubuntu-latest
container: "fedora:latest"
steps:
- name: Update package list
run: dnf update -y
- name: Install Dependencies
run: dnf install -y git gcc g++ make openssl-devel cmake libunistring-devel
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_USE_SYSTEM_CURL: OFF
CPR_CURL_USE_LIBPSL: OFF
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-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 g++ clang git make openssl-devel libasan libubsan liblsan libtsan cmake libpsl-devel libunistring-devel meson
- name: Checkout
uses: actions/checkout@v6
- 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:
- uses: actions/setup-python@v6
- name: Install meson
run: pip install meson
- name: Setup MSVC environment
uses: ilammy/msvc-dev-cmd@v1
- name: Checkout
uses: actions/checkout@v6
- 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:
- uses: actions/setup-python@v6
- name: Install meson
run: pip install meson
- name: Setup MSVC environment
uses: ilammy/msvc-dev-cmd@v1
- name: Install OpenSSL
run: choco install openssl -y
- name: Checkout
uses: actions/checkout@v6
- 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: Install libpsl
run: brew install libpsl
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: OFF
CPR_USE_SYSTEM_LIB_PSL: ON
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: Install libpsl
run: brew install libpsl
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: OFF
CPR_FORCE_DARWINSSL_BACKEND: ON
CPR_USE_SYSTEM_LIB_PSL: 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: Install libpsl
run: brew install libpsl
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_USE_SYSTEM_LIB_PSL: 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: Install libpsl
run: brew install libpsl
- name: Checkout
uses: actions/checkout@v6
- name: "Build & Test"
env:
CPR_BUILD_TESTS: ON
CPR_BUILD_TESTS_SSL: ON
CPR_FORCE_OPENSSL_BACKEND: ON
CPR_USE_BOOST_FILESYSTEM: ON
CPR_USE_SYSTEM_LIB_PSL: 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 }}
================================================
FILE: .github/workflows/clang-format.yml
================================================
name: "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@v6
- name: Check format
run: bash scripts/check_clang_format.sh
================================================
FILE: .github/workflows/clang-tidy.yml
================================================
name: "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 libpsl-devel meson
- name: Install clang-tidy
run: sudo dnf install -y clang-tools-extra
- name: Checkout
uses: actions/checkout@v6
- 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=ON
================================================
FILE: .github/workflows/cppcheck.yml
================================================
name: "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
container: "fedora:latest" # Use fedora for an up to date version of cppcheck
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Update package list
run: dnf update -y
- name: Install Dependencies
run: dnf install -y gcc clang git gcc gdb make openssl-devel cmake libpsl-devel cppcheck meson
- 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: false
================================================
FILE: .github/workflows/readme-updater.yml
================================================
name: 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@v6
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@v7
with:
commit_message: ${{env.COMMIT_MESSAGE}}
branch: ${{env.OUTPUT_BRANCH}}
================================================
FILE: .gitignore
================================================
# 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
# CLion
.idea/
================================================
FILE: .vscode/tasks.json
================================================
{
// 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}"
}
}
]
}
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.18)
project(cpr VERSION 1.15.0 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 behavior 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_USE_EXISTING_CURL_TARGET "Use an existing libcurl cmake target instead of having the cpr project source the dependency itself." OFF)
cpr_option(CPR_CURL_USE_LIBPSL "Since curl 8.13 curl depends on libpsl (https://everything.curl.dev/build/deps.html#libpsl). By default cpr keeps this as a secure default enabled wich in turn requires meson as build dependency. If set to OFF, psl support inside curl will be disabled." ON)
cpr_option(CPR_USE_SYSTEM_LIB_PSL "If enabled we will use the psl lib already installed on this system. Else meson is required as build dependency. Only relevant in case 'CPR_CURL_USE_LIBPSL' is set to ON." ${CPR_USE_SYSTEM_CURL})
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.
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_EXISTING_CURL_TARGET)
message(STATUS "cpr skipping management of curl dependency (CPR_USE_EXISTING_CURL_TARGET is set to ON).")
elseif(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()
# Since curl 8.13, curl depends on lib psl
set(CURL_USE_LIBPSL ${CPR_CURL_USE_LIBPSL} CACHE INTERNAL "" FORCE)
if(CPR_CURL_USE_LIBPSL AND NOT CPR_USE_SYSTEM_LIB_PSL)
include(libpsl)
endif()
FetchContent_Declare(curl URL https://github.com/curl/curl/releases/download/curl-8_13_0/curl-8.13.0.tar.xz
URL_HASH SHA256=4a093979a3c2d02de2fbc00549a32771007f2e78032c6faa5ecd2f7a9e152025) # the file hash for curl-8.13.0.tar.xz
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
SOURCE_SUBDIR "?") # Nonexistent directory to prevent FetchContent_MakeAvailable from calling add_subdirectory and duplicating the mongoose target
if (NOT mongoose_POPULATED)
FetchContent_MakeAvailable(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()
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# 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.
================================================
FILE: CONTRIBUTING.md
================================================
# 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).
================================================
FILE: LICENSE
================================================
This 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.
================================================
FILE: README.md
================================================
# C++ Requests: Curl for People
[](https://docs.libcpr.dev/)

[](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.14.x | `cpp17` | ![alt text][supported] | |
| 1.10.x - 1.13.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
[](https://docs.libcpr.dev/)
You can find the latest documentation [here](https://docs.libcpr.dev/). It's a work in progress, but it should give you a better idea of how to use the library than the [tests](https://github.com/libcpr/cpr/tree/master/test) currently do.
## 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
* Server Sent Events (SSE) handling
## 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 f091b2c061b307ee89b164c39976fc9202a1c79d.12.0) # 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
`cpr` can be added as an extension by adding the following lines to your bazel MODULE file (tested with Bazel 8). Edit the versions as needed.
```starlark
bazel_dep(name = "curl", version = "8.8.0.bcr.3")
git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "cpr",
build_file = "//path/to/build:cpr.BUILD",
commit = "516cb3e5f4e38bede088f69fcf122c6089e38f00",
remote = "https://github.com/libcpr/cpr.git",
patches = ["//path/to/patch:cpr.PATCH"]
)
```
```starlark
// cpr.BUILD
cc_library(
name = "cpr",
hdrs = glob(["include/**/*.h"]),
includes = ["include"],
visibility = ["//visibility:public"],
srcs = glob(["cpr/**/*.cpp"]),
deps = [
"@curl//:curl"
],
)
```
```starlark
// Remove this line: cpr.PATCH
--- include/cpr/cpr.h
+++ include/cpr/cpr.h
@@ -10,7 +10,6 @@
#include "cpr/connection_pool.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"
```
### 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.71.0`. Lower versions are not supported. This means you need Debian `>= 11` or Ubuntu `>= 22.04 LTS`.
* [`The Meson Build System`](https://mesonbuild.com/) is required build PSL from source ([PSL support for curl](https://everything.curl.dev/build/deps.html#libpsl)). For more information take a look at the `CPR_CURL_USE_LIBPSL` and `CPR_USE_SYSTEM_LIB_PSL` CMake options.
## 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.
================================================
FILE: cmake/FindMbedTLS.cmake
================================================
# 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)
================================================
FILE: cmake/clang-tidy.cmake
================================================
# Include this file if and only if you want to use clang-tidy linter.
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if (NOT ${CPR_LINTER_PATH} STREQUAL "")
get_filename_component(CLANG_TIDY_HINT_FILENAME ${CPR_LINTER_PATH} NAME)
get_filename_component(CLANG_TIDY_HINT_PATH ${CPR_LINTER_PATH} DIRECTORY)
endif ()
find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy ${CLANG_TIDY_HINT_FILENAME} HINTS ${CLANG_TIDY_HINT_PATH} REQUIRED)
mark_as_advanced(CLANG_TIDY_EXECUTABLE)
message(STATUS "Enabling clang-tidy: ${CLANG_TIDY_EXECUTABLE}")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*")
else ()
message(FATAL_ERROR "Clang-tidy is not supported when building for windows")
endif ()
================================================
FILE: cmake/clear_variable.cmake
================================================
macro(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()
================================================
FILE: cmake/code_coverage.cmake
================================================
# 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()
================================================
FILE: cmake/cppcheck.cmake
================================================
# Include this file if and only if you want to use cppcheck.
if (NOT ${CPR_CPPCHECK_PATH} STREQUAL "")
get_filename_component(CPPCHECK_HINT_FILENAME ${CPR_CPPCHECK_PATH} NAME)
get_filename_component(CPPCHECK_HINT_PATH ${CPR_CPPCHECK_PATH} DIRECTORY)
endif ()
find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck ${CPPCHECK_HINT_FILENAME} HINTS ${CPPCHECK_HINT_PATH} REQUIRED)
message(STATUS "Found cppcheck: ${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"
)
================================================
FILE: cmake/cprConfig-ssl.cmake.in
================================================
include(CMakeFindDependencyMacro)
@PACKAGE_INIT@
find_dependency(CURL REQUIRED)
find_dependency(OpenSSL REQUIRED)
include(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)
check_required_components(cpr)
================================================
FILE: cmake/cprConfig.cmake.in
================================================
include(CMakeFindDependencyMacro)
@PACKAGE_INIT@
find_dependency(CURL REQUIRED)
include(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)
check_required_components(cpr)
================================================
FILE: cmake/cprver.h.in
================================================
#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
================================================
FILE: cmake/libpsl.cmake
================================================
# Builds libpsl which is especially necessary on Windows since there it is not available via e.g. a package manager.
include(ExternalProject)
find_program(MESON_PATH meson)
if(MESON_PATH STREQUAL "MESON_PATH-NOTFOUND")
message(FATAL_ERROR "meson not found. Please make sure you have meson installed on your system (https://mesonbuild.com/Getting-meson.html). Meson is required for building libpsl for curl on Windows.")
return()
endif()
FetchContent_Declare(libpsl_src GIT_REPOSITORY https://github.com/rockdaboot/libpsl.git
GIT_TAG 0.21.5)
FetchContent_MakeAvailable(libpsl_src) # sets libpsl_src_SOURCE_DIR / _BINARY_DIR
set(LIBPSL_SOURCE_DIR "${libpsl_src_SOURCE_DIR}")
set(LIBPSL_BUILD_DIR "${libpsl_src_BINARY_DIR}")
set(LIBPSL_INSTALL_DIR "${CMAKE_BINARY_DIR}/libpsl_src-install")
file(MAKE_DIRECTORY "${LIBPSL_BUILD_DIR}")
string(TOLOWER "${CMAKE_SYSTEM_NAME}" MESON_TARGET_HOST_SYSTEM_NAME)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" MESON_TARGET_SYSTEM_PROCESSOR_LOWER)
if(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES "^(x86_64|amd64)$")
set(MESON_TARGET_HOST_CPU_FAMILY "x86_64")
elseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES "^(i.86|x86)$")
set(MESON_TARGET_HOST_CPU_FAMILY "x86")
elseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES "^(armv7|armv6|arm)$")
set(MESON_TARGET_HOST_CPU_FAMILY "arm")
elseif(MESON_TARGET_SYSTEM_PROCESSOR_LOWER MATCHES "^(aarch64|arm64)$")
set(MESON_TARGET_HOST_CPU_FAMILY "aarch64")
else()
set(MESON_TARGET_HOST_CPU_FAMILY "${MESON_TARGET_SYSTEM_PROCESSOR_LOWER}")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(MESON_BUILD_TYPE debug)
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(MESON_BUILD_TYPE debugoptimized)
else()
set(MESON_BUILD_TYPE release)
endif()
include (TestBigEndian)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
if(IS_BIG_ENDIAN)
set(MESON_ENDIAN "big")
else()
set(MESON_ENDIAN "little")
endif()
# libpsl is plain C. Make sure CMake initializes a C tool-chain.
if(NOT CMAKE_C_COMPILER)
enable_language(C) # initializes CMAKE_C_COMPILER, CMAKE_AR, …
endif()
# Write a meson cross compilation file to allow cross compiling
# for example for building NuGet packages although usually it is not required.
file(WRITE "${CMAKE_BINARY_DIR}/libpsl-meson-cross.txt" "[binaries]
c = '${CMAKE_C_COMPILER}'
cpp = '${CMAKE_CXX_COMPILER}'
ar = '${CMAKE_AR}'
strip = '${CMAKE_STRIP}'
[host_machine]
system = '${MESON_TARGET_HOST_SYSTEM_NAME}'
cpu_family = '${MESON_TARGET_HOST_CPU_FAMILY}'
cpu = '${MESON_TARGET_HOST_CPU_FAMILY}'
endian = '${MESON_ENDIAN}'
")
# Meson configure
# We only care about static libraries of psl. In case you need a dynamic version, feel free to add support for it.
message(STATUS "Configuring libpsl...")
execute_process(COMMAND "${MESON_PATH}" setup
"${LIBPSL_BUILD_DIR}"
"${LIBPSL_SOURCE_DIR}"
-Dtests=false
-Ddocs=false
--cross-file "${CMAKE_BINARY_DIR}/libpsl-meson-cross.txt"
--buildtype=${MESON_BUILD_TYPE}
--prefix "${LIBPSL_INSTALL_DIR}"
--default-library=static
RESULT_VARIABLE MESON_SETUP_RC)
if(MESON_SETUP_RC)
message(FATAL_ERROR "Meson setup for libpsl failed!")
endif()
# Meson build
message(STATUS "Building libpsl...")
execute_process(COMMAND "${MESON_PATH}" compile -C "${LIBPSL_BUILD_DIR}"
RESULT_VARIABLE MESON_COMPILE_RC
)
if(MESON_COMPILE_RC)
message(FATAL_ERROR "Meson compile for libpsl failed!")
endif()
# Meson install
message(STATUS "Installing libpsl...")
execute_process(COMMAND "${MESON_PATH}" install -C "${LIBPSL_BUILD_DIR}"
RESULT_VARIABLE MESON_INSTALL_RC)
if(MESON_INSTALL_RC)
message(FATAL_ERROR "Meson install for libpsl failed!")
endif()
list(APPEND CMAKE_INCLUDE_PATH "${LIBPSL_INSTALL_DIR}/include")
if(EXISTS "${LIBPSL_INSTALL_DIR}/lib64")
set(LIBPSL_LIBRARY "${LIBPSL_INSTALL_DIR}/lib/libpsl.a")
list(APPEND CMAKE_LIBRARY_PATH "${LIBPSL_INSTALL_DIR}/lib64")
else()
set(LIBPSL_LIBRARY "${LIBPSL_INSTALL_DIR}/lib/libpsl.a")
list(APPEND CMAKE_LIBRARY_PATH "${LIBPSL_INSTALL_DIR}/lib")
endif()
set(LIBPSL_INCLUDE_DIR "${LIBPSL_INSTALL_DIR}/include")
# Workaround for Windows compilation.
# Ref: https://github.com/microsoft/vcpkg/pull/38847/files#diff-922fe829582a7e5acf5b0c35181daa63064fc12a2c889c5d89a19e5e02113f1bL44
add_compile_definitions(PSL_STATIC=1)
================================================
FILE: cmake/mongoose.CMakeLists.txt
================================================
cmake_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()
================================================
FILE: cmake/sanitizer.cmake
================================================
include(CheckCXXSourceCompiles)
set(ALL_ACTIVE_SAN_FLAGS "")
# No sanitizers when cross compiling to prevent stuff like this: https://github.com/whoshuu/cpr/issues/582
if(NOT CMAKE_CROSSCOMPILING)
function(cpr_check_sanitizer_compile flags result_var)
set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${flags}")
check_cxx_source_compiles("int main() { return 0; }" ${result_var})
set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG})
endfunction()
# Thread sanitizer
set(THREAD_SAN_FLAGS "-fsanitize=thread")
if(CPR_DEBUG_SANITIZER_FLAG_THREAD)
cpr_check_sanitizer_compile("${THREAD_SAN_FLAGS}" THREAD_SANITIZER_AVAILABLE_AND_ENABLED)
if(NOT THREAD_SANITIZER_AVAILABLE_AND_ENABLED)
message(FATAL_ERROR "ThreadSanitizer requested but the test program failed to compile with ${THREAD_SAN_FLAGS}.")
endif()
endif()
# Address sanitizer
set(ADDR_SAN_FLAGS "-fsanitize=address")
if(CPR_DEBUG_SANITIZER_FLAG_ADDR)
cpr_check_sanitizer_compile("${ADDR_SAN_FLAGS}" ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)
if(NOT ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)
message(FATAL_ERROR "AddressSanitizer requested but the test program failed to compile with ${ADDR_SAN_FLAGS}.")
endif()
endif()
# Leak sanitizer
set(LEAK_SAN_FLAGS "-fsanitize=leak")
if(CPR_DEBUG_SANITIZER_FLAG_LEAK)
cpr_check_sanitizer_compile("${LEAK_SAN_FLAGS}" LEAK_SANITIZER_AVAILABLE_AND_ENABLED)
if(NOT LEAK_SANITIZER_AVAILABLE_AND_ENABLED)
message(FATAL_ERROR "LeakSanitizer requested but the test program failed to compile with ${LEAK_SAN_FLAGS}.")
endif()
endif()
# Undefined behavior sanitizer
set(UDEF_SAN_FLAGS "-fsanitize=undefined")
if(CPR_DEBUG_SANITIZER_FLAG_UB)
cpr_check_sanitizer_compile("${UDEF_SAN_FLAGS}" UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)
if(NOT UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)
message(FATAL_ERROR "UndefinedBehaviorSanitizer requested but the test program failed to compile with ${UDEF_SAN_FLAGS}.")
endif()
endif()
# All sanitizer (without thread sanitizer)
if(CPR_DEBUG_SANITIZER_FLAG_ALL)
cpr_check_sanitizer_compile("${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}" ALL_SANITIZERS_AVAILABLE_AND_ENABLED)
if(NOT ALL_SANITIZERS_AVAILABLE_AND_ENABLED)
message(FATAL_ERROR "All sanitizers requested but the test program failed to compile with ${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}.")
endif()
set(ALL_ACTIVE_SAN_FLAGS "${ADDR_SAN_FLAGS} ${UDEF_SAN_FLAGS} ${LEAK_SAN_FLAGS}")
endif()
if(THREAD_SANITIZER_AVAILABLE_AND_ENABLED)
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(ALL_SANITIZERS_AVAILABLE_AND_ENABLED)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ALL_ACTIVE_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during most possible sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ALL_ACTIVE_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during most possible sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during most possible sanitizer builds" FORCE)
elseif(ADDRESS_SANITIZER_AVAILABLE_AND_ENABLED)
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(LEAK_SANITIZER_AVAILABLE_AND_ENABLED)
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(UNDEFINED_BEHAVIOR_SANITIZER_AVAILABLE_AND_ENABLED)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during undefined behavior sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during undefined behavior sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during undefined behavior sanitizer builds" FORCE)
endif()
endif()
================================================
FILE: cmake/std_fs_support_test.cpp
================================================
#if __has_include()
#include
namespace fs = std::filesystem;
#else
#include
namespace fs = std::experimental::filesystem;
#endif
int main() {
auto cwd = fs::current_path();
}
================================================
FILE: cmake/zlib_external.cmake
================================================
# 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()
================================================
FILE: cppcheck-suppressions.xml
================================================
*
*/build/*
CheckLevelMaxBranches
noExplicitConstructor
knownConditionTrueFalse
*/include/cpr/async_wrapper.h
y2038-unsafe-call
*/cpr/cookies.cpp
y2038-unsafe-call
*/include/cpr/low_speed.h
y2038-unsafe-call
*/curl/curl.h
normalCheckLevelMaxBranches
constParameterPointer
*/cpr/util.cpp
postfixOperator
syntaxError
*/cpr/util.cpp
================================================
FILE: cpr/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.15)
add_library(cpr
accept_encoding.cpp
async.cpp
auth.cpp
callback.cpp
cert_info.cpp
connection_pool.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
sse.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)
if(SSL_BACKEND_USED STREQUAL "OpenSSL")
configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig-ssl.cmake.in
"${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)
else()
configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in
"${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)
endif()
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)
================================================
FILE: cpr/accept_encoding.cpp
================================================
#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
================================================
FILE: cpr/async.cpp
================================================
#include "cpr/async.h"
namespace cpr {
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
CPR_SINGLETON_IMPL(GlobalThreadPool);
} // namespace cpr
================================================
FILE: cpr/auth.cpp
================================================
#include "cpr/auth.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;
}
const char* Authentication::GetAuthString() const noexcept {
return auth_string_.c_str();
}
AuthMode Authentication::GetAuthMode() const noexcept {
return auth_mode_;
}
} // namespace cpr
================================================
FILE: cpr/callback.cpp
================================================
#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 const_operation = !(cancellation_state->load());
return user_cb ? (const_operation && (*user_cb)(dltotal, dlnow, ultotal, ulnow)) : const_operation;
}
} // namespace cpr
================================================
FILE: cpr/cert_info.cpp
================================================
#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
================================================
FILE: cpr/connection_pool.cpp
================================================
#include "cpr/connection_pool.h"
#include
#include
#include
namespace cpr {
ConnectionPool::ConnectionPool() {
CURLSH* curl_share = curl_share_init();
this->connection_mutex_ = std::make_shared();
auto lock_f = +[](CURL* /*handle*/, curl_lock_data /*data*/, curl_lock_access /*access*/, void* userptr) {
std::mutex* lock = static_cast(userptr);
lock->lock(); // cppcheck-suppress localMutex // False positive: mutex is used as callback for libcurl, not local scope
};
auto unlock_f = +[](CURL* /*handle*/, curl_lock_data /*data*/, void* userptr) {
std::mutex* lock = static_cast(userptr);
lock->unlock();
};
curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
curl_share_setopt(curl_share, CURLSHOPT_USERDATA, this->connection_mutex_.get());
curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, lock_f);
curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, unlock_f);
this->curl_sh_ = std::shared_ptr(curl_share, [](CURLSH* ptr) {
// Make sure to reset callbacks before cleanup to avoid deadlocks
curl_share_setopt(ptr, CURLSHOPT_LOCKFUNC, nullptr);
curl_share_setopt(ptr, CURLSHOPT_UNLOCKFUNC, nullptr);
curl_share_cleanup(ptr);
});
}
void ConnectionPool::SetupHandler(CURL* easy_handler) const {
curl_easy_setopt(easy_handler, CURLOPT_SHARE, this->curl_sh_.get());
}
} // namespace cpr
================================================
FILE: cpr/cookies.cpp
================================================
#include "cpr/cookies.h"
#include "cpr/curlholder.h"
#include
#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 ? std::string_view{holder.urlEncode(item.GetName())} : std::string_view{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 ? std::string_view{holder.urlEncode(item.GetValue())} : std::string_view{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
================================================
FILE: cpr/cprtypes.cpp
================================================
#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
================================================
FILE: cpr/curl_container.cpp
================================================
#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 ? std::string{holder.urlEncode(parameter.key)} : parameter.key;
if (parameter.value.empty()) {
content += escapedKey;
} else {
const std::string escapedValue = encode ? std::string{holder.urlEncode(parameter.value)} : parameter.value;
content += escapedKey + "=";
content += escapedValue;
}
}
return content;
}
template <>
const std::string CurlContainer::GetContent() const {
std::string content{};
for (const Parameter& parameter : containerList_) {
if (!content.empty()) {
content += "&";
}
if (parameter.value.empty()) {
content += parameter.key;
} else {
content += parameter.key + "=";
content += parameter.value;
}
}
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 ? std::string{holder.urlEncode(element.value)} : element.value;
content += element.key + "=" + escaped;
}
return content;
}
template <>
const std::string CurlContainer::GetContent() const {
std::string content{};
for (const cpr::Pair& element : containerList_) {
if (!content.empty()) {
content += "&";
}
content += element.key + "=" + element.value;
}
return content;
}
template class CurlContainer;
template class CurlContainer;
} // namespace cpr
================================================
FILE: cpr/curlholder.cpp
================================================
#include "cpr/curlholder.h"
#include "cpr/secure_string.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);
}
CurlHolder::CurlHolder(CurlHolder&& old) noexcept : handle(old.handle), chunk(old.chunk), resolveCurlList(old.resolveCurlList), multipart(old.multipart), error(old.error) {
// Avoid double free
old.handle = nullptr;
old.chunk = nullptr;
old.resolveCurlList = nullptr;
old.multipart = nullptr;
}
CurlHolder::~CurlHolder() {
curl_slist_free_all(chunk);
curl_slist_free_all(resolveCurlList);
curl_mime_free(multipart);
curl_easy_cleanup(handle);
}
CurlHolder& CurlHolder::operator=(CurlHolder&& old) noexcept {
// Free the previous stuff
curl_slist_free_all(chunk);
curl_slist_free_all(resolveCurlList);
curl_mime_free(multipart);
curl_easy_cleanup(handle);
// Move
handle = old.handle;
chunk = old.chunk;
resolveCurlList = old.resolveCurlList;
multipart = old.multipart;
error = old.error;
// Avoid double free
old.handle = nullptr;
old.chunk = nullptr;
old.resolveCurlList = nullptr;
old.multipart = nullptr;
return *this;
}
util::SecureString CurlHolder::urlEncode(std::string_view s) const {
assert(handle);
char* output = curl_easy_escape(handle, s.data(), static_cast(s.length()));
if (output) {
util::SecureString result = output;
curl_free(output);
return result;
}
return "";
}
util::SecureString CurlHolder::urlDecode(std::string_view s) const {
assert(handle);
char* output = curl_easy_unescape(handle, s.data(), static_cast(s.length()), nullptr);
if (output) {
util::SecureString result = output;
curl_free(output);
return result;
}
return "";
}
} // namespace cpr
================================================
FILE: cpr/curlmultiholder.cpp
================================================
#include "cpr/curlmultiholder.h"
#include
#include
namespace cpr {
CurlMultiHolder::CurlMultiHolder() : handle{curl_multi_init()} {
assert(handle);
}
CurlMultiHolder::~CurlMultiHolder() {
curl_multi_cleanup(handle);
}
} // namespace cpr
================================================
FILE: cpr/error.cpp
================================================
#include "cpr/error.h"
#include
#include
#include
namespace cpr {
// NOLINTBEGIN(bugprone-branch-clone) Fine in this case
ErrorCode Error::getErrorCodeForCurlError(std::int32_t curl_code) {
switch (curl_code) {
case CURLE_OK:
return ErrorCode::OK;
case CURLE_UNSUPPORTED_PROTOCOL:
return ErrorCode::UNSUPPORTED_PROTOCOL;
case CURLE_FAILED_INIT:
return ErrorCode::FAILED_INIT;
case CURLE_URL_MALFORMAT:
return ErrorCode::URL_MALFORMAT;
case CURLE_NOT_BUILT_IN:
return ErrorCode::NOT_BUILT_IN;
case CURLE_COULDNT_RESOLVE_PROXY:
return ErrorCode::COULDNT_RESOLVE_PROXY;
case CURLE_COULDNT_RESOLVE_HOST:
return ErrorCode::COULDNT_RESOLVE_HOST;
case CURLE_COULDNT_CONNECT:
return ErrorCode::COULDNT_CONNECT;
// Name changed in curl >= 7.51.0.
#if LIBCURL_VERSION_NUM >= 0x073300
case CURLE_WEIRD_SERVER_REPLY:
return ErrorCode::WEIRD_SERVER_REPLY;
#else
case CURLE_FTP_WEIRD_SERVER_REPLY:
return ErrorCode::WEIRD_SERVER_REPLY;
#endif
case CURLE_REMOTE_ACCESS_DENIED:
return ErrorCode::REMOTE_ACCESS_DENIED;
case CURLE_HTTP2:
return ErrorCode::HTTP2;
case CURLE_QUOTE_ERROR:
return ErrorCode::QUOTE_ERROR;
case CURLE_HTTP_RETURNED_ERROR:
return ErrorCode::HTTP_RETURNED_ERROR;
case CURLE_WRITE_ERROR:
return ErrorCode::WRITE_ERROR;
case CURLE_UPLOAD_FAILED:
return ErrorCode::UPLOAD_FAILED;
case CURLE_READ_ERROR:
return ErrorCode::READ_ERROR;
case CURLE_OUT_OF_MEMORY:
return ErrorCode::OUT_OF_MEMORY;
case CURLE_OPERATION_TIMEDOUT:
return ErrorCode::OPERATION_TIMEDOUT;
case CURLE_RANGE_ERROR:
return ErrorCode::RANGE_ERROR;
case CURLE_HTTP_POST_ERROR:
return ErrorCode::HTTP_POST_ERROR;
case CURLE_SSL_CONNECT_ERROR:
return ErrorCode::SSL_CONNECT_ERROR;
case CURLE_BAD_DOWNLOAD_RESUME:
return ErrorCode::BAD_DOWNLOAD_RESUME;
case CURLE_FILE_COULDNT_READ_FILE:
return ErrorCode::FILE_COULDNT_READ_FILE;
case CURLE_FUNCTION_NOT_FOUND:
return ErrorCode::FUNCTION_NOT_FOUND;
case CURLE_ABORTED_BY_CALLBACK:
return ErrorCode::ABORTED_BY_CALLBACK;
case CURLE_BAD_FUNCTION_ARGUMENT:
return ErrorCode::BAD_FUNCTION_ARGUMENT;
case CURLE_INTERFACE_FAILED:
return ErrorCode::INTERFACE_FAILED;
case CURLE_TOO_MANY_REDIRECTS:
return ErrorCode::TOO_MANY_REDIRECTS;
case CURLE_UNKNOWN_OPTION:
return ErrorCode::UNKNOWN_OPTION;
// Added in curl 7.78.0.
#if LIBCURL_VERSION_NUM >= 0x074E00
case CURLE_SETOPT_OPTION_SYNTAX:
return ErrorCode::SETOPT_OPTION_SYNTAX;
#endif
case CURLE_GOT_NOTHING:
return ErrorCode::GOT_NOTHING;
case CURLE_SSL_ENGINE_NOTFOUND:
return ErrorCode::SSL_ENGINE_NOTFOUND;
case CURLE_SSL_ENGINE_SETFAILED:
return ErrorCode::SSL_ENGINE_SETFAILED;
case CURLE_SEND_ERROR:
return ErrorCode::SEND_ERROR;
case CURLE_RECV_ERROR:
return ErrorCode::RECV_ERROR;
case CURLE_SSL_CERTPROBLEM:
return ErrorCode::SSL_CERTPROBLEM;
case CURLE_SSL_CIPHER:
return ErrorCode::SSL_CIPHER;
case CURLE_PEER_FAILED_VERIFICATION:
return ErrorCode::PEER_FAILED_VERIFICATION;
case CURLE_BAD_CONTENT_ENCODING:
return ErrorCode::BAD_CONTENT_ENCODING;
case CURLE_FILESIZE_EXCEEDED:
return ErrorCode::FILESIZE_EXCEEDED;
case CURLE_USE_SSL_FAILED:
return ErrorCode::USE_SSL_FAILED;
case CURLE_SEND_FAIL_REWIND:
return ErrorCode::SEND_FAIL_REWIND;
case CURLE_SSL_ENGINE_INITFAILED:
return ErrorCode::SSL_ENGINE_INITFAILED;
// Added in curl 7.13.1.
#if LIBCURL_VERSION_NUM >= 0x070D01
case CURLE_LOGIN_DENIED:
return ErrorCode::LOGIN_DENIED;
#endif
// Added in curl 7.16.0.
#if LIBCURL_VERSION_NUM >= 0x071000
case CURLE_SSL_CACERT_BADFILE:
return ErrorCode::SSL_CACERT_BADFILE;
#endif
// Added in curl 7.16.1.
#if LIBCURL_VERSION_NUM >= 0x071001
case CURLE_SSL_SHUTDOWN_FAILED:
return ErrorCode::SSL_SHUTDOWN_FAILED;
#endif
// Added in curl 7.18.2.
#if LIBCURL_VERSION_NUM >= 0x071202
case CURLE_AGAIN:
return ErrorCode::AGAIN;
#endif
// Added in curl 7.19.0.
#if LIBCURL_VERSION_NUM >= 0x071300
case CURLE_SSL_CRL_BADFILE:
return ErrorCode::SSL_CRL_BADFILE;
case CURLE_SSL_ISSUER_ERROR:
return ErrorCode::SSL_ISSUER_ERROR;
#endif
// Added in curl 7.21.0.
#if LIBCURL_VERSION_NUM >= 0x071500
case CURLE_CHUNK_FAILED:
return ErrorCode::CHUNK_FAILED;
#endif
// Added in curl 7.30.0.
#if LIBCURL_VERSION_NUM >= 0x071E00
case CURLE_NO_CONNECTION_AVAILABLE:
return ErrorCode::NO_CONNECTION_AVAILABLE;
#endif
// Added in curl 7.39.0.
#if LIBCURL_VERSION_NUM >= 0x072700
case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
return ErrorCode::SSL_PINNEDPUBKEYNOTMATCH;
#endif
// Added in curl 7.41.0.
#if LIBCURL_VERSION_NUM >= 0x072900
case CURLE_SSL_INVALIDCERTSTATUS:
return ErrorCode::SSL_INVALIDCERTSTATUS;
#endif
// Added in curl 7.49.0.
#if LIBCURL_VERSION_NUM >= 0x073100
case CURLE_HTTP2_STREAM:
return ErrorCode::HTTP2_STREAM;
#endif
case CURLE_PARTIAL_FILE:
return ErrorCode::PARTIAL_FILE;
// Added in curl 7.59.0.
#if LIBCURL_VERSION_NUM >= 0x073B00
case CURLE_RECURSIVE_API_CALL:
return ErrorCode::RECURSIVE_API_CALL;
#endif
// Added in curl 7.66.0.
#if LIBCURL_VERSION_NUM >= 0x074200
case CURLE_AUTH_ERROR:
return ErrorCode::AUTH_ERROR;
#endif
// Added in curl 7.68.0.
#if LIBCURL_VERSION_NUM >= 0x074400
case CURLE_HTTP3:
return ErrorCode::HTTP3;
#endif
// Added in curl 7.69.0.
#if LIBCURL_VERSION_NUM >= 0x074500
case CURLE_QUIC_CONNECT_ERROR:
return ErrorCode::QUIC_CONNECT_ERROR;
#endif
// Added in curl 7.73.0.
#if LIBCURL_VERSION_NUM >= 0x074900
case CURLE_PROXY:
return ErrorCode::PROXY;
#endif
// Added in curl 7.77.0.
#if LIBCURL_VERSION_NUM >= 0x074D00
case CURLE_SSL_CLIENTCERT:
return ErrorCode::SSL_CLIENTCERT;
#endif
// Added in curl 7.84.0.
#if LIBCURL_VERSION_NUM >= 0x075400
case CURLE_UNRECOVERABLE_POLL:
return ErrorCode::UNRECOVERABLE_POLL;
#endif
// Added in curl 7.6.0.
#if LIBCURL_VERSION_NUM >= 0x080600
case CURLE_TOO_LARGE:
return ErrorCode::TOO_LARGE;
#endif
default:
return ErrorCode::UNKNOWN_ERROR;
}
}
// NOLINTEND(bugprone-branch-clone)
} // namespace cpr
================================================
FILE: cpr/file.cpp
================================================
#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
================================================
FILE: cpr/interceptor.cpp
================================================
#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
================================================
FILE: cpr/multipart.cpp
================================================
#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
================================================
FILE: cpr/multiperform.cpp
================================================
#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(MultiPerform&& old) noexcept {
*this = std::move(old);
}
MultiPerform& MultiPerform::operator=(MultiPerform&& old) noexcept {
sessions_ = std::move(old.sessions_);
multicurl_ = std::move(old.multicurl_);
interceptors_ = std::move(old.interceptors_);
current_interceptor_ = interceptors_.end();
first_interceptor_ = interceptors_.end();
return *this;
}
MultiPerform::~MultiPerform() {
// Unlock all sessions
for (const auto& [session, method] : sessions_) {
session->isUsedInMultiPerform = false;
// Remove easy handle from multi handle
const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle);
if (error_code) {
std::cerr << "curl_multi_remove_handle() failed, code " << static_cast(error_code) << '\n';
}
}
}
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;
}
// 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) {
if (sessions_.empty()) {
throw std::invalid_argument("Failed to find session!");
}
// 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};
for (const auto& [session, _] : sessions_) {
const CURLMcode error_code = curl_multi_add_handle(multicurl_->handle, session->curl_->handle);
if (error_code && error_code != CURLM_ADDED_ALREADY) {
std::cerr << "curl_multi_add_handle() failed, code " << static_cast(error_code) << '\n';
}
}
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);
for (const auto& [session, _] : sessions_) {
const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle);
if (error_code) {
std::cerr << "curl_multi_remove_handle() failed, code " << static_cast(error_code) << '\n';
}
}
// Sort response objects to match order of added sessions
std::vector sorted_responses;
for (const auto& [session, _] : sessions_) {
Session& current_session = *session;
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 positive
// 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 auto& [session, method] : sessions_) {
switch (method) {
case HttpMethod::GET_REQUEST:
session->PrepareGet();
break;
case HttpMethod::POST_REQUEST:
session->PreparePost();
break;
case HttpMethod::PUT_REQUEST:
session->PreparePut();
break;
case HttpMethod::DELETE_REQUEST:
session->PrepareDelete();
break;
case HttpMethod::PATCH_REQUEST:
session->PreparePatch();
break;
case HttpMethod::HEAD_REQUEST:
session->PrepareHead();
break;
case HttpMethod::OPTIONS_REQUEST:
session->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 auto& [session, method] = sessions_[sessions_index];
switch (method) {
case HttpMethod::DOWNLOAD_REQUEST:
session->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 auto& [session, method] = sessions_[sessions_index];
switch (method) {
case HttpMethod::DOWNLOAD_REQUEST:
session->PrepareDownload(file);
break;
default:
std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << '\n';
return;
}
}
void MultiPerform::SetHttpMethod(HttpMethod method) {
for (auto& [_, session_method] : sessions_) {
session_method = 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 auto& [_, method] : sessions_) {
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
================================================
FILE: cpr/parameters.cpp
================================================
// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future
#include "cpr/parameters.h"
namespace cpr {} // namespace cpr
================================================
FILE: cpr/payload.cpp
================================================
// NOLINTNEXTLINE(misc-include-cleaner) Ignored since it's for the future
#include "cpr/payload.h"
namespace cpr {} // namespace cpr
================================================
FILE: cpr/proxies.cpp
================================================
#include "cpr/proxies.h"
#include
#include