initial commit

This commit is contained in:
千住柱間 2024-04-13 21:01:22 -04:00
commit 528b3b235e
Signed by: hashirama
GPG key ID: 53E62470A86BC185
157 changed files with 19011 additions and 0 deletions

373
CMakeLists.txt Normal file
View file

@ -0,0 +1,373 @@
cmake_minimum_required(VERSION 3.15)
project(cpr VERSION 1.10.5 LANGUAGES CXX)
math(EXPR cpr_VERSION_NUM "${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL)
configure_file("${cpr_SOURCE_DIR}/cmake/cprver.h.in" "${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h")
# Only change the folder behaviour if cpr is not a subproject
if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
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()
option(BUILD_SHARED_LIBS "Build libraries as shared libraries" ON)
message(STATUS "C++ Requests CMake Options")
message(STATUS "=======================================================")
cpr_option(CPR_GENERATE_COVERAGE "Set to ON to generate coverage reports." OFF)
cpr_option(CPR_CURL_NOSIGNAL "Set to ON to disable use of signals in libcurl." OFF)
cpr_option(CURL_VERBOSE_LOGGING "Curl verbose logging during building curl" OFF)
cpr_option(CPR_USE_SYSTEM_GTEST "If ON, this project will look in the system paths for an installed gtest library. If none is found it will use the built-in one." OFF)
cpr_option(CPR_USE_SYSTEM_CURL "If enabled we will use the curl lib already installed on this system." OFF)
cpr_option(CPR_ENABLE_CURL_HTTP_ONLY "If enabled we will only use the HTTP/HTTPS protocols from CURL. If disabled, all the CURL protocols are enabled. This is useful if your project uses libcurl and you need support for other CURL features e.g. sending emails." ON)
cpr_option(CPR_ENABLE_SSL "Enables or disables the SSL backend. Required to perform HTTPS requests." ON)
cpr_option(CPR_FORCE_OPENSSL_BACKEND "Force to use the OpenSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...)." OFF)
cpr_option(CPR_FORCE_WINSSL_BACKEND "Force to use the WinSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...)." OFF)
cpr_option(CPR_FORCE_DARWINSSL_BACKEND "Force to use the DarwinSSL backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...)." OFF)
cpr_option(CPR_FORCE_MBEDTLS_BACKEND "Force to use the Mbed TLS backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...)." OFF)
cpr_option(CPR_ENABLE_LINTING "Set to ON to enable clang linting." OFF)
cpr_option(CPR_ENABLE_CPPCHECK "Set to ON to enable Cppcheck static analysis. Requires CPR_BUILD_TESTS and CPR_BUILD_TESTS_SSL to be OFF to prevent checking google tests source code." OFF)
cpr_option(CPR_BUILD_TESTS "Set to ON to build cpr tests." OFF)
cpr_option(CPR_BUILD_TESTS_SSL "Set to ON to build cpr ssl tests" ${CPR_BUILD_TESTS})
cpr_option(CPR_BUILD_TESTS_PROXY "Set to ON to build proxy tests. They fail in case there is no valid proxy server available in proxy_tests.cpp" OFF)
cpr_option(CPR_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 (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()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror")
endif()
# SSL
if(CPR_ENABLE_SSL)
if(CPR_FORCE_OPENSSL_BACKEND OR CPR_FORCE_WINSSL_BACKEND OR CPR_FORCE_DARWINSSL_BACKEND OR CPR_FORCE_MBEDTLS_BACKEND)
message(STATUS "Disabled SSL backend auto detect since either CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, or CPR_FORCE_WINSSL_BACKEND is enabled.")
set(DETECT_SSL_BACKEND OFF CACHE INTERNAL "" FORCE)
else()
message(STATUS "Automatically detecting SSL backend.")
set(DETECT_SSL_BACKEND ON CACHE INTERNAL "" FORCE)
endif()
if(CPR_FORCE_WINSSL_BACKEND AND (NOT WIN32))
message(FATAL_ERROR "WinSSL is only available on Windows! Use either OpenSSL (CPR_FORCE_OPENSSL_BACKEND) or DarwinSSL (CPR_FORCE_DARWINSSL_BACKEND) instead.")
endif()
if(DETECT_SSL_BACKEND)
message(STATUS "Detecting SSL backend...")
if(WIN32)
message(STATUS "SSL auto detect: Using WinSSL.")
set(SSL_BACKEND_USED "WinSSL")
elseif(APPLE)
message(STATUS "SSL auto detect: Using DarwinSSL.")
set(CPR_BUILD_TESTS_SSL OFF)
set(SSL_BACKEND_USED "DarwinSSL")
else()
find_package(OpenSSL)
if(OPENSSL_FOUND)
message(STATUS "SSL auto detect: Using OpenSSL.")
set(SSL_BACKEND_USED "OpenSSL")
else()
find_package(MbedTLS)
if(MBEDTLS_FOUND)
set(SSL_BACKEND_USED "MbedTLS")
else()
message(FATAL_ERROR "No valid SSL backend found! Please install OpenSSL, Mbed TLS or disable SSL by setting CPR_ENABLE_SSL to OFF.")
endif()
endif()
endif()
else()
if(CPR_FORCE_OPENSSL_BACKEND)
find_package(OpenSSL)
if(OPENSSL_FOUND)
message(STATUS "Using OpenSSL.")
set(SSL_BACKEND_USED "OpenSSL")
else()
message(FATAL_ERROR "CPR_FORCE_OPENSSL_BACKEND enabled but we were not able to find OpenSSL!")
endif()
elseif(CPR_FORCE_WINSSL_BACKEND)
message(STATUS "Using WinSSL.")
set(SSL_BACKEND_USED "WinSSL")
elseif(CPR_FORCE_DARWINSSL_BACKEND)
message(STATUS "Using DarwinSSL.")
set(CPR_BUILD_TESTS_SSL OFF)
set(SSL_BACKEND_USED "DarwinSSL")
elseif(CPR_FORCE_MBEDTLS_BACKEND)
message(STATUS "Using Mbed TLS.")
set(CPR_BUILD_TESTS_SSL OFF)
set(SSL_BACKEND_USED "MbedTLS")
endif()
endif()
endif()
if(SSL_BACKEND_USED STREQUAL "OpenSSL")
# Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly
find_package(OpenSSL REQUIRED)
add_compile_definitions(OPENSSL_BACKEND_USED)
endif()
# Curl configuration
if(CPR_USE_SYSTEM_CURL)
if(CPR_ENABLE_SSL)
find_package(CURL COMPONENTS HTTP HTTPS)
if(CURL_FOUND)
message(STATUS "Curl ${CURL_VERSION_STRING} found on this system.")
# To be able to load certificates under Windows when using OpenSSL:
if(CMAKE_USE_OPENSSL AND WIN32 AND (NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.71.0")))
message(FATAL_ERROR "Your system curl version (${CURL_VERSION_STRING}) is too old to support OpenSSL on Windows which requires curl >= 7.71.0. Update your curl version, use WinSSL, disable SSL or use the built-in version of curl.")
endif()
else()
find_package(CURL COMPONENTS HTTP)
if(CURL_FOUND)
message(FATAL_ERROR "Curl found on this system but WITHOUT HTTPS/SSL support. Either disable SSL by setting CPR_ENABLE_SSL to OFF or use the built-in version of curl by setting CPR_USE_SYSTEM_CURL to OFF.")
else()
message(FATAL_ERROR "Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.")
endif()
endif()
else()
find_package(CURL COMPONENTS HTTP)
if(CURL_FOUND)
message(STATUS "Curl found on this system.")
else()
message(FATAL_ERROR "Curl not found on this system. To use the built-in version set CPR_USE_SYSTEM_CURL to OFF.")
endif()
endif()
else()
message(STATUS "Configuring built-in curl...")
# ZLIB is optional for curl
# to disable it:
# * from command line:
# -DCURL_ZLIB=OFF
# * from CMake script:
# SET(CURL_ZLIB OFF CACHE STRING "" FORCE)
if (CURL_ZLIB OR CURL_ZLIB STREQUAL AUTO OR NOT DEFINED CACHE{CURL_ZLIB})
include(cmake/zlib_external.cmake)
endif()
if (CPR_ENABLE_CURL_HTTP_ONLY)
# We only need HTTP (and HTTPS) support:
set(HTTP_ONLY ON CACHE INTERNAL "" FORCE)
endif()
set(BUILD_CURL_EXE OFF CACHE INTERNAL "" FORCE)
set(BUILD_TESTING OFF)
if (CURL_VERBOSE_LOGGING)
message(STATUS "Enabled curl debug features")
set(ENABLE_DEBUG ON CACHE INTERNAL "" FORCE)
endif()
if (CPR_ENABLE_SSL)
set(CURL_ENABLE_SSL ON CACHE INTERNAL "" FORCE)
if(ANDROID)
set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE INTERNAL "")
elseif(CPR_SKIP_CA_BUNDLE_SEARCH)
set(CURL_CA_PATH "none" CACHE INTERNAL "")
else()
set(CURL_CA_PATH "auto" CACHE INTERNAL "")
endif()
if(CPR_SKIP_CA_BUNDLE_SEARCH)
set(CURL_CA_BUNDLE "none" CACHE INTERNAL "")
elseif(NOT DEFINED CURL_CA_BUNDLE)
set(CURL_CA_BUNDLE "auto" CACHE INTERNAL "")
endif()
if(SSL_BACKEND_USED STREQUAL "WinSSL")
set(CURL_USE_SCHANNEL ON CACHE INTERNAL "" FORCE)
set(CURL_WINDOWS_SSPI ON CACHE INTERNAL "" FORCE)
endif()
if(SSL_BACKEND_USED STREQUAL "OpenSSL")
set(CURL_USE_OPENSSL ON CACHE INTERNAL "" FORCE)
endif()
if(SSL_BACKEND_USED STREQUAL "DarwinSSL")
set(CURL_USE_SECTRANSP ON CACHE INTERNAL "" FORCE)
endif()
if(SSL_BACKEND_USED STREQUAL "MbedTLS")
set(CURL_USE_MBEDTLS ON CACHE INTERNAL "" FORCE)
endif()
message(STATUS "Enabled curl SSL")
else()
set(CURL_ENABLE_SSL OFF CACHE INTERNAL "" FORCE)
set(CURL_CA_PATH "none" CACHE INTERNAL "" FORCE)
set(CURL_USE_SCHANNEL OFF CACHE INTERNAL "" FORCE)
set(CURL_WINDOWS_SSPI OFF CACHE INTERNAL "" FORCE)
set(CURL_USE_OPENSSL OFF CACHE INTERNAL "" FORCE)
set(CURL_USE_SECTRANSP OFF CACHE INTERNAL "" FORCE)
set(CURL_USE_MBEDTLS OFF CACHE INTERNAL "" FORCE)
message(STATUS "Disabled curl SSL")
endif()
# Disable linting for curl
clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()
#FetchContent
restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
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/release-1.11.0.tar.gz
URL_HASH SHA256=b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5 # the file hash for release-1.11.0.tar.gz
USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress
FetchContent_MakeAvailable(googletest)
restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
add_library(gtest_int INTERFACE)
target_link_libraries(gtest_int INTERFACE gtest)
target_include_directories(gtest_int INTERFACE ${googletest_SOURCE_DIR}/include)
add_library(GTest::GTest ALIAS gtest_int)
# Group under the "tests/gtest" project folder in IDEs such as Visual Studio.
set_property(TARGET gtest PROPERTY FOLDER "tests/gtest")
set_property(TARGET gtest_main PROPERTY FOLDER "tests/gtest")
endif()
endif()
# Mongoose configuration
if(CPR_BUILD_TESTS)
message(STATUS "Building mongoose project for test support.")
if(CPR_BUILD_TESTS_SSL)
if(NOT CPR_ENABLE_SSL)
message(FATAL_ERROR "OpenSSL is required to build SSL test but CPR_ENABLE_SSL is disabled. Either set CPR_ENABLE_SSL to ON or disable CPR_BUILD_TESTS_SSL.")
endif()
if(NOT(SSL_BACKEND_USED STREQUAL "OpenSSL"))
message(FATAL_ERROR "OpenSSL is required for SSL test, but it seams like OpenSSL is not being used as SSL backend. Either set CPR_BUILD_TESTS_SSL to OFF or set CPR_FORCE_OPENSSL_BACKEND to ON and try again.")
endif()
set(ENABLE_SSL_TESTS ON CACHE INTERNAL "")
else()
set(ENABLE_SSL_TESTS OFF CACHE INTERNAL "")
endif()
# Disable linting for mongoose
clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
FetchContent_Declare(mongoose
URL https://github.com/cesanta/mongoose/archive/7.7.tar.gz
URL_HASH SHA256=4e5733dae31c3a81156af63ca9aa3a6b9b736547f21f23c3ab2f8e3f1ecc16c0 # the hash for 7.7.tar.gz
USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress
# We can not use FetchContent_MakeAvailable, since we need to patch mongoose to use CMake
if (NOT mongoose_POPULATED)
FetchContent_POPULATE(mongoose)
file(INSTALL cmake/mongoose.CMakeLists.txt DESTINATION ${mongoose_SOURCE_DIR})
file(RENAME ${mongoose_SOURCE_DIR}/mongoose.CMakeLists.txt ${mongoose_SOURCE_DIR}/CMakeLists.txt)
add_subdirectory(${mongoose_SOURCE_DIR} ${mongoose_BINARY_DIR})
endif()
# Group under the "external" project folder in IDEs such as Visual Studio.
set_property(TARGET mongoose PROPERTY FOLDER "external")
restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
endif()
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()

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# 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.

27
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,27 @@
# Contributing to C++ Requests
Please fork this repository and contribute back using [pull requests](https://github.com/whoshuu/cpr/pulls). Features can be requested using [issues](https://github.com/whoshuu/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/whoshuu/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:
```
./format-check.sh
```
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).

3
CppCheckSuppressions.txt Normal file
View file

@ -0,0 +1,3 @@
noExplicitConstructor
ConfigurationNotChecked
passedByValue

25
LICENSE Normal file
View file

@ -0,0 +1,25 @@
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.

175
README.md Normal file
View file

@ -0,0 +1,175 @@
My fork of libcpr that works on guix.
# C++ Requests: Curl for People <img align="right" height="40" src="http://i.imgur.com/d9Xtyts.png">
[![Documentation](https://img.shields.io/badge/docs-online-informational?style=flat&link=https://docs.libcpr.org/)](https://docs.libcpr.org/)
![CI](https://github.com/libcpr/cpr/workflows/CI/badge.svg)
[![Gitter](https://badges.gitter.im/libcpr/community.svg)](https://gitter.im/libcpr/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
## Announcements
* This project is being maintained by [Fabian Sauter](https://github.com/com8) and [Kilian Traub](https://github.com/KingKili).
* For quick help, and discussion libcpr also offer 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.10.x | `cpp17` | ![alt text][supported] | |
| 1.9.x | `cpp11` | ![alt text][supported] | Supported until 01.01.2025 |
| <= 1.8.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 anything but, and making mistakes, misusing it is a common source of error and frustration. Using the more expressive language facilities of `C++17` (or `C++11` in case you use cpr < 1.10.0), this library captures the essence of making network calls into a few concise idioms.
Here's a quick GET request:
```c++
#include <cpr/cpr.h>
int main(int argc, char** argv) {
cpr::Response r = cpr::Get(cpr::Url{"https://api.github.com/repos/whoshuu/cpr/contributors"},
cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC},
cpr::Parameters{{"anon", "true"}, {"key", "value"}});
r.status_code; // 200
r.header["content-type"]; // application/json; charset=utf-8
r.text; // JSON text string
return 0;
}
```
And here's [less functional, more complicated code, without cpr](https://gist.github.com/whoshuu/2dc858b8730079602044).
## Documentation
[![Documentation](https://img.shields.io/badge/docs-online-informational?style=for-the-badge&link=https://docs.libcpr.org/)](https://docs.libcpr.org/)
You can find the latest documentation [here](https://docs.libcpr.org/). It's a work in progress, but it should give you a better idea of how to use the library than the [tests](https://github.com/libcpr/cpr/tree/master/test) currently do.
## Features
C++ Requests currently supports:
* Custom headers
* Url encoded parameters
* Url encoded POST values
* Multipart form POST upload
* File POST upload
* Basic authentication
* Bearer authentication
* Digest authentication
* NTLM authentication
* Connection and request timeout specification
* Timeout for low speed connection
* Asynchronous requests
* :cookie: support!
* Proxy support
* Callback interfaces
* PUT methods
* DELETE methods
* HEAD methods
* OPTIONS methods
* PATCH methods
* Thread Safe access to [libCurl](https://curl.haxx.se/libcurl/c/threadsafe.html)
* OpenSSL and WinSSL support for HTTPS requests
## Planned
For a quick overview about the planed 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 0817715923c9705e68994eb52ef9df3f6845beba) # The commit hash for 1.10.x. Replace with the latest 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 .
$ 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)
```
### Bazel
Please refer to [hedronvision/bazel-make-cc-https-easy](https://github.com/hedronvision/bazel-make-cc-https-easy).
### Packages for Linux Distributions
Alternatively, you may install a package specific to your Linux distribution. Since so few distributions currently have a package for cpr, most users will not be able to run your program with this approach.
Currently, we are aware of packages for the following distributions:
* [Arch Linux (AUR)](https://aur.archlinux.org/packages/cpr)
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/)
## 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.
## 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.

173
README.md~ Normal file
View file

@ -0,0 +1,173 @@
# C++ Requests: Curl for People <img align="right" height="40" src="http://i.imgur.com/d9Xtyts.png">
[![Documentation](https://img.shields.io/badge/docs-online-informational?style=flat&link=https://docs.libcpr.org/)](https://docs.libcpr.org/)
![CI](https://github.com/libcpr/cpr/workflows/CI/badge.svg)
[![Gitter](https://badges.gitter.im/libcpr/community.svg)](https://gitter.im/libcpr/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
## Announcements
* This project is being maintained by [Fabian Sauter](https://github.com/com8) and [Kilian Traub](https://github.com/KingKili).
* For quick help, and discussion libcpr also offer 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.10.x | `cpp17` | ![alt text][supported] | |
| 1.9.x | `cpp11` | ![alt text][supported] | Supported until 01.01.2025 |
| <= 1.8.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 anything but, and making mistakes, misusing it is a common source of error and frustration. Using the more expressive language facilities of `C++17` (or `C++11` in case you use cpr < 1.10.0), this library captures the essence of making network calls into a few concise idioms.
Here's a quick GET request:
```c++
#include <cpr/cpr.h>
int main(int argc, char** argv) {
cpr::Response r = cpr::Get(cpr::Url{"https://api.github.com/repos/whoshuu/cpr/contributors"},
cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC},
cpr::Parameters{{"anon", "true"}, {"key", "value"}});
r.status_code; // 200
r.header["content-type"]; // application/json; charset=utf-8
r.text; // JSON text string
return 0;
}
```
And here's [less functional, more complicated code, without cpr](https://gist.github.com/whoshuu/2dc858b8730079602044).
## Documentation
[![Documentation](https://img.shields.io/badge/docs-online-informational?style=for-the-badge&link=https://docs.libcpr.org/)](https://docs.libcpr.org/)
You can find the latest documentation [here](https://docs.libcpr.org/). It's a work in progress, but it should give you a better idea of how to use the library than the [tests](https://github.com/libcpr/cpr/tree/master/test) currently do.
## Features
C++ Requests currently supports:
* Custom headers
* Url encoded parameters
* Url encoded POST values
* Multipart form POST upload
* File POST upload
* Basic authentication
* Bearer authentication
* Digest authentication
* NTLM authentication
* Connection and request timeout specification
* Timeout for low speed connection
* Asynchronous requests
* :cookie: support!
* Proxy support
* Callback interfaces
* PUT methods
* DELETE methods
* HEAD methods
* OPTIONS methods
* PATCH methods
* Thread Safe access to [libCurl](https://curl.haxx.se/libcurl/c/threadsafe.html)
* OpenSSL and WinSSL support for HTTPS requests
## Planned
For a quick overview about the planed 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 0817715923c9705e68994eb52ef9df3f6845beba) # The commit hash for 1.10.x. Replace with the latest 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 .
$ 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)
```
### Bazel
Please refer to [hedronvision/bazel-make-cc-https-easy](https://github.com/hedronvision/bazel-make-cc-https-easy).
### Packages for Linux Distributions
Alternatively, you may install a package specific to your Linux distribution. Since so few distributions currently have a package for cpr, most users will not be able to run your program with this approach.
Currently, we are aware of packages for the following distributions:
* [Arch Linux (AUR)](https://aur.archlinux.org/packages/cpr)
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/)
## 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.
## 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.

14
cmake/FindMbedTLS.cmake Normal file
View file

@ -0,0 +1,14 @@
# 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)

13
cmake/clang-tidy.cmake Normal file
View file

@ -0,0 +1,13 @@
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy)
mark_as_advanced(CLANG_TIDY_EXECUTABLE)
if (${CLANG_TIDY_EXECUTABLE})
message(FATAL_ERROR "Clang-tidy not found")
else()
message(STATUS "Enabling clang-tidy")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*")
endif()
else()
message(FATAL_ERROR "Clang-tidy is not supported when building for windows")
endif()

View file

@ -0,0 +1,11 @@
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()

29
cmake/code_coverage.cmake Normal file
View file

@ -0,0 +1,29 @@
# 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()

10
cmake/cppcheck.cmake Normal file
View file

@ -0,0 +1,10 @@
find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck)
if(CMAKE_CXX_CPPCHECK)
list(APPEND CMAKE_CXX_CPPCHECK
"--error-exitcode=1"
"--enable=warning,style"
"--force"
"--inline-suppr"
"--std=c++${CMAKE_CXX_STANDARD}"
"--suppressions-list=${CMAKE_SOURCE_DIR}/CppCheckSuppressions.txt")
endif()

8
cmake/cprConfig.cmake.in Normal file
View file

@ -0,0 +1,8 @@
include(CMakeFindDependencyMacro)
@PACKAGE_INIT@
find_dependency(CURL REQUIRED)
include(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)
check_required_components(cpr)

30
cmake/cprver.h.in Normal file
View file

@ -0,0 +1,30 @@
#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

View file

@ -0,0 +1,12 @@
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 PRIVATE OpenSSL::SSL)
endif()

69
cmake/sanitizer.cmake Normal file
View file

@ -0,0 +1,69 @@
include(CheckCXXCompilerFlag)
include(CheckCXXSourceRuns)
set(ALL_SAN_FLAGS "")
# No sanitizers when cross compiling to prevent stuff like this: https://github.com/whoshuu/cpr/issues/582
if(NOT CMAKE_CROSSCOMPILING)
# Thread sanitizer
set(THREAD_SAN_FLAGS "-fsanitize=thread")
set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${THREAD_SAN_FLAGS}")
check_cxx_source_runs("int main() { return 0; }" THREAD_SANITIZER_AVAILABLE)
set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG})
# Do not add the ThreadSanitizer for builds with all sanitizers enabled because it is incompatible with other sanitizers.
# Address sanitizer
set(ADDR_SAN_FLAGS "-fsanitize=address")
set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${ADDR_SAN_FLAGS}")
check_cxx_source_runs("int main() { return 0; }" ADDRESS_SANITIZER_AVAILABLE)
set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG})
if(ADDRESS_SANITIZER_AVAILABLE)
set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${ADDR_SAN_FLAGS}")
endif()
# Leak sanitizer
set(LEAK_SAN_FLAGS "-fsanitize=leak")
check_cxx_compiler_flag(${LEAK_SAN_FLAGS} LEAK_SANITIZER_AVAILABLE)
if(LEAK_SANITIZER_AVAILABLE)
set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${LEAK_SAN_FLAGS}")
endif()
# Undefined behavior sanitizer
set(UDEF_SAN_FLAGS "-fsanitize=undefined")
check_cxx_compiler_flag(${UDEF_SAN_FLAGS} UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE)
if(UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE)
set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${UDEF_SAN_FLAGS}")
endif()
# All sanitizer (without thread sanitizer)
if(NOT ALL_SAN_FLAGS STREQUAL "")
set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${ALL_SAN_FLAGS}")
check_cxx_source_runs("int main() { return 0; }" ALL_SANITIZERS_AVAILABLE)
set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG})
endif()
if(CPR_DEBUG_SANITIZER_FLAG_THREAD AND THREAD_SANITIZER_AVAILABLE)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during thread sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during thread sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during thread sanitizer builds" FORCE)
elseif(CPR_DEBUG_SANITIZER_FLAG_ADDR AND ADDRESS_SANITIZER_AVAILABLE)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during address sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during address sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during address sanitizer builds" FORCE)
elseif(CPR_DEBUG_SANITIZER_FLAG_LEAK AND LEAK_SANITIZER_AVAILABLE)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer" CACHE INTERNAL "Flags used by the C compiler during leak sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer" CACHE INTERNAL "Flags used by the C++ compiler during leak sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during leak sanitizer builds" FORCE)
elseif(CPR_DEBUG_SANITIZER_FLAG_UB AND UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during undefined behaviour sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during undefined behaviour sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during undefined behaviour sanitizer builds" FORCE)
elseif(CPR_DEBUG_SANITIZER_FLAG_ALL AND ALL_SANITIZERS_AVAILABLE)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ALL_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during most possible sanitizer builds." FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ALL_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during most possible sanitizer builds." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during most possible sanitizer builds" FORCE)
endif()
endif()

18
cmake/zlib_external.cmake Normal file
View file

@ -0,0 +1,18 @@
# ZLIB
# Fix Windows missing "zlib.dll":
if(WIN32 AND (${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}))
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/$<CONFIG> CACHE INTERNAL "" FORCE)
endif()
set(ZLIB_COMPAT ON CACHE INTERNAL "" FORCE)
set(ZLIB_ENABLE_TESTS OFF CACHE INTERNAL "" FORCE)
#FetchContent Zlib
# Fix Windows zlib dll names from "zlibd1.dll" to "zlib.dll":
if(WIN32)
set_target_properties(zlib PROPERTIES OUTPUT_NAME "zlib")
set_target_properties(zlib PROPERTIES DEBUG_POSTFIX "")
set_target_properties(zlib PROPERTIES SUFFIX ".dll")
endif()

26
cpr-config.cmake Normal file
View file

@ -0,0 +1,26 @@
# - C++ Requests, Curl for People
# This module is a libcurl wrapper written in modern C++.
# It provides an easy, intuitive, and efficient interface to
# a host of networking methods.
#
# Finding this module will define the following variables:
# CPR_FOUND - True if the core library has been found
# CPR_LIBRARIES - Path to the core library archive
# CPR_INCLUDE_DIRS - Path to the include directories. Gives access
# to cpr.h, which must be included in every
# file that uses this interface
find_path(CPR_INCLUDE_DIR
NAMES cpr.h)
find_library(CPR_LIBRARY
NAMES cpr
HINTS ${CPR_LIBRARY_ROOT})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CPR REQUIRED_VARS CPR_LIBRARY CPR_INCLUDE_DIR)
if(CPR_FOUND)
set(CPR_LIBRARIES ${CPR_LIBRARY})
set(CPR_INCLUDE_DIRS ${CPR_INCLUDE_DIR})
endif()

85
cpr/CMakeLists.txt Normal file
View file

@ -0,0 +1,85 @@
cmake_minimum_required(VERSION 3.15)
add_library(cpr
accept_encoding.cpp
async.cpp
auth.cpp
bearer.cpp
callback.cpp
cert_info.cpp
cookies.cpp
cprtypes.cpp
curl_container.cpp
curlholder.cpp
error.cpp
file.cpp
multipart.cpp
parameters.cpp
payload.cpp
proxies.cpp
proxyauth.cpp
session.cpp
threadpool.cpp
timeout.cpp
unix_socket.cpp
util.cpp
response.cpp
redirect.cpp
interceptor.cpp
ssl_ctx.cpp
curlmultiholder.cpp
multiperform.cpp)
add_library(cpr::cpr ALIAS cpr)
target_link_libraries(cpr PUBLIC -lcurl) # 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)
if(CPR_USE_SYSTEM_CURL)
install(TARGETS cpr
EXPORT cprTargets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
# Include CMake helpers for package config files
# Follow this installation guideline: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake"
VERSION ${${PROJECT_NAME}_VERSION}
COMPATIBILITY ExactVersion)
configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in
"${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)
install(EXPORT cprTargets
FILE cprTargets.cmake
NAMESPACE cpr::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)
install(FILES ${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake
${PROJECT_BINARY_DIR}/cpr/cprConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr)
else()
install(TARGETS cpr
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

37
cpr/accept_encoding.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "cpr/accept_encoding.h"
#include <algorithm>
#include <cassert>
#include <initializer_list>
#include <iterator>
#include <numeric>
#include <stdexcept>
namespace cpr {
AcceptEncoding::AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& 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<std::string>& 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

8
cpr/async.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "cpr/async.h"
namespace cpr {
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
CPR_SINGLETON_IMPL(GlobalThreadPool)
} // namespace cpr

16
cpr/auth.cpp Normal file
View file

@ -0,0 +1,16 @@
#include "cpr/auth.h"
#include "cpr/util.h"
namespace cpr {
Authentication::~Authentication() noexcept {
util::secureStringClear(auth_string_);
}
const char* Authentication::GetAuthString() const noexcept {
return auth_string_.c_str();
}
AuthMode Authentication::GetAuthMode() const noexcept {
return auth_mode_;
}
} // namespace cpr

16
cpr/bearer.cpp Normal file
View file

@ -0,0 +1,16 @@
#include "cpr/bearer.h"
#include "cpr/util.h"
namespace cpr {
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
Bearer::~Bearer() noexcept {
util::secureStringClear(token_string_);
}
const char* Bearer::GetToken() const noexcept {
return token_string_.c_str();
}
#endif
} // namespace cpr

14
cpr/callback.cpp Normal file
View file

@ -0,0 +1,14 @@
#include <cpr/callback.h>
#include <curl/curl.h>
#include <functional>
namespace cpr {
void CancellationCallback::SetProgressCallback(ProgressCallback& u_cb) {
user_cb.emplace(std::reference_wrapper{u_cb});
}
bool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const {
const bool cont_operation{!cancellation_state->load()};
return user_cb ? (cont_operation && (*user_cb)(dltotal, dlnow, ultotal, ulnow)) : cont_operation;
}
} // namespace cpr

43
cpr/cert_info.cpp Normal file
View file

@ -0,0 +1,43 @@
#include "cpr/cert_info.h"
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

106
cpr/cookies.cpp Normal file
View file

@ -0,0 +1,106 @@
#include "cpr/cookies.h"
#include <ctime>
#include <iomanip>
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_;
}
const std::chrono::system_clock::time_point Cookie::GetExpires() const {
return expires_;
}
const 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
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_;
}
const std::string Cookies::GetEncoded(const CurlHolder& holder) const {
std::stringstream stream;
for (const cpr::Cookie& item : cookies_) {
// Depending on if encoding is set to "true", we will URL-encode cookies
stream << (encode ? holder.urlEncode(item.GetName()) : item.GetName()) << "=";
// special case version 1 cookies, which can be distinguished by
// beginning and trailing quotes
if (!item.GetValue().empty() && item.GetValue().front() == '"' && item.GetValue().back() == '"') {
stream << item.GetValue();
} else {
// Depending on if encoding is set to "true", we will URL-encode cookies
stream << (encode ? holder.urlEncode(item.GetValue()) : item.GetValue());
}
stream << "; ";
}
return stream.str();
}
cpr::Cookie& Cookies::operator[](size_t pos) {
return cookies_[pos];
}
Cookies::iterator Cookies::begin() {
return cookies_.begin();
}
Cookies::iterator Cookies::end() {
return cookies_.end();
}
Cookies::const_iterator Cookies::begin() const {
return cookies_.begin();
}
Cookies::const_iterator Cookies::end() const {
return cookies_.end();
}
Cookies::const_iterator Cookies::cbegin() const {
return cookies_.cbegin();
}
Cookies::const_iterator Cookies::cend() const {
return cookies_.cend();
}
void Cookies::emplace_back(const Cookie& str) {
cookies_.emplace_back(str);
}
void Cookies::push_back(const Cookie& str) {
cookies_.push_back(str);
}
void Cookies::pop_back() {
cookies_.pop_back();
}
} // namespace cpr

10
cpr/cprtypes.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "cpr/cprtypes.h"
#include <algorithm>
#include <cctype>
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

58
cpr/curl_container.cpp Normal file
View file

@ -0,0 +1,58 @@
#include "cpr/curl_container.h"
#include <algorithm>
#include <iterator>
namespace cpr {
template <class T>
CurlContainer<T>::CurlContainer(const std::initializer_list<T>& containerList) : containerList_(containerList) {}
template <class T>
void CurlContainer<T>::Add(const std::initializer_list<T>& containerList) {
std::transform(containerList.begin(), containerList.end(), std::back_inserter(containerList_), [](const T& elem) { return std::move(elem); });
}
template <class T>
void CurlContainer<T>::Add(const T& element) {
containerList_.push_back(std::move(element));
}
template <>
const std::string CurlContainer<Parameter>::GetContent(const CurlHolder& holder) const {
std::string content{};
for (const Parameter& parameter : containerList_) {
if (!content.empty()) {
content += "&";
}
const std::string escapedKey = encode ? holder.urlEncode(parameter.key) : parameter.key;
if (parameter.value.empty()) {
content += escapedKey;
} else {
const std::string escapedValue = encode ? holder.urlEncode(parameter.value) : parameter.value;
content += escapedKey + "=";
content += escapedValue;
}
};
return content;
}
template <>
const std::string CurlContainer<Pair>::GetContent(const CurlHolder& holder) const {
std::string content{};
for (const cpr::Pair& element : containerList_) {
if (!content.empty()) {
content += "&";
}
const std::string escaped = encode ? holder.urlEncode(element.value) : element.value;
content += element.key + "=" + escaped;
}
return content;
}
template class CurlContainer<Pair>;
template class CurlContainer<Parameter>;
} // namespace cpr

49
cpr/curlholder.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "cpr/curlholder.h"
#include <cassert>
namespace cpr {
CurlHolder::CurlHolder() {
/**
* Allow multithreaded access to CPR by locking curl_easy_init().
* curl_easy_init() is not thread safe.
* References:
* https://curl.haxx.se/libcurl/c/curl_easy_init.html
* https://curl.haxx.se/libcurl/c/threadsafe.html
**/
curl_easy_init_mutex_().lock();
// NOLINTNEXTLINE (cppcoreguidelines-prefer-member-initializer) since we need it to happen inside the lock
handle = curl_easy_init();
curl_easy_init_mutex_().unlock();
assert(handle);
} // namespace cpr
CurlHolder::~CurlHolder() {
curl_slist_free_all(chunk);
curl_slist_free_all(resolveCurlList);
curl_mime_free(multipart);
curl_easy_cleanup(handle);
}
std::string CurlHolder::urlEncode(const std::string& s) const {
assert(handle);
char* output = curl_easy_escape(handle, s.c_str(), static_cast<int>(s.length()));
if (output) {
std::string result = output;
curl_free(output);
return result;
}
return "";
}
std::string CurlHolder::urlDecode(const std::string& s) const {
assert(handle);
char* output = curl_easy_unescape(handle, s.c_str(), static_cast<int>(s.length()), nullptr);
if (output) {
std::string result = output;
curl_free(output);
return result;
}
return "";
}
} // namespace cpr

15
cpr/curlmultiholder.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "cpr/curlmultiholder.h"
#include <cassert>
namespace cpr {
CurlMultiHolder::CurlMultiHolder() : handle{curl_multi_init()} {
assert(handle);
}
CurlMultiHolder::~CurlMultiHolder() {
curl_multi_cleanup(handle);
}
} // namespace cpr

68
cpr/error.cpp Normal file
View file

@ -0,0 +1,68 @@
#include "cpr/error.h"
#include <curl/curl.h>
namespace cpr {
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_URL_MALFORMAT:
return ErrorCode::INVALID_URL_FORMAT;
case CURLE_COULDNT_RESOLVE_PROXY:
return ErrorCode::PROXY_RESOLUTION_FAILURE;
case CURLE_COULDNT_RESOLVE_HOST:
return ErrorCode::HOST_RESOLUTION_FAILURE;
case CURLE_COULDNT_CONNECT:
return ErrorCode::CONNECTION_FAILURE;
case CURLE_OPERATION_TIMEDOUT:
return ErrorCode::OPERATION_TIMEDOUT;
case CURLE_SSL_CONNECT_ERROR:
return ErrorCode::SSL_CONNECT_ERROR;
#if LIBCURL_VERSION_NUM < 0x073e00
case CURLE_PEER_FAILED_VERIFICATION:
return ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR;
#endif
case CURLE_ABORTED_BY_CALLBACK:
case CURLE_WRITE_ERROR:
return ErrorCode::REQUEST_CANCELLED;
case CURLE_GOT_NOTHING:
return ErrorCode::EMPTY_RESPONSE;
case CURLE_SSL_ENGINE_NOTFOUND:
case CURLE_SSL_ENGINE_SETFAILED:
return ErrorCode::GENERIC_SSL_ERROR;
case CURLE_SEND_ERROR:
return ErrorCode::NETWORK_SEND_FAILURE;
case CURLE_RECV_ERROR:
return ErrorCode::NETWORK_RECEIVE_ERROR;
case CURLE_SSL_CERTPROBLEM:
return ErrorCode::SSL_LOCAL_CERTIFICATE_ERROR;
case CURLE_SSL_CIPHER:
return ErrorCode::GENERIC_SSL_ERROR;
#if LIBCURL_VERSION_NUM >= 0x073e00
case CURLE_PEER_FAILED_VERIFICATION:
return ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR;
#else
case CURLE_SSL_CACERT:
return ErrorCode::SSL_CACERT_ERROR;
#endif
case CURLE_USE_SSL_FAILED:
case CURLE_SSL_ENGINE_INITFAILED:
return ErrorCode::GENERIC_SSL_ERROR;
case CURLE_SSL_CACERT_BADFILE:
return ErrorCode::SSL_CACERT_ERROR;
case CURLE_SSL_SHUTDOWN_FAILED:
return ErrorCode::GENERIC_SSL_ERROR;
case CURLE_SSL_CRL_BADFILE:
case CURLE_SSL_ISSUER_ERROR:
return ErrorCode::SSL_CACERT_ERROR;
case CURLE_TOO_MANY_REDIRECTS:
return ErrorCode::TOO_MANY_REDIRECTS;
default:
return ErrorCode::INTERNAL_ERROR;
}
}
} // namespace cpr

60
cpr/file.cpp Normal file
View file

@ -0,0 +1,60 @@
#include "cpr/file.h"
namespace cpr {
Files::Files(const std::initializer_list<std::string>& p_filepaths) {
for (const std::string& filepath : p_filepaths) {
files.emplace_back(filepath);
}
}
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

53
cpr/interceptor.cpp Normal file
View file

@ -0,0 +1,53 @@
#include "cpr/interceptor.h"
#include <exception>
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<Response> 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

5
cpr/multipart.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "cpr/multipart.h"
namespace cpr {
Multipart::Multipart(const std::initializer_list<Part>& p_parts) : parts{p_parts} {}
} // namespace cpr

331
cpr/multiperform.cpp Normal file
View file

@ -0,0 +1,331 @@
#include "cpr/multiperform.h"
#include "cpr/interceptor.h"
#include "cpr/multipart.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
namespace cpr {
MultiPerform::MultiPerform() : multicurl_(new CurlMultiHolder()) {}
MultiPerform::~MultiPerform() {
// Unlock all sessions
for (const std::pair<std::shared_ptr<Session>, HttpMethod>& pair : sessions_) {
pair.first->isUsedInMultiPerform = false;
// Remove easy handle from multi handle
const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, pair.first->curl_->handle);
if (error_code) {
std::cerr << "curl_multi_remove_handle() failed, code " << static_cast<int>(error_code) << std::endl;
return;
}
}
}
void MultiPerform::AddSession(std::shared_ptr<Session>& session, HttpMethod method) {
// Check if this multiperform is download only
if (((method != HttpMethod::DOWNLOAD_REQUEST && is_download_multi_perform) && method != HttpMethod::UNDEFINED) || (method == HttpMethod::DOWNLOAD_REQUEST && !is_download_multi_perform && !sessions_.empty())) {
// Currently it is not possible to mix download and non-download methods, as download needs additional parameters
throw std::invalid_argument("Failed to add session: Cannot mix download and non-download methods!");
}
// Set download only if neccessary
if (method == HttpMethod::DOWNLOAD_REQUEST) {
is_download_multi_perform = true;
}
// Add easy handle to multi handle
const CURLMcode error_code = curl_multi_add_handle(multicurl_->handle, session->curl_->handle);
if (error_code) {
std::cerr << "curl_multi_add_handle() failed, code " << static_cast<int>(error_code) << std::endl;
return;
}
// Lock session to the multihandle
session->isUsedInMultiPerform = true;
// Add session to sessions_
sessions_.emplace_back(session, method);
}
void MultiPerform::RemoveSession(const std::shared_ptr<Session>& session) {
// Remove easy handle from multihandle
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<int>(error_code) << std::endl;
return;
}
// Unock session
session->isUsedInMultiPerform = false;
// Remove session from sessions_
auto it = std::find_if(sessions_.begin(), sessions_.end(), [&session](const std::pair<std::shared_ptr<Session>, HttpMethod>& pair) { return session->curl_->handle == pair.first->curl_->handle; });
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<std::pair<std::shared_ptr<Session>, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() {
return sessions_;
}
const std::vector<std::pair<std::shared_ptr<Session>, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() const {
return sessions_;
}
void MultiPerform::DoMultiPerform() {
// Do multi perform until every handle has finished
int still_running{0};
do {
CURLMcode error_code = curl_multi_perform(multicurl_->handle, &still_running);
if (error_code) {
std::cerr << "curl_multi_perform() failed, code " << static_cast<int>(error_code) << std::endl;
break;
}
if (still_running) {
const int timeout_ms{250};
error_code = curl_multi_poll(multicurl_->handle, nullptr, 0, timeout_ms, nullptr);
if (error_code) {
std::cerr << "curl_multi_poll() failed, code " << static_cast<int>(error_code) << std::endl;
break;
}
}
} while (still_running);
}
std::vector<Response> MultiPerform::ReadMultiInfo(std::function<Response(Session&, CURLcode)>&& complete_function) {
// Get infos and create Response objects
std::vector<Response> 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<std::shared_ptr<Session>, HttpMethod>& pair) { return pair.first->curl_->handle == info->easy_handle; });
if (it == sessions_.end()) {
std::cerr << "Failed to find current session!" << std::endl;
break;
}
const std::shared_ptr<Session> current_session = (*it).first;
// Add response object
// NOLINTNEXTLINE (cppcoreguidelines-pro-type-union-access)
responses.push_back(complete_function(*current_session, info->data.result));
}
} while (info);
// Sort response objects to match order of added sessions
std::vector<Response> sorted_responses;
for (const std::pair<std::shared_ptr<Session>, HttpMethod>& pair : sessions_) {
Session& current_session = *(pair.first);
auto it = std::find_if(responses.begin(), responses.end(), [&current_session](const Response& response) { return current_session.curl_->handle == response.curl_->handle; });
const Response current_response = *it;
// Erase response from original vector to increase future search speed
responses.erase(it);
sorted_responses.push_back(current_response);
}
return sorted_responses;
}
std::vector<Response> MultiPerform::MakeRequest() {
if (!interceptors_.empty()) {
return intercept();
}
DoMultiPerform();
return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.Complete(curl_error); });
}
std::vector<Response> MultiPerform::MakeDownloadRequest() {
if (!interceptors_.empty()) {
return intercept();
}
DoMultiPerform();
return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.CompleteDownload(curl_error); });
}
void MultiPerform::PrepareSessions() {
for (const std::pair<std::shared_ptr<Session>, HttpMethod>& pair : sessions_) {
switch (pair.second) {
case HttpMethod::GET_REQUEST:
pair.first->PrepareGet();
break;
case HttpMethod::POST_REQUEST:
pair.first->PreparePost();
break;
case HttpMethod::PUT_REQUEST:
pair.first->PreparePut();
break;
case HttpMethod::DELETE_REQUEST:
pair.first->PrepareDelete();
break;
case HttpMethod::PATCH_REQUEST:
pair.first->PreparePatch();
break;
case HttpMethod::HEAD_REQUEST:
pair.first->PrepareHead();
break;
case HttpMethod::OPTIONS_REQUEST:
pair.first->PrepareOptions();
break;
default:
std::cerr << "PrepareSessions failed: Undefined HttpMethod or download without arguments!" << std::endl;
return;
}
}
}
void MultiPerform::PrepareDownloadSession(size_t sessions_index, const WriteCallback& write) {
const std::pair<std::shared_ptr<Session>, HttpMethod>& pair = sessions_[sessions_index];
switch (pair.second) {
case HttpMethod::DOWNLOAD_REQUEST:
pair.first->PrepareDownload(write);
break;
default:
std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << std::endl;
return;
}
}
void MultiPerform::PrepareDownloadSession(size_t sessions_index, std::ofstream& file) {
const std::pair<std::shared_ptr<Session>, HttpMethod>& pair = sessions_[sessions_index];
switch (pair.second) {
case HttpMethod::DOWNLOAD_REQUEST:
pair.first->PrepareDownload(file);
break;
default:
std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << std::endl;
return;
}
}
void MultiPerform::SetHttpMethod(HttpMethod method) {
for (std::pair<std::shared_ptr<Session>, HttpMethod>& pair : sessions_) {
pair.second = method;
}
}
void MultiPerform::PrepareGet() {
SetHttpMethod(HttpMethod::GET_REQUEST);
PrepareSessions();
}
void MultiPerform::PrepareDelete() {
SetHttpMethod(HttpMethod::DELETE_REQUEST);
PrepareSessions();
}
void MultiPerform::PreparePut() {
SetHttpMethod(HttpMethod::PUT_REQUEST);
PrepareSessions();
}
void MultiPerform::PreparePatch() {
SetHttpMethod(HttpMethod::PATCH_REQUEST);
PrepareSessions();
}
void MultiPerform::PrepareHead() {
SetHttpMethod(HttpMethod::HEAD_REQUEST);
PrepareSessions();
}
void MultiPerform::PrepareOptions() {
SetHttpMethod(HttpMethod::OPTIONS_REQUEST);
PrepareSessions();
}
void MultiPerform::PreparePost() {
SetHttpMethod(HttpMethod::POST_REQUEST);
PrepareSessions();
}
std::vector<Response> MultiPerform::Get() {
PrepareGet();
return MakeRequest();
}
std::vector<Response> MultiPerform::Delete() {
PrepareDelete();
return MakeRequest();
}
std::vector<Response> MultiPerform::Put() {
PreparePut();
return MakeRequest();
}
std::vector<Response> MultiPerform::Head() {
PrepareHead();
return MakeRequest();
}
std::vector<Response> MultiPerform::Options() {
PrepareOptions();
return MakeRequest();
}
std::vector<Response> MultiPerform::Patch() {
PreparePatch();
return MakeRequest();
}
std::vector<Response> MultiPerform::Post() {
PreparePost();
return MakeRequest();
}
std::vector<Response> MultiPerform::Perform() {
PrepareSessions();
return MakeRequest();
}
std::vector<Response> MultiPerform::proceed() {
// Check if this multiperform mixes download and non download requests
if (!sessions_.empty()) {
const bool new_is_download_multi_perform = sessions_.front().second == HttpMethod::DOWNLOAD_REQUEST;
for (const std::pair<std::shared_ptr<Session>, HttpMethod>& s : sessions_) {
const HttpMethod method = s.second;
if ((new_is_download_multi_perform && method != HttpMethod::DOWNLOAD_REQUEST) || (!new_is_download_multi_perform && method == HttpMethod::DOWNLOAD_REQUEST)) {
throw std::invalid_argument("Failed to proceed with session: Cannot mix download and non-download methods!");
}
}
is_download_multi_perform = new_is_download_multi_perform;
}
PrepareSessions();
return MakeRequest();
}
std::vector<Response> MultiPerform::intercept() {
// At least one interceptor exists -> Execute its intercept function
const std::shared_ptr<InterceptorMulti> interceptor = interceptors_.front();
interceptors_.pop();
return interceptor->intercept(*this);
}
void MultiPerform::AddInterceptor(const std::shared_ptr<InterceptorMulti>& pinterceptor) {
interceptors_.push(pinterceptor);
}
} // namespace cpr

10
cpr/parameters.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "cpr/parameters.h"
#include <initializer_list>
#include <string>
#include "cpr/util.h"
namespace cpr {
Parameters::Parameters(const std::initializer_list<Parameter>& parameters) : CurlContainer<Parameter>(parameters) {}
} // namespace cpr

10
cpr/payload.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "cpr/payload.h"
#include <initializer_list>
#include <string>
#include "cpr/util.h"
namespace cpr {
Payload::Payload(const std::initializer_list<Pair>& pairs) : CurlContainer<Pair>(pairs) {}
} // namespace cpr

21
cpr/proxies.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "cpr/proxies.h"
#include <initializer_list>
#include <map>
#include <string>
#include <utility>
namespace cpr {
Proxies::Proxies(const std::initializer_list<std::pair<const std::string, std::string>>& hosts) : hosts_{hosts} {}
Proxies::Proxies(const std::map<std::string, std::string>& hosts) : hosts_{hosts} {}
bool Proxies::has(const std::string& protocol) const {
return hosts_.count(protocol) > 0;
}
const std::string& Proxies::operator[](const std::string& protocol) {
return hosts_[protocol];
}
} // namespace cpr

21
cpr/proxyauth.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "cpr/proxyauth.h"
#include "cpr/util.h"
namespace cpr {
EncodedAuthentication::~EncodedAuthentication() noexcept {
util::secureStringClear(auth_string_);
}
const char* EncodedAuthentication::GetAuthString() const noexcept {
return auth_string_.c_str();
}
bool ProxyAuthentication::has(const std::string& protocol) const {
return proxyAuth_.count(protocol) > 0;
}
const char* ProxyAuthentication::operator[](const std::string& protocol) {
return proxyAuth_[protocol].GetAuthString();
}
} // namespace cpr

40
cpr/redirect.cpp Normal file
View file

@ -0,0 +1,40 @@
#include "cpr/redirect.h"
namespace cpr {
PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs) {
return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs) {
return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs) {
return static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs));
}
PostRedirectFlags operator~(PostRedirectFlags flag) {
return static_cast<PostRedirectFlags>(~static_cast<uint8_t>(flag));
}
PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {
lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
const uint8_t tmp = static_cast<uint8_t>(lhs);
lhs = static_cast<PostRedirectFlags>(tmp);
return lhs;
}
PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {
lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
return lhs;
}
PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs) {
lhs = static_cast<PostRedirectFlags>(static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs));
return lhs;
}
bool any(PostRedirectFlags flag) {
return flag != PostRedirectFlags::NONE;
}
} // namespace cpr

44
cpr/response.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "cpr/response.h"
namespace cpr {
Response::Response(std::shared_ptr<CurlHolder> curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies = Cookies{}, Error&& p_error = Error{}) : curl_(std::move(curl)), text(std::move(p_text)), cookies(std::move(p_cookies)), error(std::move(p_error)), raw_header(std::move(p_header_string)) {
header = cpr::util::parseHeader(raw_header, &status_line, &reason);
assert(curl_);
assert(curl_->handle);
curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code);
curl_easy_getinfo(curl_->handle, CURLINFO_TOTAL_TIME, &elapsed);
char* url_string{nullptr};
curl_easy_getinfo(curl_->handle, CURLINFO_EFFECTIVE_URL, &url_string);
url = Url(url_string);
#if LIBCURL_VERSION_NUM >= 0x073700
curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD_T, &downloaded_bytes);
curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD_T, &uploaded_bytes);
#else
double downloaded_bytes_double, uploaded_bytes_double;
curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD, &downloaded_bytes_double);
curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD, &uploaded_bytes_double);
downloaded_bytes = downloaded_bytes_double;
uploaded_bytes = uploaded_bytes_double;
#endif
curl_easy_getinfo(curl_->handle, CURLINFO_REDIRECT_COUNT, &redirect_count);
}
std::vector<CertInfo> Response::GetCertInfos() {
assert(curl_);
assert(curl_->handle);
curl_certinfo* ci{nullptr};
curl_easy_getinfo(curl_->handle, CURLINFO_CERTINFO, &ci);
std::vector<CertInfo> cert_infos;
for (int i = 0; i < ci->num_of_certs; i++) {
CertInfo cert_info;
// NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic)
for (curl_slist* slist = ci->certinfo[i]; slist; slist = slist->next) {
cert_info.emplace_back(std::string{slist->data});
}
cert_infos.emplace_back(cert_info);
}
return cert_infos;
}
} // namespace cpr

975
cpr/session.cpp Normal file
View file

@ -0,0 +1,975 @@
#include "cpr/session.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <stdexcept>
#include <string>
#include <curl/curl.h>
#include "cpr/async.h"
#include "cpr/cprtypes.h"
#include "cpr/interceptor.h"
#include "cpr/util.h"
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
#include "cpr/ssl_ctx.h"
#endif
namespace cpr {
// Ignored here since libcurl reqires a long:
// NOLINTNEXTLINE(google-runtime-int)
constexpr long ON = 1L;
// Ignored here since libcurl reqires a long:
// NOLINTNEXTLINE(google-runtime-int)
constexpr long OFF = 0L;
CURLcode Session::DoEasyPerform() {
if (isUsedInMultiPerform) {
std::cerr << "curl_easy_perform cannot be executed if the CURL handle is used in a MultiPerform." << std::endl;
return CURLcode::CURLE_FAILED_INIT;
}
return curl_easy_perform(curl_->handle);
}
void Session::SetHeaderInternal() {
curl_slist* chunk = nullptr;
for (const std::pair<const std::string, std::string>& item : header_) {
std::string header_string = item.first;
if (item.second.empty()) {
header_string += ";";
} else {
header_string += ": " + item.second;
}
curl_slist* temp = curl_slist_append(chunk, header_string.c_str());
if (temp) {
chunk = temp;
}
}
// Set the chunked transfer encoding in case it does not already exist:
if (chunkedTransferEncoding_ && header_.find("Transfer-Encoding") == header_.end()) {
curl_slist* temp = curl_slist_append(chunk, "Transfer-Encoding:chunked");
if (temp) {
chunk = temp;
}
}
// libcurl would prepare the header "Expect: 100-continue" by default when uploading files larger than 1 MB.
// Here we would like to disable this feature:
curl_slist* temp = curl_slist_append(chunk, "Expect:");
if (temp) {
chunk = temp;
}
curl_easy_setopt(curl_->handle, CURLOPT_HTTPHEADER, chunk);
curl_slist_free_all(curl_->chunk);
curl_->chunk = chunk;
}
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void Session::SetBearer(const Bearer& token) {
// Ignore here since this has been defined by libcurl.
curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
curl_easy_setopt(curl_->handle, CURLOPT_XOAUTH2_BEARER, token.GetToken());
}
#endif
Session::Session() : curl_(new CurlHolder()) {
// Set up some sensible defaults
curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW);
const std::string version = "curl/" + std::string{version_info->version};
curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str());
SetRedirect(Redirect());
curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data());
curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, "");
#ifdef CPR_CURL_NOSIGNAL
curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L);
#endif
#if LIBCURL_VERSION_NUM >= 0x071900
curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L);
#endif
}
Response Session::makeDownloadRequest() {
if (!interceptors_.empty()) {
return intercept();
}
const CURLcode curl_error = DoEasyPerform();
return CompleteDownload(curl_error);
}
void Session::prepareCommon() {
assert(curl_->handle);
// Set Header:
SetHeaderInternal();
const std::string parametersContent = parameters_.GetContent(*curl_);
if (!parametersContent.empty()) {
const Url new_url{url_ + "?" + parametersContent};
curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str());
} else {
curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str());
}
// Proxy:
const std::string protocol = url_.str().substr(0, url_.str().find(':'));
if (proxies_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str());
if (proxyAuth_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]);
}
}
#if LIBCURL_VERSION_NUM >= 0x072100
if (acceptEncoding_.empty()) {
// Enable all supported built-in compressions
curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, "");
} else if (acceptEncoding_.disabled()) {
// Disable curl adding the 'Accept-Encoding' header
curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, nullptr);
} else {
curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str());
}
#endif
#if LIBCURL_VERSION_NUM >= 0x077100
#if SUPPORT_SSL_NO_REVOKE
// NOLINTNEXTLINE (google-runtime-int)
long bitmask{0};
curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, &bitmask);
const bool noRevoke = bitmask & CURLSSLOPT_NO_REVOKE;
#endif
// Fix loading certs from Windows cert store when using OpenSSL:
curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
// Ensure SSL no revoke is still set
#if SUPPORT_SSL_NO_REVOKE
if (noRevoke) {
curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
}
#endif
#endif
curl_->error[0] = '\0';
response_string_.clear();
if (response_string_reserve_size_ > 0) {
response_string_.reserve(response_string_reserve_size_);
}
header_string_.clear();
if (!this->writecb_.callback) {
curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction);
curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_);
}
if (!this->headercb_.callback) {
curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction);
curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_);
}
// Enable so we are able to retrive certificate information:
curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L);
}
void Session::prepareCommonDownload() {
assert(curl_->handle);
// Set Header:
SetHeaderInternal();
const std::string parametersContent = parameters_.GetContent(*curl_);
if (!parametersContent.empty()) {
const Url new_url{url_ + "?" + parametersContent};
curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str());
} else {
curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str());
}
const std::string protocol = url_.str().substr(0, url_.str().find(':'));
if (proxies_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str());
if (proxyAuth_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]);
}
}
curl_->error[0] = '\0';
header_string_.clear();
if (headercb_.callback) {
curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction);
curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_);
} else {
curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction);
curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_);
}
}
Response Session::makeRequest() {
if (!interceptors_.empty()) {
return intercept();
}
const CURLcode curl_error = DoEasyPerform();
return Complete(curl_error);
}
void Session::SetLimitRate(const LimitRate& limit_rate) {
curl_easy_setopt(curl_->handle, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate.downrate);
curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate);
}
void Session::SetReadCallback(const ReadCallback& read) {
readcb_ = read;
curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size);
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size);
curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction);
curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &readcb_);
chunkedTransferEncoding_ = read.size == -1;
}
void Session::SetHeaderCallback(const HeaderCallback& header) {
curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction);
headercb_ = header;
curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_);
}
void Session::SetWriteCallback(const WriteCallback& write) {
curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction);
writecb_ = write;
curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &writecb_);
}
void Session::SetProgressCallback(const ProgressCallback& progress) {
progresscb_ = progress;
if (isCancellable) {
cancellationcb_.SetProgressCallback(progresscb_);
return;
}
#if LIBCURL_VERSION_NUM < 0x072000
curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<ProgressCallback>);
curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &progresscb_);
#else
curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<ProgressCallback>);
curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &progresscb_);
#endif
curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L);
}
void Session::SetDebugCallback(const DebugCallback& debug) {
curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction);
debugcb_ = debug;
curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &debugcb_);
curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L);
}
void Session::SetUrl(const Url& url) {
url_ = url;
}
void Session::SetResolve(const Resolve& resolve) {
SetResolves({resolve});
}
void Session::SetResolves(const std::vector<Resolve>& resolves) {
curl_slist_free_all(curl_->resolveCurlList);
curl_->resolveCurlList = nullptr;
for (const Resolve& resolve : resolves) {
for (const uint16_t port : resolve.ports) {
curl_->resolveCurlList = curl_slist_append(curl_->resolveCurlList, (resolve.host + ":" + std::to_string(port) + ":" + resolve.addr).c_str());
}
}
curl_easy_setopt(curl_->handle, CURLOPT_RESOLVE, curl_->resolveCurlList);
}
void Session::SetParameters(const Parameters& parameters) {
parameters_ = parameters;
}
void Session::SetParameters(Parameters&& parameters) {
parameters_ = std::move(parameters);
}
void Session::SetHeader(const Header& header) {
header_ = header;
}
void Session::UpdateHeader(const Header& header) {
for (const std::pair<const std::string, std::string>& item : header) {
header_[item.first] = item.second;
}
}
void Session::SetTimeout(const Timeout& timeout) {
curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds());
}
void Session::SetConnectTimeout(const ConnectTimeout& timeout) {
curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds());
}
void Session::SetAuth(const Authentication& auth) {
// Ignore here since this has been defined by libcurl.
switch (auth.GetAuthMode()) {
case AuthMode::BASIC:
curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());
break;
case AuthMode::DIGEST:
curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());
break;
case AuthMode::NTLM:
curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString());
break;
}
}
void Session::SetUserAgent(const UserAgent& ua) {
curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str());
}
void Session::SetPayload(const Payload& payload) {
hasBodyOrPayload_ = true;
const std::string content = payload.GetContent(*curl_);
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(content.length()));
curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str());
}
void Session::SetPayload(Payload&& payload) {
hasBodyOrPayload_ = true;
const std::string content = payload.GetContent(*curl_);
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(content.length()));
curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str());
}
void Session::SetProxies(const Proxies& proxies) {
proxies_ = proxies;
}
void Session::SetProxies(Proxies&& proxies) {
proxies_ = std::move(proxies);
}
void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) {
proxyAuth_ = std::move(proxy_auth);
}
void Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) {
proxyAuth_ = proxy_auth;
}
void Session::SetMultipart(const Multipart& multipart) {
// Make sure, we have a empty multipart to start with:
if (curl_->multipart) {
curl_mime_free(curl_->multipart);
}
curl_->multipart = curl_mime_init(curl_->handle);
// Add all multipart pieces:
for (const Part& part : multipart.parts) {
if (part.is_file) {
for (const File& file : part.files) {
curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart);
if (!part.content_type.empty()) {
curl_mime_type(mimePart, part.content_type.c_str());
}
curl_mime_filedata(mimePart, file.filepath.c_str());
curl_mime_name(mimePart, part.name.c_str());
if (file.hasOverridenFilename()) {
curl_mime_filename(mimePart, file.overriden_filename.c_str());
}
}
} else {
curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart);
if (!part.content_type.empty()) {
curl_mime_type(mimePart, part.content_type.c_str());
}
if (part.is_buffer) {
// Do not use formdata, to prevent having to use reinterpreter_cast:
curl_mime_name(mimePart, part.name.c_str());
curl_mime_data(mimePart, part.data, part.datalen);
curl_mime_filename(mimePart, part.value.c_str());
} else {
curl_mime_name(mimePart, part.name.c_str());
curl_mime_data(mimePart, part.value.c_str(), CURL_ZERO_TERMINATED);
}
}
}
curl_easy_setopt(curl_->handle, CURLOPT_MIMEPOST, curl_->multipart);
hasBodyOrPayload_ = true;
}
void Session::SetMultipart(Multipart&& multipart) {
SetMultipart(multipart);
}
void Session::SetRedirect(const Redirect& redirect) {
curl_easy_setopt(curl_->handle, CURLOPT_FOLLOWLOCATION, redirect.follow ? 1L : 0L);
curl_easy_setopt(curl_->handle, CURLOPT_MAXREDIRS, redirect.maximum);
curl_easy_setopt(curl_->handle, CURLOPT_UNRESTRICTED_AUTH, redirect.cont_send_cred ? 1L : 0L);
// NOLINTNEXTLINE (google-runtime-int)
long mask = 0;
if (any(redirect.post_flags & PostRedirectFlags::POST_301)) {
mask |= CURL_REDIR_POST_301;
}
if (any(redirect.post_flags & PostRedirectFlags::POST_302)) {
mask |= CURL_REDIR_POST_302;
}
if (any(redirect.post_flags & PostRedirectFlags::POST_303)) {
mask |= CURL_REDIR_POST_303;
}
curl_easy_setopt(curl_->handle, CURLOPT_POSTREDIR, mask);
}
void Session::SetCookies(const Cookies& cookies) {
curl_easy_setopt(curl_->handle, CURLOPT_COOKIELIST, "ALL");
curl_easy_setopt(curl_->handle, CURLOPT_COOKIE, cookies.GetEncoded(*curl_).c_str());
}
void Session::SetBody(const Body& body) {
hasBodyOrPayload_ = true;
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.str().length()));
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, body.c_str());
}
void Session::SetBody(Body&& body) {
hasBodyOrPayload_ = true;
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.str().length()));
curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str());
}
void Session::SetLowSpeed(const LowSpeed& low_speed) {
curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, low_speed.limit);
curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, low_speed.time);
}
void Session::SetVerifySsl(const VerifySsl& verify) {
curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF);
curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L);
}
void Session::SetUnixSocket(const UnixSocket& unix_socket) {
curl_easy_setopt(curl_->handle, CURLOPT_UNIX_SOCKET_PATH, unix_socket.GetUnixSocketString());
}
void Session::SetSslOptions(const SslOptions& options) {
if (!options.cert_file.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT, options.cert_file.c_str());
if (!options.cert_type.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSLCERTTYPE, options.cert_type.c_str());
}
}
if (!options.key_file.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY, options.key_file.c_str());
if (!options.key_type.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str());
}
if (!options.key_pass.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str());
}
#if SUPPORT_CURLOPT_SSLKEY_BLOB
} else if (!options.key_blob.empty()) {
std::string key_blob(options.key_blob);
curl_blob blob{};
// NOLINTNEXTLINE (readability-container-data-pointer)
blob.data = &key_blob[0];
blob.len = key_blob.length();
curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY_BLOB, &blob);
if (!options.key_type.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str());
}
if (!options.key_pass.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str());
}
#endif
}
if (!options.pinned_public_key.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_PINNEDPUBLICKEY, options.pinned_public_key.c_str());
}
#if SUPPORT_ALPN
curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_ALPN, options.enable_alpn ? ON : OFF);
#endif
#if SUPPORT_NPN
curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_NPN, options.enable_npn ? ON : OFF);
#endif
curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, options.verify_peer ? ON : OFF);
curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, options.verify_host ? 2L : 0L);
#if LIBCURL_VERSION_NUM >= 0x072900
curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYSTATUS, options.verify_status ? ON : OFF);
#endif
int maxTlsVersion = options.ssl_version;
#if SUPPORT_MAX_TLS_VERSION
maxTlsVersion |= options.max_version;
#endif
curl_easy_setopt(curl_->handle, CURLOPT_SSLVERSION,
// Ignore here since this has been defined by libcurl.
maxTlsVersion);
#if SUPPORT_SSL_NO_REVOKE
if (options.ssl_no_revoke) {
curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
}
#endif
if (!options.ca_info.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_CAINFO, options.ca_info.c_str());
}
if (!options.ca_path.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_CAPATH, options.ca_path.c_str());
}
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
#ifdef OPENSSL_BACKEND_USED
if (!options.ca_buffer.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function_load_ca_cert_from_buffer);
curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_DATA, options.ca_buffer.c_str());
}
#endif
#endif
if (!options.crl_file.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_CRLFILE, options.crl_file.c_str());
}
if (!options.ciphers.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_SSL_CIPHER_LIST, options.ciphers.c_str());
}
#if SUPPORT_TLSv13_CIPHERS
if (!options.tls13_ciphers.empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_TLS13_CIPHERS, options.ciphers.c_str());
}
#endif
#if SUPPORT_SESSIONID_CACHE
curl_easy_setopt(curl_->handle, CURLOPT_SSL_SESSIONID_CACHE, options.session_id_cache ? ON : OFF);
#endif
}
void Session::SetVerbose(const Verbose& verbose) {
curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF);
}
void Session::SetInterface(const Interface& iface) {
if (iface.str().empty()) {
curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr);
} else {
curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str());
}
}
void Session::SetLocalPort(const LocalPort& local_port) {
curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORT, local_port);
}
void Session::SetLocalPortRange(const LocalPortRange& local_port_range) {
curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORTRANGE, local_port_range);
}
void Session::SetHttpVersion(const HttpVersion& version) {
switch (version.code) {
case HttpVersionCode::VERSION_NONE:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
break;
case HttpVersionCode::VERSION_1_0:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
break;
case HttpVersionCode::VERSION_1_1:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
break;
#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0
case HttpVersionCode::VERSION_2_0:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
break;
#endif
#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0
case HttpVersionCode::VERSION_2_0_TLS:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
break;
#endif
#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0
case HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
break;
#endif
#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0
case HttpVersionCode::VERSION_3_0:
curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
break;
#endif
default: // Should not happen
throw std::invalid_argument("Invalid/Unknown HTTP version type.");
break;
}
}
void Session::SetRange(const Range& range) {
const std::string range_str = range.str();
curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str());
}
void Session::SetMultiRange(const MultiRange& multi_range) {
const std::string multi_range_str = multi_range.str();
curl_easy_setopt(curl_->handle, CURLOPT_RANGE, multi_range_str.c_str());
}
void Session::SetReserveSize(const ReserveSize& reserve_size) {
ResponseStringReserve(reserve_size.size);
}
void Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) {
acceptEncoding_ = accept_encoding;
}
void Session::SetAcceptEncoding(AcceptEncoding&& accept_encoding) {
acceptEncoding_ = std::move(accept_encoding);
}
cpr_off_t Session::GetDownloadFileLength() {
cpr_off_t downloadFileLenth = -1;
curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str());
const std::string protocol = url_.str().substr(0, url_.str().find(':'));
if (proxies_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str());
if (proxyAuth_.has(protocol)) {
curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]);
}
}
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1);
if (DoEasyPerform() == CURLE_OK) {
// NOLINTNEXTLINE (google-runtime-int)
long status_code{};
curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code);
if (200 == status_code) {
curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLenth);
}
}
return downloadFileLenth;
}
void Session::ResponseStringReserve(size_t size) {
response_string_reserve_size_ = size;
}
Response Session::Delete() {
PrepareDelete();
return makeRequest();
}
Response Session::Download(const WriteCallback& write) {
PrepareDownload(write);
return makeDownloadRequest();
}
Response Session::Download(std::ofstream& file) {
PrepareDownload(file);
return makeDownloadRequest();
}
Response Session::Get() {
PrepareGet();
return makeRequest();
}
Response Session::Head() {
PrepareHead();
return makeRequest();
}
Response Session::Options() {
PrepareOptions();
return makeRequest();
}
Response Session::Patch() {
PreparePatch();
return makeRequest();
}
Response Session::Post() {
PreparePost();
return makeRequest();
}
Response Session::Put() {
PreparePut();
return makeRequest();
}
std::shared_ptr<Session> Session::GetSharedPtrFromThis() {
try {
return shared_from_this();
} catch (std::bad_weak_ptr&) {
throw std::runtime_error("Failed to get a shared pointer from this. The reason is probably that the session object is not managed by a shared pointer, which is required to use this functionality.");
}
}
AsyncResponse Session::GetAsync() {
auto shared_this = shared_from_this();
return async([shared_this]() { return shared_this->Get(); });
}
AsyncResponse Session::DeleteAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Delete(); });
}
AsyncResponse Session::DownloadAsync(const WriteCallback& write) {
return async([shared_this = GetSharedPtrFromThis(), write]() { return shared_this->Download(write); });
}
AsyncResponse Session::DownloadAsync(std::ofstream& file) {
return async([shared_this = GetSharedPtrFromThis(), &file]() { return shared_this->Download(file); });
}
AsyncResponse Session::HeadAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Head(); });
}
AsyncResponse Session::OptionsAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Options(); });
}
AsyncResponse Session::PatchAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Patch(); });
}
AsyncResponse Session::PostAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Post(); });
}
AsyncResponse Session::PutAsync() {
return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Put(); });
}
std::shared_ptr<CurlHolder> Session::GetCurlHolder() {
return curl_;
}
std::string Session::GetFullRequestUrl() {
const std::string parametersContent = parameters_.GetContent(*curl_);
return url_.str() + (parametersContent.empty() ? "" : "?") + parametersContent;
}
void Session::PrepareDelete() {
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "DELETE");
prepareCommon();
}
void Session::PrepareGet() {
// In case there is a body or payload for this request, we create a custom GET-Request since a
// GET-Request with body is based on the HTTP RFC **not** a leagal request.
if (hasBodyOrPayload_) {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "GET");
} else {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1L);
}
prepareCommon();
}
void Session::PrepareHead() {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);
prepareCommon();
}
void Session::PrepareOptions() {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
prepareCommon();
}
void Session::PreparePatch() {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PATCH");
prepareCommon();
}
void Session::PreparePost() {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
// In case there is no body or payload set it to an empty post:
if (hasBodyOrPayload_) {
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);
} else {
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, readcb_.callback ? nullptr : "");
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "POST");
}
prepareCommon();
}
void Session::PreparePut() {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
if (!hasBodyOrPayload_ && readcb_.callback) {
/**
* Yes, this one has to be CURLOPT_POSTFIELDS even if we are performing a PUT request.
* In case we don't set this one, performing a POST-request with PUT won't work.
* It in theory this only enforces the usage of the readcallback for POST requests, but works here as well.
**/
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr);
}
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl_->handle, CURLOPT_RANGE, nullptr);
prepareCommon();
}
void Session::PrepareDownload(std::ofstream& file) {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction);
curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);
prepareCommonDownload();
}
void Session::PrepareDownload(const WriteCallback& write) {
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr);
SetWriteCallback(write);
prepareCommonDownload();
}
Response Session::Complete(CURLcode curl_error) {
curl_slist* raw_cookies{nullptr};
curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies);
Cookies cookies = util::parseCookies(raw_cookies);
curl_slist_free_all(raw_cookies);
// Reset the has no body property:
hasBodyOrPayload_ = false;
std::string errorMsg = curl_->error.data();
return Response(curl_, std::move(response_string_), std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg)));
}
Response Session::CompleteDownload(CURLcode curl_error) {
if (!headercb_.callback) {
curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr);
curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0);
}
curl_slist* raw_cookies{nullptr};
curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies);
Cookies cookies = util::parseCookies(raw_cookies);
curl_slist_free_all(raw_cookies);
std::string errorMsg = curl_->error.data();
return Response(curl_, "", std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg)));
}
void Session::AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor) {
interceptors_.push(pinterceptor);
}
Response Session::proceed() {
prepareCommon();
return makeRequest();
}
Response Session::intercept() {
// At least one interceptor exists -> Execute its intercept function
const std::shared_ptr<Interceptor> interceptor = interceptors_.front();
interceptors_.pop();
return interceptor->intercept(*this);
}
// clang-format off
void Session::SetOption(const Resolve& resolve) { SetResolve(resolve); }
void Session::SetOption(const std::vector<Resolve>& resolves) { SetResolves(resolves); }
void Session::SetOption(const ReadCallback& read) { SetReadCallback(read); }
void Session::SetOption(const HeaderCallback& header) { SetHeaderCallback(header); }
void Session::SetOption(const WriteCallback& write) { SetWriteCallback(write); }
void Session::SetOption(const ProgressCallback& progress) { SetProgressCallback(progress); }
void Session::SetOption(const DebugCallback& debug) { SetDebugCallback(debug); }
void Session::SetOption(const Url& url) { SetUrl(url); }
void Session::SetOption(const Parameters& parameters) { SetParameters(parameters); }
void Session::SetOption(Parameters&& parameters) { SetParameters(std::move(parameters)); }
void Session::SetOption(const Header& header) { SetHeader(header); }
void Session::SetOption(const Timeout& timeout) { SetTimeout(timeout); }
void Session::SetOption(const ConnectTimeout& timeout) { SetConnectTimeout(timeout); }
void Session::SetOption(const Authentication& auth) { SetAuth(auth); }
void Session::SetOption(const LimitRate& limit_rate) { SetLimitRate(limit_rate); }
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void Session::SetOption(const Bearer& auth) { SetBearer(auth); }
#endif
void Session::SetOption(const UserAgent& ua) { SetUserAgent(ua); }
void Session::SetOption(const Payload& payload) { SetPayload(payload); }
void Session::SetOption(Payload&& payload) { SetPayload(std::move(payload)); }
void Session::SetOption(const Proxies& proxies) { SetProxies(proxies); }
void Session::SetOption(Proxies&& proxies) { SetProxies(std::move(proxies)); }
void Session::SetOption(ProxyAuthentication&& proxy_auth) { SetProxyAuth(std::move(proxy_auth)); }
void Session::SetOption(const ProxyAuthentication& proxy_auth) { SetProxyAuth(proxy_auth); }
void Session::SetOption(const Multipart& multipart) { SetMultipart(multipart); }
void Session::SetOption(Multipart&& multipart) { SetMultipart(std::move(multipart)); }
void Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); }
void Session::SetOption(const Cookies& cookies) { SetCookies(cookies); }
void Session::SetOption(const Body& body) { SetBody(body); }
void Session::SetOption(Body&& body) { SetBody(std::move(body)); }
void Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); }
void Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); }
void Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); }
void Session::SetOption(const UnixSocket& unix_socket) { SetUnixSocket(unix_socket); }
void Session::SetOption(const SslOptions& options) { SetSslOptions(options); }
void Session::SetOption(const Interface& iface) { SetInterface(iface); }
void Session::SetOption(const LocalPort& local_port) { SetLocalPort(local_port); }
void Session::SetOption(const LocalPortRange& local_port_range) { SetLocalPortRange(local_port_range); }
void Session::SetOption(const HttpVersion& version) { SetHttpVersion(version); }
void Session::SetOption(const Range& range) { SetRange(range); }
void Session::SetOption(const MultiRange& multi_range) { SetMultiRange(multi_range); }
void Session::SetOption(const ReserveSize& reserve_size) { SetReserveSize(reserve_size.size); }
void Session::SetOption(const AcceptEncoding& accept_encoding) { SetAcceptEncoding(accept_encoding); }
void Session::SetOption(AcceptEncoding&& accept_encoding) { SetAcceptEncoding(accept_encoding); }
// clang-format on
void Session::SetCancellationParam(std::shared_ptr<std::atomic_bool> param) {
cancellationcb_ = CancellationCallback{std::move(param)};
isCancellable = true;
#if LIBCURL_VERSION_NUM < 0x072000
curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<CancellationCallback>);
curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cancellationcb_);
#else
curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<CancellationCallback>);
curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cancellationcb_);
#endif
curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L);
}
} // namespace cpr

70
cpr/ssl_ctx.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "cpr/ssl_ctx.h"
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
#ifdef OPENSSL_BACKEND_USED
#include <openssl/err.h>
#include <openssl/safestack.h>
#include <openssl/ssl.h>
namespace cpr {
/**
* The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL.
* If an error is returned from the callback no attempt to establish a connection is made and
* the perform operation will return the callback's error code.
*
* Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html
* https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html
*/
CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) {
// Check arguments
if (raw_cert_buf == nullptr || sslctx == nullptr) {
printf("Invalid callback arguments\n");
return CURLE_ABORTED_BY_CALLBACK;
}
// Setup pointer
X509_STORE* store = nullptr;
X509* cert = nullptr;
BIO* bio = nullptr;
char* cert_buf = static_cast<char*>(raw_cert_buf);
// Create a memory BIO using the data of cert_buf.
// Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen.
bio = BIO_new_mem_buf(cert_buf, -1);
// Load the PEM formatted certicifate into an X509 structure which OpenSSL can use.
PEM_read_bio_X509(bio, &cert, nullptr, nullptr);
if (cert == nullptr) {
printf("PEM_read_bio_X509 failed\n");
return CURLE_ABORTED_BY_CALLBACK;
}
// Get a pointer to the current certificate verification storage
store = SSL_CTX_get_cert_store(static_cast<SSL_CTX*>(sslctx));
// Add the loaded certificate to the verification storage
const int status = X509_STORE_add_cert(store, cert);
if (status == 0) {
printf("Error adding certificate\n");
return CURLE_ABORTED_BY_CALLBACK;
}
// Decrement the reference count of the X509 structure cert and frees it up
X509_free(cert);
// Free the entire bio chain
BIO_free(bio);
// The CA certificate was loaded successfully into the verification storage
return CURLE_OK;
}
} // namespace cpr
#endif // OPENSSL_BACKEND_USED
#endif // SUPPORT_CURLOPT_SSL_CTX_FUNCTION

148
cpr/threadpool.cpp Normal file
View file

@ -0,0 +1,148 @@
#include "cpr/threadpool.h"
namespace cpr {
ThreadPool::ThreadPool(size_t min_threads, size_t max_threads, std::chrono::milliseconds max_idle_ms) : min_thread_num(min_threads), max_thread_num(max_threads), max_idle_time(max_idle_ms), status(STOP), cur_thread_num(0), idle_thread_num(0) {}
ThreadPool::~ThreadPool() {
Stop();
}
int ThreadPool::Start(size_t start_threads) {
if (status != STOP) {
return -1;
}
status = RUNNING;
if (start_threads < min_thread_num) {
start_threads = min_thread_num;
}
if (start_threads > max_thread_num) {
start_threads = max_thread_num;
}
for (size_t i = 0; i < start_threads; ++i) {
CreateThread();
}
return 0;
}
int ThreadPool::Stop() {
if (status == STOP) {
return -1;
}
status = STOP;
task_cond.notify_all();
for (auto& i : threads) {
if (i.thread->joinable()) {
i.thread->join();
}
}
threads.clear();
cur_thread_num = 0;
idle_thread_num = 0;
return 0;
}
int ThreadPool::Pause() {
if (status == RUNNING) {
status = PAUSE;
}
return 0;
}
int ThreadPool::Resume() {
if (status == PAUSE) {
status = RUNNING;
}
return 0;
}
int ThreadPool::Wait() {
while (true) {
if (status == STOP || (tasks.empty() && idle_thread_num == cur_thread_num)) {
break;
}
std::this_thread::yield();
}
return 0;
}
bool ThreadPool::CreateThread() {
if (cur_thread_num >= max_thread_num) {
return false;
}
std::thread* thread = new std::thread([this] {
bool initialRun = true;
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
Task task;
{
std::unique_lock<std::mutex> locker(task_mutex);
task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() { return status == STOP || !tasks.empty(); });
if (status == STOP) {
return;
}
if (tasks.empty()) {
if (cur_thread_num > min_thread_num) {
DelThread(std::this_thread::get_id());
return;
}
continue;
}
if (!initialRun) {
--idle_thread_num;
}
task = std::move(tasks.front());
tasks.pop();
}
if (task) {
task();
++idle_thread_num;
if (initialRun) {
initialRun = false;
}
}
}
});
AddThread(thread);
return true;
}
void ThreadPool::AddThread(std::thread* thread) {
thread_mutex.lock();
++cur_thread_num;
ThreadData data;
data.thread = std::shared_ptr<std::thread>(thread);
data.id = thread->get_id();
data.status = RUNNING;
data.start_time = time(nullptr);
data.stop_time = 0;
threads.emplace_back(data);
thread_mutex.unlock();
}
void ThreadPool::DelThread(std::thread::id id) {
const time_t now = time(nullptr);
thread_mutex.lock();
--cur_thread_num;
--idle_thread_num;
auto iter = threads.begin();
while (iter != threads.end()) {
if (iter->status == STOP && now > iter->stop_time) {
if (iter->thread->joinable()) {
iter->thread->join();
iter = threads.erase(iter);
continue;
}
} else if (iter->id == id) {
iter->status = STOP;
iter->stop_time = time(nullptr);
}
++iter;
}
thread_mutex.unlock();
}
} // namespace cpr

31
cpr/timeout.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "cpr/timeout.h"
#include <limits>
#include <stdexcept>
#include <string>
#include <type_traits>
namespace cpr {
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
long Timeout::Milliseconds() const {
static_assert(std::is_same<std::chrono::milliseconds, decltype(ms)>::value, "Following casting expects milliseconds.");
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
if (ms.count() > static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<long>::max())) {
throw std::overflow_error("cpr::Timeout: timeout value overflow: " + std::to_string(ms.count()) + " ms.");
}
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
if (ms.count() < static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<long>::min())) {
throw std::underflow_error("cpr::Timeout: timeout value underflow: " + std::to_string(ms.count()) + " ms.");
}
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
return static_cast<long>(ms.count());
}
} // namespace cpr

8
cpr/unix_socket.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "cpr/unix_socket.h"
namespace cpr {
const char* UnixSocket::GetUnixSocketString() const noexcept {
return unix_socket_.data();
}
} // namespace cpr

228
cpr/util.cpp Normal file
View file

@ -0,0 +1,228 @@
#include "cpr/util.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iomanip>
#include <ios>
#include <sstream>
#include <string>
#include <vector>
#if defined(_Win32)
#include <Windows.h>
#else
// https://en.cppreference.com/w/c/string/byte/memset
// NOLINTNEXTLINE(bugprone-reserved-identifier, cert-dcl37-c, cert-dcl51-cpp, cppcoreguidelines-macro-usage)
#define __STDC_WANT_LIB_EXT1__ 1
#include <cstring>
#endif
namespace cpr::util {
enum class CurlHTTPCookieField : size_t {
Domain = 0,
IncludeSubdomains,
Path,
HttpsOnly,
Expires,
Name,
Value,
};
Cookies parseCookies(curl_slist* raw_cookies) {
const int CURL_HTTP_COOKIE_SIZE = static_cast<int>(CurlHTTPCookieField::Value) + 1;
Cookies cookies;
for (curl_slist* nc = raw_cookies; nc; nc = nc->next) {
std::vector<std::string> tokens = cpr::util::split(nc->data, '\t');
while (tokens.size() < CURL_HTTP_COOKIE_SIZE) {
tokens.emplace_back("");
}
const std::time_t expires = static_cast<time_t>(std::stoul(tokens.at(static_cast<size_t>(CurlHTTPCookieField::Expires))));
cookies.emplace_back(Cookie{
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Name)),
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Value)),
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Domain)),
isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::IncludeSubdomains))),
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Path)),
isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::HttpsOnly))),
std::chrono::system_clock::from_time_t(expires),
});
}
return cookies;
}
Header parseHeader(const std::string& headers, std::string* status_line, std::string* reason) {
Header header;
std::vector<std::string> lines;
std::istringstream stream(headers);
{
std::string line;
while (std::getline(stream, line, '\n')) {
lines.push_back(line);
}
}
for (std::string& line : lines) {
if (line.substr(0, 5) == "HTTP/") {
// set the status_line if it was given
if ((status_line != nullptr) || (reason != nullptr)) {
line.resize(std::min<size_t>(line.size(), line.find_last_not_of("\t\n\r ") + 1));
if (status_line != nullptr) {
*status_line = line;
}
// set the reason if it was given
if (reason != nullptr) {
const size_t pos1 = line.find_first_of("\t ");
size_t pos2 = std::string::npos;
if (pos1 != std::string::npos) {
pos2 = line.find_first_of("\t ", pos1 + 1);
}
if (pos2 != std::string::npos) {
line.erase(0, pos2 + 1);
*reason = line;
}
}
}
header.clear();
}
if (line.length() > 0) {
const size_t found = line.find(':');
if (found != std::string::npos) {
std::string value = line.substr(found + 1);
value.erase(0, value.find_first_not_of("\t "));
value.resize(std::min<size_t>(value.size(), value.find_last_not_of("\t\n\r ") + 1));
header[line.substr(0, found)] = value;
}
}
}
return header;
}
std::vector<std::string> split(const std::string& to_split, char delimiter) {
std::vector<std::string> tokens;
std::stringstream stream(to_split);
std::string item;
while (std::getline(stream, item, delimiter)) {
tokens.push_back(item);
}
return tokens;
}
size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read) {
size *= nitems;
return (*read)(ptr, size) ? size : CURL_READFUNC_ABORT;
}
size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header) {
size *= nmemb;
return (*header)({ptr, size}) ? size : 0;
}
size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data) {
size *= nmemb;
data->append(ptr, size);
return size;
}
size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file) {
size *= nmemb;
file->write(ptr, static_cast<std::streamsize>(size));
return size;
}
size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write) {
size *= nmemb;
return (*write)({ptr, size}) ? size : 0;
}
int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, const DebugCallback* debug) {
(*debug)(static_cast<DebugCallback::InfoType>(type), std::string(data, size));
return 0;
}
/**
* Creates a temporary CurlHolder object and uses it to escape the given string.
* If you plan to use this methode on a regular basis think about creating a CurlHolder
* object and calling urlEncode(std::string) on it.
*
* Example:
* CurlHolder holder;
* std::string input = "Hello World!";
* std::string result = holder.urlEncode(input);
**/
std::string urlEncode(const std::string& s) {
const CurlHolder holder; // Create a temporary new holder for URL encoding
return holder.urlEncode(s);
}
/**
* Creates a temporary CurlHolder object and uses it to unescape the given string.
* If you plan to use this methode on a regular basis think about creating a CurlHolder
* object and calling urlDecode(std::string) on it.
*
* Example:
* CurlHolder holder;
* std::string input = "Hello%20World%21";
* std::string result = holder.urlDecode(input);
**/
std::string urlDecode(const std::string& s) {
const CurlHolder holder; // Create a temporary new holder for URL decoding
return holder.urlDecode(s);
}
#if defined(__STDC_LIB_EXT1__)
void secureStringClear(std::string& s) {
if (s.empty()) {
return;
}
memset_s(&s.front(), s.length(), 0, s.length());
s.clear();
}
#elif defined(_WIN32)
void secureStringClear(std::string& s) {
if (s.empty()) {
return;
}
SecureZeroMemory(&s.front(), s.length());
s.clear();
}
#else
#if defined(__clang__)
#pragma clang optimize off // clang
#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__)
#pragma GCC push_options // g++
#pragma GCC optimize("O0") // g++
#endif
void secureStringClear(std::string& s) {
if (s.empty()) {
return;
}
// NOLINTNEXTLINE (readability-container-data-pointer)
char* ptr = &(s[0]);
memset(ptr, '\0', s.length());
s.clear();
}
#if defined(__clang__)
#pragma clang optimize on // clang
#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__)
#pragma GCC pop_options // g++
#endif
#endif
bool isTrue(const std::string& s) {
std::string temp_string{s};
std::transform(temp_string.begin(), temp_string.end(), temp_string.begin(), [](unsigned char c) { return std::tolower(c); });
return temp_string == "true";
}
} // namespace cpr::util

68
include/CMakeLists.txt Normal file
View file

@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.15)
target_include_directories(cpr PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/cpr_generated_includes/>)
target_sources(cpr PRIVATE
# Header files (useful in IDEs)
cpr/accept_encoding.h
cpr/api.h
cpr/async.h
cpr/async_wrapper.h
cpr/auth.h
cpr/bearer.h
cpr/body.h
cpr/buffer.h
cpr/cert_info.h
cpr/cookies.h
cpr/cpr.h
cpr/cprtypes.h
cpr/curlholder.h
cpr/curlholder.h
cpr/error.h
cpr/file.h
cpr/limit_rate.h
cpr/local_port.h
cpr/local_port_range.h
cpr/multipart.h
cpr/parameters.h
cpr/payload.h
cpr/proxies.h
cpr/proxyauth.h
cpr/response.h
cpr/session.h
cpr/singleton.h
cpr/ssl_ctx.h
cpr/ssl_options.h
cpr/threadpool.h
cpr/timeout.h
cpr/unix_socket.h
cpr/util.h
cpr/verbose.h
cpr/interface.h
cpr/redirect.h
cpr/http_version.h
cpr/interceptor.h
cpr/filesystem.h
cpr/curlmultiholder.h
cpr/multiperform.h
cpr/resolve.h
${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h
)
# Filesystem
if(CPR_USE_BOOST_FILESYSTEM)
find_package(Boost 1.44 REQUIRED COMPONENTS filesystem)
if(Boost_FOUND)
target_link_libraries(cpr PUBLIC Boost::filesystem)
endif()
endif()
if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT WIN32)) AND NOT CPR_USE_BOOST_FILESYSTEM)
target_link_libraries(cpr PUBLIC stdc++fs)
endif()
install(DIRECTORY cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

View file

@ -0,0 +1,41 @@
#ifndef CPR_ACCEPT_ENCODING_H
#define CPR_ACCEPT_ENCODING_H
#include <curl/curlver.h>
#include <initializer_list>
#include <map>
#include <string>
#include <unordered_set>
namespace cpr {
enum class AcceptEncodingMethods {
identity,
deflate,
zlib,
gzip,
disabled,
};
// NOLINTNEXTLINE(cert-err58-cpp)
static const std::map<AcceptEncodingMethods, std::string> AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}, {AcceptEncodingMethods::disabled, "disabled"}};
class AcceptEncoding {
public:
AcceptEncoding() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods);
// NOLINTNEXTLINE(google-explicit-constructor)
AcceptEncoding(const std::initializer_list<std::string>& methods);
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] const std::string getString() const;
[[nodiscard]] bool disabled() const;
private:
std::unordered_set<std::string> methods_;
};
} // namespace cpr
#endif

392
include/cpr/api.h Normal file
View file

@ -0,0 +1,392 @@
#ifndef CPR_API_H
#define CPR_API_H
#include <fstream>
#include <functional>
#include <future>
#include <string>
#include <utility>
#include "cpr/async.h"
#include "cpr/async_wrapper.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/cprtypes.h"
#include "cpr/multipart.h"
#include "cpr/multiperform.h"
#include "cpr/payload.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include <cpr/filesystem.h>
namespace cpr {
using AsyncResponse = AsyncWrapper<Response>;
namespace priv {
template <bool processed_header, typename CurrentType>
void set_option_internal(Session& session, CurrentType&& current_option) {
session.SetOption(std::forward<CurrentType>(current_option));
}
template <>
inline void set_option_internal<true, Header>(Session& session, Header&& current_option) {
// Header option was already provided -> Update previous header
session.UpdateHeader(std::forward<Header>(current_option));
}
template <bool processed_header, typename CurrentType, typename... Ts>
void set_option_internal(Session& session, CurrentType&& current_option, Ts&&... ts) {
set_option_internal<processed_header, CurrentType>(session, std::forward<CurrentType>(current_option));
if (std::is_same<CurrentType, Header>::value) {
set_option_internal<true, Ts...>(session, std::forward<Ts>(ts)...);
} else {
set_option_internal<processed_header, Ts...>(session, std::forward<Ts>(ts)...);
}
}
template <typename... Ts>
void set_option(Session& session, Ts&&... ts) {
set_option_internal<false, Ts...>(session, std::forward<Ts>(ts)...);
}
// Idea: https://stackoverflow.com/a/19060157
template <typename Tuple, std::size_t... I>
void apply_set_option_internal(Session& session, Tuple&& t, std::index_sequence<I...>) {
set_option(session, std::get<I>(std::forward<Tuple>(t))...);
}
// Idea: https://stackoverflow.com/a/19060157
template <typename Tuple>
void apply_set_option(Session& session, Tuple&& t) {
using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
apply_set_option_internal(session, std::forward<Tuple>(t), Indices());
}
template <typename T>
void setup_multiperform_internal(MultiPerform& multiperform, T&& t) {
std::shared_ptr<Session> session = std::make_shared<Session>();
apply_set_option(*session, t);
multiperform.AddSession(session);
}
template <typename T, typename... Ts>
void setup_multiperform_internal(MultiPerform& multiperform, T&& t, Ts&&... ts) {
std::shared_ptr<Session> session = std::make_shared<Session>();
apply_set_option(*session, t);
multiperform.AddSession(session);
setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);
}
template <typename... Ts>
void setup_multiperform(MultiPerform& multiperform, Ts&&... ts) {
setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);
}
using session_action_t = cpr::Response (cpr::Session::*)();
template <session_action_t SessionAction, typename T>
void setup_multiasync(std::vector<AsyncWrapper<Response, true>>& responses, T&& parameters) {
std::shared_ptr<std::atomic_bool> cancellation_state = std::make_shared<std::atomic_bool>(false);
std::function<Response(T)> execFn{[cancellation_state](T params) {
if (cancellation_state->load()) {
return Response{};
}
cpr::Session s{};
s.SetCancellationParam(cancellation_state);
apply_set_option(s, std::forward<T>(params));
return std::invoke(SessionAction, s);
}};
responses.emplace_back(GlobalThreadPool::GetInstance()->Submit(std::move(execFn), std::forward<T>(parameters)), std::move(cancellation_state));
}
template <session_action_t SessionAction, typename T, typename... Ts>
void setup_multiasync(std::vector<AsyncWrapper<Response, true>>& responses, T&& head, Ts&&... tail) {
setup_multiasync<SessionAction>(responses, std::forward<T>(head));
if constexpr (sizeof...(Ts) > 0) {
setup_multiasync<SessionAction>(responses, std::forward<Ts>(tail)...);
}
}
} // namespace priv
// Get methods
template <typename... Ts>
Response Get(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Get();
}
// Get async methods
template <typename... Ts>
AsyncResponse GetAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...);
}
// Get callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto GetCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Post methods
template <typename... Ts>
Response Post(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Post();
}
// Post async methods
template <typename... Ts>
AsyncResponse PostAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...);
}
// Post callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PostCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Put methods
template <typename... Ts>
Response Put(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Put();
}
// Put async methods
template <typename... Ts>
AsyncResponse PutAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...);
}
// Put callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PutCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Head methods
template <typename... Ts>
Response Head(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Head();
}
// Head async methods
template <typename... Ts>
AsyncResponse HeadAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...);
}
// Head callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto HeadCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Delete methods
template <typename... Ts>
Response Delete(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Delete();
}
// Delete async methods
template <typename... Ts>
AsyncResponse DeleteAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...);
}
// Delete callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto DeleteCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Options methods
template <typename... Ts>
Response Options(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Options();
}
// Options async methods
template <typename... Ts>
AsyncResponse OptionsAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...);
}
// Options callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto OptionsCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Patch methods
template <typename... Ts>
Response Patch(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Patch();
}
// Patch async methods
template <typename... Ts>
AsyncResponse PatchAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...);
}
// Patch callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PatchCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Download methods
template <typename... Ts>
Response Download(std::ofstream& file, Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Download(file);
}
// Download async method
template <typename... Ts>
AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) {
return AsyncWrapper{std::async(
std::launch::async,
[](fs::path local_path_, Ts... ts_) {
std::ofstream f(local_path_.c_str());
return Download(f, std::move(ts_)...);
},
std::move(local_path), std::move(ts)...)};
}
// Download with user callback
template <typename... Ts>
Response Download(const WriteCallback& write, Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Download(write);
}
// Multi requests
template <typename... Ts>
std::vector<Response> MultiGet(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Get();
}
template <typename... Ts>
std::vector<Response> MultiDelete(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Delete();
}
template <typename... Ts>
std::vector<Response> MultiPut(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Put();
}
template <typename... Ts>
std::vector<Response> MultiHead(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Head();
}
template <typename... Ts>
std::vector<Response> MultiOptions(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Options();
}
template <typename... Ts>
std::vector<Response> MultiPatch(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Patch();
}
template <typename... Ts>
std::vector<Response> MultiPost(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Post();
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiGetAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Get>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiDeleteAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Delete>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiHeadAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Head>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiOptionsAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Options>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiPatchAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Patch>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiPostAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Post>(ret, std::forward<Ts>(ts)...);
return ret;
}
template <typename... Ts>
std::vector<AsyncWrapper<Response, true>> MultiPutAsync(Ts&&... ts) {
std::vector<AsyncWrapper<Response, true>> ret{};
priv::setup_multiasync<&cpr::Session::Put>(ret, std::forward<Ts>(ts)...);
return ret;
}
} // namespace cpr
#endif

50
include/cpr/async.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef CPR_ASYNC_H
#define CPR_ASYNC_H
#include "async_wrapper.h"
#include "singleton.h"
#include "threadpool.h"
namespace cpr {
class GlobalThreadPool : public ThreadPool {
CPR_SINGLETON_DECL(GlobalThreadPool)
protected:
GlobalThreadPool() = default;
public:
~GlobalThreadPool() override = default;
};
/**
* Return a wrapper for a future, calling future.get() will wait until the task is done and return RetType.
* async(fn, args...)
* async(std::bind(&Class::mem_fn, &obj))
* async(std::mem_fn(&Class::mem_fn, &obj))
**/
template <class Fn, class... Args>
auto async(Fn&& fn, Args&&... args) {
return AsyncWrapper{GlobalThreadPool::GetInstance()->Submit(std::forward<Fn>(fn), std::forward<Args>(args)...)};
}
class async {
public:
static void startup(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {
GlobalThreadPool* gtp = GlobalThreadPool::GetInstance();
if (gtp->IsStarted()) {
return;
}
gtp->SetMinThreadNum(min_threads);
gtp->SetMaxThreadNum(max_threads);
gtp->SetMaxIdleTime(max_idle_ms);
gtp->Start();
}
static void cleanup() {
GlobalThreadPool::ExitInstance();
}
};
} // namespace cpr
#endif

140
include/cpr/async_wrapper.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef CPR_ASYNC_WRAPPER_H
#define CPR_ASYNC_WRAPPER_H
#include <atomic>
#include <future>
#include <memory>
#include "cpr/response.h"
namespace cpr {
enum class [[nodiscard]] CancellationResult { failure, success, invalid_operation };
/**
* A class template intended to wrap results of async operations (instances of std::future<T>)
* and also provide extended capablilities relaed to these requests, for example cancellation.
*
* The RAII semantics are the same as std::future<T> - moveable, not copyable.
*/
template <typename T, bool isCancellable = false>
class AsyncWrapper {
private:
std::future<T> future;
std::shared_ptr<std::atomic_bool> is_cancelled;
public:
// Constructors
explicit AsyncWrapper(std::future<T>&& f) : future{std::move(f)} {}
AsyncWrapper(std::future<T>&& f, std::shared_ptr<std::atomic_bool>&& cancelledState) : future{std::move(f)}, is_cancelled{std::move(cancelledState)} {}
// Copy Semantics
AsyncWrapper(const AsyncWrapper&) = delete;
AsyncWrapper& operator=(const AsyncWrapper&) = delete;
// Move Semantics
AsyncWrapper(AsyncWrapper&&) noexcept = default;
AsyncWrapper& operator=(AsyncWrapper&&) noexcept = default;
// Destructor
~AsyncWrapper() {
if constexpr (isCancellable) {
if(is_cancelled) {
is_cancelled->store(true);
}
}
}
// These methods replicate the behaviour of std::future<T>
[[nodiscard]] T get() {
if constexpr (isCancellable) {
if (IsCancelled()) {
throw std::logic_error{"Calling AsyncWrapper::get on a cancelled request!"};
}
}
if (!future.valid()) {
throw std::logic_error{"Calling AsyncWrapper::get when the associated future instance is invalid!"};
}
return future.get();
}
[[nodiscard]] bool valid() const noexcept {
if constexpr (isCancellable) {
return !is_cancelled->load() && future.valid();
} else {
return future.valid();
}
}
void wait() const {
if constexpr (isCancellable) {
if (is_cancelled->load()) {
throw std::logic_error{"Calling AsyncWrapper::wait when the associated future is invalid or cancelled!"};
}
}
if (!future.valid()) {
throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"};
}
future.wait();
}
template <class Rep, class Period>
std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
if constexpr (isCancellable) {
if (IsCancelled()) {
throw std::logic_error{"Calling AsyncWrapper::wait_for when the associated future is cancelled!"};
}
}
if (!future.valid()) {
throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"};
}
return future.wait_for(timeout_duration);
}
template <class Clock, class Duration>
std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
if constexpr (isCancellable) {
if (IsCancelled()) {
throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is cancelled!"};
}
}
if (!future.valid()) {
throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"};
}
return future.wait_until(timeout_time);
}
std::shared_future<T> share() noexcept {
return future.share();
}
// Cancellation-related methods
CancellationResult Cancel() {
if constexpr (!isCancellable) {
return CancellationResult::invalid_operation;
}
if (!future.valid() || is_cancelled->load()) {
return CancellationResult::invalid_operation;
}
is_cancelled->store(true);
return CancellationResult::success;
}
[[nodiscard]] bool IsCancelled() const {
if constexpr (isCancellable) {
return is_cancelled->load();
} else {
return false;
}
}
};
// Deduction guides
template <typename T>
AsyncWrapper(std::future<T>&&) -> AsyncWrapper<T, false>;
template <typename T>
AsyncWrapper(std::future<T>&&, std::shared_ptr<std::atomic_bool>&&) -> AsyncWrapper<T, true>;
} // namespace cpr
#endif

32
include/cpr/auth.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef CPR_AUTH_H
#define CPR_AUTH_H
#include <string>
#include <utility>
namespace cpr {
enum class AuthMode { BASIC, DIGEST, NTLM };
class Authentication {
public:
Authentication(std::string username, std::string password, AuthMode auth_mode) : auth_string_{std::move(username) + ":" + std::move(password)}, auth_mode_{std::move(auth_mode)} {}
Authentication(const Authentication& other) = default;
Authentication(Authentication&& old) noexcept = default;
~Authentication() noexcept;
Authentication& operator=(Authentication&& old) noexcept = default;
Authentication& operator=(const Authentication& other) = default;
const char* GetAuthString() const noexcept;
AuthMode GetAuthMode() const noexcept;
private:
std::string auth_string_;
AuthMode auth_mode_;
};
} // namespace cpr
#endif

34
include/cpr/bearer.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef CPR_BEARER_H
#define CPR_BEARER_H
#include <curl/curlver.h>
#include <string>
#include <utility>
namespace cpr {
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
class Bearer {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Bearer(std::string token) : token_string_{std::move(token)} {}
Bearer(const Bearer& other) = default;
Bearer(Bearer&& old) noexcept = default;
virtual ~Bearer() noexcept;
Bearer& operator=(Bearer&& old) noexcept = default;
Bearer& operator=(const Bearer& other) = default;
virtual const char* GetToken() const noexcept;
protected:
std::string token_string_;
};
#endif
} // namespace cpr
#endif

54
include/cpr/body.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef CPR_BODY_H
#define CPR_BODY_H
#include <exception>
#include <fstream>
#include <initializer_list>
#include <string>
#include <vector>
#include "cpr/buffer.h"
#include "cpr/cprtypes.h"
#include "cpr/file.h"
namespace cpr {
class Body : public StringHolder<Body> {
public:
Body() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(std::string body) : StringHolder<Body>(std::move(body)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(std::string_view body) : StringHolder<Body>(body) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(const char* body) : StringHolder<Body>(body) {}
Body(const char* str, size_t len) : StringHolder<Body>(str, len) {}
Body(const std::initializer_list<std::string> args) : StringHolder<Body>(args) {}
// NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-pro-type-reinterpret-cast)
Body(const Buffer& buffer) : StringHolder<Body>(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}
// NOLINTNEXTLINE(google-explicit-constructor)
Body(const File& file) {
std::ifstream is(file.filepath, std::ifstream::binary);
if (!is) {
throw std::invalid_argument("Can't open the file for HTTP request body!");
}
is.seekg(0, std::ios::end);
const std::streampos length = is.tellg();
is.seekg(0, std::ios::beg);
std::string buffer;
buffer.resize(static_cast<size_t>(length));
is.read(buffer.data(), length);
str_ = std::move(buffer);
}
Body(const Body& other) = default;
Body(Body&& old) noexcept = default;
~Body() override = default;
Body& operator=(Body&& old) noexcept = default;
Body& operator=(const Body& other) = default;
};
} // namespace cpr
#endif

33
include/cpr/buffer.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef CPR_BUFFER_H
#define CPR_BUFFER_H
#include <string>
#include <cpr/filesystem.h>
namespace cpr {
struct Buffer {
using data_t = const char*;
template <typename Iterator>
Buffer(Iterator begin, Iterator end, fs::path&& p_filename)
// Ignored here since libcurl reqires a long.
// There is also no way around the reinterpret_cast.
// NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast)
: data{reinterpret_cast<data_t>(&(*begin))}, datalen{static_cast<size_t>(std::distance(begin, end))}, filename(std::move(p_filename)) {
is_random_access_iterator(begin, end);
static_assert(sizeof(*begin) == 1, "Only byte buffers can be used");
}
template <typename Iterator>
typename std::enable_if<std::is_same<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>::value>::type is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {}
data_t data;
size_t datalen;
const fs::path filename;
};
} // namespace cpr
#endif

111
include/cpr/callback.h Normal file
View file

@ -0,0 +1,111 @@
#ifndef CPR_CALLBACK_H
#define CPR_CALLBACK_H
#include "cprtypes.h"
#include <atomic>
#include <functional>
#include <optional>
#include <utility>
namespace cpr {
class ReadCallback {
public:
ReadCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ReadCallback(std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {}
ReadCallback(cpr_off_t p_size, std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{p_size}, callback{std::move(p_callback)} {}
bool operator()(char* buffer, size_t& buffer_size) const {
return callback(buffer, buffer_size, userdata);
}
intptr_t userdata{};
cpr_off_t size{};
std::function<bool(char* buffer, size_t& size, intptr_t userdata)> callback;
};
class HeaderCallback {
public:
HeaderCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
HeaderCallback(std::function<bool(std::string header, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(std::string header) const {
return callback(std::move(header), userdata);
}
intptr_t userdata{};
std::function<bool(std::string header, intptr_t userdata)> callback;
};
class WriteCallback {
public:
WriteCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
WriteCallback(std::function<bool(std::string data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(std::string data) const {
return callback(std::move(data), userdata);
}
intptr_t userdata{};
std::function<bool(std::string data, intptr_t userdata)> callback;
};
class ProgressCallback {
public:
ProgressCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ProgressCallback(std::function<bool(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow) const {
return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata);
}
intptr_t userdata{};
std::function<bool(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow, intptr_t userdata)> callback;
};
class DebugCallback {
public:
enum class InfoType {
TEXT = 0,
HEADER_IN = 1,
HEADER_OUT = 2,
DATA_IN = 3,
DATA_OUT = 4,
SSL_DATA_IN = 5,
SSL_DATA_OUT = 6,
};
DebugCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DebugCallback(std::function<void(InfoType type, std::string data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
void operator()(InfoType type, std::string data) const {
callback(type, std::move(data), userdata);
}
intptr_t userdata{};
std::function<void(InfoType type, std::string data, intptr_t userdata)> callback;
};
/**
* Functor class for progress functions that will be used in cancellable requests.
*/
class CancellationCallback {
public:
CancellationCallback() = default;
explicit CancellationCallback(std::shared_ptr<std::atomic_bool>&& cs) : cancellation_state{std::move(cs)} {}
CancellationCallback(std::shared_ptr<std::atomic_bool>&& cs, ProgressCallback& u_cb) : cancellation_state{std::move(cs)}, user_cb{std::reference_wrapper{u_cb}} {}
bool operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const;
void SetProgressCallback(ProgressCallback& u_cb);
private:
std::shared_ptr<std::atomic_bool> cancellation_state;
std::optional<std::reference_wrapper<ProgressCallback>> user_cb;
};
} // namespace cpr
#endif

35
include/cpr/cert_info.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef CPR_CERT_INFO_H
#define CPR_CERT_INFO_H
#include <initializer_list>
#include <string>
#include <vector>
namespace cpr {
class CertInfo {
private:
std::vector<std::string> cert_info_;
public:
CertInfo() = default;
CertInfo(const std::initializer_list<std::string>& entry) : cert_info_{entry} {};
~CertInfo() noexcept = default;
using iterator = std::vector<std::string>::iterator;
using const_iterator = std::vector<std::string>::const_iterator;
std::string& operator[](const size_t& pos);
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
void emplace_back(const std::string& str);
void push_back(const std::string& str);
void pop_back();
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_CONNECT_TIMEOUT_H
#define CPR_CONNECT_TIMEOUT_H
#include "cpr/timeout.h"
namespace cpr {
class ConnectTimeout : public Timeout {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ConnectTimeout(const std::chrono::milliseconds& duration) : Timeout{duration} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ConnectTimeout(const std::int32_t& milliseconds) : Timeout{milliseconds} {}
};
} // namespace cpr
#endif

92
include/cpr/cookies.h Normal file
View file

@ -0,0 +1,92 @@
#ifndef CPR_COOKIES_H
#define CPR_COOKIES_H
#include "cpr/curlholder.h"
#include <chrono>
#include <initializer_list>
#include <sstream>
#include <string>
#include <vector>
namespace cpr {
/**
* EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable.
* To be used for "std::array", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value.
**/
static const std::size_t EXPIRES_STRING_SIZE = 100;
class Cookie {
public:
Cookie() = default;
/**
* Some notes for the default value used by expires:
* std::chrono::system_clock::time_point::min() won't work on Windows due to the min, max clash there.
* So we fall back to std::chrono::system_clock::from_time_t(0) for the minimum value here.
**/
Cookie(const std::string& name, const std::string& value, const std::string& domain = "", bool p_isIncludingSubdomains = false, const std::string& path = "/", bool p_isHttpsOnly = false, std::chrono::system_clock::time_point expires = std::chrono::system_clock::from_time_t(0)) : name_{name}, value_{value}, domain_{domain}, includeSubdomains_{p_isIncludingSubdomains}, path_{path}, httpsOnly_{p_isHttpsOnly}, expires_{expires} {};
const std::string GetDomain() const;
bool IsIncludingSubdomains() const;
const std::string GetPath() const;
bool IsHttpsOnly() const;
const std::chrono::system_clock::time_point GetExpires() const;
const std::string GetExpiresString() const;
const std::string GetName() const;
const std::string GetValue() const;
private:
std::string name_;
std::string value_;
std::string domain_;
bool includeSubdomains_{};
std::string path_;
bool httpsOnly_{};
/**
* TODO: Update the implementation using `std::chrono::utc_clock` of C++20
**/
std::chrono::system_clock::time_point expires_{};
};
class Cookies {
public:
/**
* Should we URL-encode cookies when making a request.
* Based on RFC6265, it is recommended but not mandatory to encode cookies.
*
* -------
* To maximize compatibility with user agents, servers that wish to
* store arbitrary data in a cookie-value SHOULD encode that data, for
* example, using Base64 [RFC4648].
* -------
* Source: RFC6265 (https://www.ietf.org/rfc/rfc6265.txt)
**/
bool encode{true};
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Cookies(bool p_encode = true) : encode{p_encode} {};
Cookies(const std::initializer_list<cpr::Cookie>& cookies, bool p_encode = true) : encode{p_encode}, cookies_{cookies} {};
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Cookies(const cpr::Cookie& cookie, bool p_encode = true) : encode{p_encode}, cookies_{cookie} {};
cpr::Cookie& operator[](size_t pos);
const std::string GetEncoded(const CurlHolder& holder) const;
using iterator = std::vector<cpr::Cookie>::iterator;
using const_iterator = std::vector<cpr::Cookie>::const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
void emplace_back(const Cookie& str);
void push_back(const Cookie& str);
void pop_back();
private:
std::vector<cpr::Cookie> cookies_;
};
} // namespace cpr
#endif

46
include/cpr/cpr.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef CPR_CPR_H
#define CPR_CPR_H
#include "cpr/api.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/callback.h"
#include "cpr/cert_info.h"
#include "cpr/connect_timeout.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/cprver.h"
#include "cpr/curl_container.h"
#include "cpr/curlholder.h"
#include "cpr/error.h"
#include "cpr/http_version.h"
#include "cpr/interceptor.h"
#include "cpr/interface.h"
#include "cpr/limit_rate.h"
#include "cpr/local_port.h"
#include "cpr/local_port_range.h"
#include "cpr/low_speed.h"
#include "cpr/multipart.h"
#include "cpr/multiperform.h"
#include "cpr/parameters.h"
#include "cpr/payload.h"
#include "cpr/proxies.h"
#include "cpr/proxyauth.h"
#include "cpr/range.h"
#include "cpr/redirect.h"
#include "cpr/reserve_size.h"
#include "cpr/resolve.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include "cpr/ssl_ctx.h"
#include "cpr/ssl_options.h"
#include "cpr/status_codes.h"
#include "cpr/timeout.h"
#include "cpr/unix_socket.h"
#include "cpr/user_agent.h"
#include "cpr/util.h"
#include "cpr/verbose.h"
#define CPR_LIBCURL_VERSION_NUM LIBCURL_VERSION_NUM
#endif

144
include/cpr/cprtypes.h Normal file
View file

@ -0,0 +1,144 @@
#ifndef CPR_CPR_TYPES_H
#define CPR_CPR_TYPES_H
#include <curl/curl.h>
#include <curl/system.h>
#include <initializer_list>
#include <map>
#include <memory>
#include <numeric>
#include <string>
#include <string_view>
namespace cpr {
/**
* Wrapper around "curl_off_t" to prevent applications from having to link against libcurl.
**/
using cpr_off_t = curl_off_t;
/**
* The argument type for progress functions, dependent on libcurl version
**/
#if LIBCURL_VERSION_NUM < 0x072000
using cpr_pf_arg_t = double;
#else
using cpr_pf_arg_t = cpr_off_t;
#endif
template <class T>
class StringHolder {
public:
StringHolder() = default;
explicit StringHolder(std::string str) : str_(std::move(str)) {}
explicit StringHolder(std::string_view str) : str_(str) {}
explicit StringHolder(const char* str) : str_(str) {}
StringHolder(const char* str, size_t len) : str_(str, len) {}
StringHolder(const std::initializer_list<std::string> args) {
str_ = std::accumulate(args.begin(), args.end(), str_);
}
StringHolder(const StringHolder& other) = default;
StringHolder(StringHolder&& old) noexcept = default;
virtual ~StringHolder() = default;
StringHolder& operator=(StringHolder&& old) noexcept = default;
StringHolder& operator=(const StringHolder& other) = default;
explicit operator std::string() const {
return str_;
}
T operator+(const char* rhs) const {
return T(str_ + rhs);
}
T operator+(const std::string& rhs) const {
return T(str_ + rhs);
}
T operator+(const StringHolder<T>& rhs) const {
return T(str_ + rhs.str_);
}
void operator+=(const char* rhs) {
str_ += rhs;
}
void operator+=(const std::string& rhs) {
str_ += rhs;
}
void operator+=(const StringHolder<T>& rhs) {
str_ += rhs;
}
bool operator==(const char* rhs) const {
return str_ == rhs;
}
bool operator==(const std::string& rhs) const {
return str_ == rhs;
}
bool operator==(const StringHolder<T>& rhs) const {
return str_ == rhs.str_;
}
bool operator!=(const char* rhs) const {
return str_.c_str() != rhs;
}
bool operator!=(const std::string& rhs) const {
return str_ != rhs;
}
bool operator!=(const StringHolder<T>& rhs) const {
return str_ != rhs.str_;
}
const std::string& str() {
return str_;
}
const std::string& str() const {
return str_;
}
const char* c_str() const {
return str_.c_str();
}
const char* data() const {
return str_.data();
}
protected:
std::string str_{};
};
template <class T>
std::ostream& operator<<(std::ostream& os, const StringHolder<T>& s) {
os << s.str();
return os;
}
class Url : public StringHolder<Url> {
public:
Url() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(std::string url) : StringHolder<Url>(std::move(url)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(std::string_view url) : StringHolder<Url>(url) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(const char* url) : StringHolder<Url>(url) {}
Url(const char* str, size_t len) : StringHolder<Url>(std::string(str, len)) {}
Url(const std::initializer_list<std::string> args) : StringHolder<Url>(args) {}
Url(const Url& other) = default;
Url(Url&& old) noexcept = default;
~Url() override = default;
Url& operator=(Url&& old) noexcept = default;
Url& operator=(const Url& other) = default;
};
struct CaseInsensitiveCompare {
bool operator()(const std::string& a, const std::string& b) const noexcept;
};
using Header = std::map<std::string, std::string, CaseInsensitiveCompare>;
} // namespace cpr
#endif

View file

@ -0,0 +1,51 @@
#ifndef CURL_CONTAINER_H
#define CURL_CONTAINER_H
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "cpr/curlholder.h"
namespace cpr {
struct Parameter {
Parameter(std::string p_key, std::string p_value) : key{std::move(p_key)}, value{std::move(p_value)} {}
std::string key;
std::string value;
};
struct Pair {
Pair(std::string p_key, std::string p_value) : key(std::move(p_key)), value(std::move(p_value)) {}
std::string key;
std::string value;
};
template <class T>
class CurlContainer {
public:
/**
* Enables or disables URL encoding for keys and values when calling GetContent(...).
**/
bool encode = true;
CurlContainer() = default;
CurlContainer(const std::initializer_list<T>&);
void Add(const std::initializer_list<T>&);
void Add(const T&);
const std::string GetContent(const CurlHolder&) const;
protected:
std::vector<T> containerList_;
};
} // namespace cpr
#endif //

54
include/cpr/curlholder.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef CPR_CURL_HOLDER_H
#define CPR_CURL_HOLDER_H
#include <array>
#include <mutex>
#include <string>
#include <curl/curl.h>
namespace cpr {
struct CurlHolder {
private:
/**
* Mutex for curl_easy_init().
* curl_easy_init() is not thread save.
* References:
* https://curl.haxx.se/libcurl/c/curl_easy_init.html
* https://curl.haxx.se/libcurl/c/threadsafe.html
**/
// Avoids initalization order problems in a static build
static std::mutex& curl_easy_init_mutex_() {
static std::mutex curl_easy_init_mutex_;
return curl_easy_init_mutex_;
}
public:
CURL* handle{nullptr};
struct curl_slist* chunk{nullptr};
struct curl_slist* resolveCurlList{nullptr};
curl_mime* multipart{nullptr};
std::array<char, CURL_ERROR_SIZE> error{};
CurlHolder();
CurlHolder(const CurlHolder& other) = default;
CurlHolder(CurlHolder&& old) noexcept = default;
~CurlHolder();
CurlHolder& operator=(CurlHolder&& old) noexcept = default;
CurlHolder& operator=(const CurlHolder& other) = default;
/**
* Uses curl_easy_escape(...) for escaping the given string.
**/
std::string urlEncode(const std::string& s) const;
/**
* Uses curl_easy_unescape(...) for unescaping the given string.
**/
std::string urlDecode(const std::string& s) const;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_CURLMULTIHOLDER_H
#define CPR_CURLMULTIHOLDER_H
#include <curl/curl.h>
namespace cpr {
class CurlMultiHolder {
public:
CurlMultiHolder();
~CurlMultiHolder();
CURLM* handle{nullptr};
};
} // namespace cpr
#endif

53
include/cpr/error.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef CPR_ERROR_H
#define CPR_ERROR_H
#include <cstdint>
#include <string>
#include "cpr/cprtypes.h"
#include <utility>
namespace cpr {
enum class ErrorCode {
OK = 0,
CONNECTION_FAILURE,
EMPTY_RESPONSE,
HOST_RESOLUTION_FAILURE,
INTERNAL_ERROR,
INVALID_URL_FORMAT,
NETWORK_RECEIVE_ERROR,
NETWORK_SEND_FAILURE,
OPERATION_TIMEDOUT,
PROXY_RESOLUTION_FAILURE,
SSL_CONNECT_ERROR,
SSL_LOCAL_CERTIFICATE_ERROR,
SSL_REMOTE_CERTIFICATE_ERROR,
SSL_CACERT_ERROR,
GENERIC_SSL_ERROR,
UNSUPPORTED_PROTOCOL,
REQUEST_CANCELLED,
TOO_MANY_REDIRECTS,
UNKNOWN_ERROR = 1000,
};
class Error {
public:
ErrorCode code = ErrorCode::OK;
std::string message{};
Error() = default;
Error(const std::int32_t& curl_code, std::string&& p_error_message) : code{getErrorCodeForCurlError(curl_code)}, message(std::move(p_error_message)) {}
explicit operator bool() const {
return code != ErrorCode::OK;
}
private:
static ErrorCode getErrorCodeForCurlError(std::int32_t curl_code);
};
} // namespace cpr
#endif

59
include/cpr/file.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef CPR_FILE_H
#define CPR_FILE_H
#include <initializer_list>
#include <string>
#include <vector>
#include <cpr/filesystem.h>
namespace cpr {
struct File {
explicit File(std::string p_filepath, const std::string& p_overriden_filename = {}) : filepath(std::move(p_filepath)), overriden_filename(p_overriden_filename) {}
std::string filepath;
std::string overriden_filename;
[[nodiscard]] bool hasOverridenFilename() const noexcept {
return !overriden_filename.empty();
};
};
class Files {
public:
Files() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
Files(const File& p_file) : files{p_file} {};
Files(const Files& other) = default;
Files(Files&& old) noexcept = default;
Files(const std::initializer_list<File>& p_files) : files{p_files} {};
Files(const std::initializer_list<std::string>& p_filepaths);
~Files() noexcept = default;
Files& operator=(const Files& other);
Files& operator=(Files&& old) noexcept;
using iterator = std::vector<File>::iterator;
using const_iterator = std::vector<File>::const_iterator;
iterator begin();
iterator end();
[[nodiscard]] const_iterator begin() const;
[[nodiscard]] const_iterator end() const;
[[nodiscard]] const_iterator cbegin() const;
[[nodiscard]] const_iterator cend() const;
void emplace_back(const File& file);
void push_back(const File& file);
void pop_back();
private:
std::vector<File> files;
};
} // namespace cpr
#endif

26
include/cpr/filesystem.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef CPR_FILESYSTEM_H
#define CPR_FILESYSTEM_H
// Include filesystem into the namespace "fs" from either "filesystem" or "experimental/filesystem" or "boost/filesystem"
#ifdef CPR_USE_BOOST_FILESYSTEM
#define BOOST_FILESYSTEM_VERSION 4 // Use the latest, with the closest behavior to std::filesystem.
#include <boost/filesystem.hpp>
namespace cpr {
namespace fs = boost::filesystem;
}
// cppcheck-suppress preprocessorErrorDirective
#elif __has_include(<filesystem>)
#include <filesystem>
namespace cpr {
namespace fs = std::filesystem;
}
#elif __has_include("experimental/filesystem")
#include <experimental/filesystem>
namespace cpr {
namespace fs = std::experimental::filesystem;
}
#else
#error "Failed to include <filesystem> header!"
#endif
#endif

View file

@ -0,0 +1,67 @@
#ifndef CPR_HTTP_VERSION_H
#define CPR_HTTP_VERSION_H
#include <curl/curlver.h>
namespace cpr {
enum class HttpVersionCode {
/**
* Let libcurl decide which version is the best.
**/
VERSION_NONE,
/**
* Enforce HTTP 1.0 requests.
**/
VERSION_1_0,
/**
* Enforce HTTP 1.1 requests.
**/
VERSION_1_1,
#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0
/**
* Attempt HTTP 2.0 requests.
* Fallback to HTTP 1.1 if negotiation fails.
**/
VERSION_2_0,
#endif
#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0
/**
* Attempt HTTP 2.0 for HTTPS requests only.
* Fallback to HTTP 1.1 if negotiation fails.
* HTTP 1.1 will be used for HTTP connections.
**/
VERSION_2_0_TLS,
#endif
#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0
/**
* Start HTTP 2.0 for HTTP requests.
* Requires prior knowledge that the server supports HTTP 2.0.
* For HTTPS requests we will negotiate the protocol version in the TLS handshake.
**/
VERSION_2_0_PRIOR_KNOWLEDGE,
#endif
#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0
/**
* Attempt HTTP 3.0 requests.
* Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade.
* Fallback to HTTP 1.1 if negotiation fails.
**/
VERSION_3_0
#endif
};
class HttpVersion {
public:
/**
* The HTTP version that should be used by libcurl when initiating a HTTP(S) connection.
* Default: HttpVersionCode::VERSION_NONE
**/
HttpVersionCode code = HttpVersionCode::VERSION_NONE;
HttpVersion() = default;
explicit HttpVersion(HttpVersionCode _code) : code(_code) {}
};
} // namespace cpr
#endif

74
include/cpr/interceptor.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef CPR_INTERCEPTOR_H
#define CPR_INTERCEPTOR_H
#include "cpr/multiperform.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include <vector>
namespace cpr {
class Interceptor {
public:
enum class ProceedHttpMethod {
GET_REQUEST = 0,
POST_REQUEST,
PUT_REQUEST,
DELETE_REQUEST,
PATCH_REQUEST,
HEAD_REQUEST,
OPTIONS_REQUEST,
DOWNLOAD_CALLBACK_REQUEST,
DOWNLOAD_FILE_REQUEST,
};
Interceptor() = default;
Interceptor(const Interceptor& other) = default;
Interceptor(Interceptor&& old) = default;
virtual ~Interceptor() = default;
Interceptor& operator=(const Interceptor& other) = default;
Interceptor& operator=(Interceptor&& old) = default;
virtual Response intercept(Session& session) = 0;
protected:
static Response proceed(Session& session);
static Response proceed(Session& session, ProceedHttpMethod httpMethod);
static Response proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file);
static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write);
};
class InterceptorMulti {
public:
enum class ProceedHttpMethod {
GET_REQUEST = 0,
POST_REQUEST,
PUT_REQUEST,
DELETE_REQUEST,
PATCH_REQUEST,
HEAD_REQUEST,
OPTIONS_REQUEST,
DOWNLOAD_CALLBACK_REQUEST,
DOWNLOAD_FILE_REQUEST,
};
InterceptorMulti() = default;
InterceptorMulti(const InterceptorMulti& other) = default;
InterceptorMulti(InterceptorMulti&& old) = default;
virtual ~InterceptorMulti() = default;
InterceptorMulti& operator=(const InterceptorMulti& other) = default;
InterceptorMulti& operator=(InterceptorMulti&& old) = default;
virtual std::vector<Response> intercept(MultiPerform& multi) = 0;
protected:
static std::vector<Response> proceed(MultiPerform& multi);
static void PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write);
};
} // namespace cpr
#endif

32
include/cpr/interface.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef CPR_INTERFACE_H
#define CPR_INTERFACE_H
#include <initializer_list>
#include <string>
#include "cpr/cprtypes.h"
namespace cpr {
class Interface : public StringHolder<Interface> {
public:
Interface() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(std::string iface) : StringHolder<Interface>(std::move(iface)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(std::string_view iface) : StringHolder<Interface>(iface) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(const char* iface) : StringHolder<Interface>(iface) {}
Interface(const char* str, size_t len) : StringHolder<Interface>(str, len) {}
Interface(const std::initializer_list<std::string> args) : StringHolder<Interface>(args) {}
Interface(const Interface& other) = default;
Interface(Interface&& old) noexcept = default;
~Interface() override = default;
Interface& operator=(Interface&& old) noexcept = default;
Interface& operator=(const Interface& other) = default;
};
} // namespace cpr
#endif

18
include/cpr/limit_rate.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef CPR_SPEED_LIMIT_H
#define CPR_SPEED_LIMIT_H
#include <cstdint>
namespace cpr {
class LimitRate {
public:
LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {}
std::int64_t downrate = 0;
std::int64_t uprate = 0;
};
} // namespace cpr
#endif

23
include/cpr/local_port.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef CPR_LOCAL_PORT_H
#define CPR_LOCAL_PORT_H
#include <cstdint>
namespace cpr {
class LocalPort {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {}
operator std::uint16_t() const {
return localport_;
}
private:
std::uint16_t localport_ = 0;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_LOCAL_PORT_RANGE_H
#define CPR_LOCAL_PORT_RANGE_H
#include <cstdint>
namespace cpr {
class LocalPortRange {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {}
operator std::uint16_t() const {
return localportrange_;
}
private:
std::uint16_t localportrange_ = 0;
};
} // namespace cpr
#endif

18
include/cpr/low_speed.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef CPR_LOW_SPEED_H
#define CPR_LOW_SPEED_H
#include <cstdint>
namespace cpr {
class LowSpeed {
public:
LowSpeed(const std::int32_t p_limit, const std::int32_t p_time) : limit(p_limit), time(p_time) {}
std::int32_t limit;
std::int32_t time;
};
} // namespace cpr
#endif

43
include/cpr/multipart.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef CPR_MULTIPART_H
#define CPR_MULTIPART_H
#include <cstdint>
#include <initializer_list>
#include <string>
#include <type_traits>
#include <vector>
#include "buffer.h"
#include "file.h"
namespace cpr {
struct Part {
Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) : name{p_name}, value{p_value}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}
Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) : name{p_name}, value{std::to_string(p_value)}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}
Part(const std::string& p_name, const Files& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {}
Part(const std::string& p_name, Files&& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {}
Part(const std::string& p_name, const Buffer& buffer, const std::string& p_content_type = {}) : name{p_name}, value{buffer.filename.string()}, content_type{p_content_type}, data{buffer.data}, datalen{buffer.datalen}, is_file{false}, is_buffer{true} {}
std::string name;
// We don't use fs::path here, as this leads to problems using windows
std::string value;
std::string content_type;
Buffer::data_t data{nullptr};
size_t datalen{0};
bool is_file;
bool is_buffer;
Files files;
};
class Multipart {
public:
Multipart(const std::initializer_list<Part>& parts);
std::vector<Part> parts;
};
} // namespace cpr
#endif

137
include/cpr/multiperform.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef CPR_MULTIPERFORM_H
#define CPR_MULTIPERFORM_H
#include "cpr/curlmultiholder.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include <functional>
#include <memory>
#include <queue>
#include <stdexcept>
#include <vector>
namespace cpr {
class InterceptorMulti;
class MultiPerform {
public:
enum class HttpMethod {
UNDEFINED = 0,
GET_REQUEST,
POST_REQUEST,
PUT_REQUEST,
DELETE_REQUEST,
PATCH_REQUEST,
HEAD_REQUEST,
OPTIONS_REQUEST,
DOWNLOAD_REQUEST,
};
MultiPerform();
MultiPerform(const MultiPerform& other) = delete;
MultiPerform(MultiPerform&& old) = default;
~MultiPerform();
MultiPerform& operator=(const MultiPerform& other) = delete;
MultiPerform& operator=(MultiPerform&& old) noexcept = default;
std::vector<Response> Get();
std::vector<Response> Delete();
template <typename... DownloadArgTypes>
std::vector<Response> Download(DownloadArgTypes... args);
std::vector<Response> Put();
std::vector<Response> Head();
std::vector<Response> Options();
std::vector<Response> Patch();
std::vector<Response> Post();
std::vector<Response> Perform();
template <typename... DownloadArgTypes>
std::vector<Response> PerformDownload(DownloadArgTypes... args);
void AddSession(std::shared_ptr<Session>& session, HttpMethod method = HttpMethod::UNDEFINED);
void RemoveSession(const std::shared_ptr<Session>& session);
std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>>& GetSessions();
[[nodiscard]] const std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>>& GetSessions() const;
void AddInterceptor(const std::shared_ptr<InterceptorMulti>& pinterceptor);
private:
// Interceptors should be able to call the private proceed() and PrepareDownloadSessions() functions
friend InterceptorMulti;
void SetHttpMethod(HttpMethod method);
void PrepareSessions();
template <typename CurrentDownloadArgType, typename... DownloadArgTypes>
void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args);
template <typename CurrentDownloadArgType>
void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg);
void PrepareDownloadSession(size_t sessions_index, std::ofstream& file);
void PrepareDownloadSession(size_t sessions_index, const WriteCallback& write);
void PrepareGet();
void PrepareDelete();
void PreparePut();
void PreparePatch();
void PrepareHead();
void PrepareOptions();
void PreparePost();
template <typename... DownloadArgTypes>
void PrepareDownload(DownloadArgTypes... args);
std::vector<Response> intercept();
std::vector<Response> proceed();
std::vector<Response> MakeRequest();
std::vector<Response> MakeDownloadRequest();
void DoMultiPerform();
std::vector<Response> ReadMultiInfo(std::function<Response(Session&, CURLcode)>&& complete_function);
std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>> sessions_;
std::unique_ptr<CurlMultiHolder> multicurl_;
bool is_download_multi_perform{false};
std::queue<std::shared_ptr<InterceptorMulti>> interceptors_;
};
template <typename CurrentDownloadArgType>
void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg) {
PrepareDownloadSession(sessions_index, current_arg);
}
template <typename CurrentDownloadArgType, typename... DownloadArgTypes>
void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args) {
PrepareDownloadSession(sessions_index, current_arg);
PrepareDownloadSessions<DownloadArgTypes...>(sessions_index + 1, args...);
}
template <typename... DownloadArgTypes>
void MultiPerform::PrepareDownload(DownloadArgTypes... args) {
SetHttpMethod(HttpMethod::DOWNLOAD_REQUEST);
PrepareDownloadSessions<DownloadArgTypes...>(0, args...);
}
template <typename... DownloadArgTypes>
std::vector<Response> MultiPerform::Download(DownloadArgTypes... args) {
if (sizeof...(args) != sessions_.size()) {
throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!");
}
PrepareDownload(args...);
return MakeDownloadRequest();
}
template <typename... DownloadArgTypes>
std::vector<Response> MultiPerform::PerformDownload(DownloadArgTypes... args) {
if (sizeof...(args) != sessions_.size()) {
throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!");
}
PrepareDownloadSessions<DownloadArgTypes...>(0, args...);
return MakeDownloadRequest();
}
} // namespace cpr
#endif

18
include/cpr/parameters.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef CPR_PARAMETERS_H
#define CPR_PARAMETERS_H
#include <initializer_list>
#include "cpr/curl_container.h"
namespace cpr {
class Parameters : public CurlContainer<Parameter> {
public:
Parameters() = default;
Parameters(const std::initializer_list<Parameter>& parameters);
};
} // namespace cpr
#endif

23
include/cpr/payload.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef CPR_PAYLOAD_H
#define CPR_PAYLOAD_H
#include <initializer_list>
#include "cpr/curl_container.h"
namespace cpr {
class Payload : public CurlContainer<Pair> {
public:
template <class It>
Payload(const It begin, const It end) {
for (It pair = begin; pair != end; ++pair) {
Add(*pair);
}
}
Payload(const std::initializer_list<Pair>& pairs);
};
} // namespace cpr
#endif

23
include/cpr/proxies.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef CPR_PROXIES_H
#define CPR_PROXIES_H
#include <initializer_list>
#include <map>
#include <string>
namespace cpr {
class Proxies {
public:
Proxies() = default;
Proxies(const std::initializer_list<std::pair<const std::string, std::string>>& hosts);
Proxies(const std::map<std::string, std::string>& hosts);
bool has(const std::string& protocol) const;
const std::string& operator[](const std::string& protocol);
private:
std::map<std::string, std::string> hosts_;
};
} // namespace cpr
#endif

43
include/cpr/proxyauth.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef CPR_PROXYAUTH_H
#define CPR_PROXYAUTH_H
#include <initializer_list>
#include <map>
#include "cpr/auth.h"
#include "cpr/util.h"
namespace cpr {
class EncodedAuthentication {
public:
EncodedAuthentication() : auth_string_{""} {}
EncodedAuthentication(std::string username, std::string password) : auth_string_{cpr::util::urlEncode(std::move(username)) + ":" + cpr::util::urlEncode(std::move(password))} {}
EncodedAuthentication(const EncodedAuthentication& other) = default;
EncodedAuthentication(EncodedAuthentication&& old) noexcept = default;
virtual ~EncodedAuthentication() noexcept;
EncodedAuthentication& operator=(EncodedAuthentication&& old) noexcept = default;
EncodedAuthentication& operator=(const EncodedAuthentication& other) = default;
const char* GetAuthString() const noexcept;
protected:
std::string auth_string_;
};
class ProxyAuthentication {
public:
ProxyAuthentication() = default;
ProxyAuthentication(const std::initializer_list<std::pair<const std::string, EncodedAuthentication>>& auths) : proxyAuth_{auths} {}
ProxyAuthentication(const std::map<std::string, EncodedAuthentication>& auths) : proxyAuth_{auths} {}
bool has(const std::string& protocol) const;
const char* operator[](const std::string& protocol);
private:
std::map<std::string, EncodedAuthentication> proxyAuth_;
};
} // namespace cpr
#endif

44
include/cpr/range.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef CPR_RANGE_H
#define CPR_RANGE_H
#include <cstdint>
#include <optional>
namespace cpr {
class Range {
public:
Range(const std::optional<std::int64_t> p_resume_from = std::nullopt, const std::optional<std::int64_t> p_finish_at = std::nullopt) {
resume_from = p_resume_from.value_or(0);
finish_at = p_finish_at.value_or(-1);
}
std::int64_t resume_from;
std::int64_t finish_at;
const std::string str() const {
std::string from_str = (resume_from < 0U) ? "" : std::to_string(resume_from);
std::string to_str = (finish_at < 0U) ? "" : std::to_string(finish_at);
return from_str + "-" + to_str;
}
};
class MultiRange {
public:
MultiRange(std::initializer_list<Range> rs) : ranges{rs} {}
const std::string str() const {
std::string multi_range_string{};
for (Range range : ranges) {
multi_range_string += ((multi_range_string.empty()) ? "" : ", ") + range.str();
}
return multi_range_string;
}
private:
std::vector<Range> ranges;
}; // namespace cpr
} // namespace cpr
#endif

84
include/cpr/redirect.h Normal file
View file

@ -0,0 +1,84 @@
#ifndef CPR_REDIRECT_H
#define CPR_REDIRECT_H
#include <cstdint>
namespace cpr {
enum class PostRedirectFlags : uint8_t {
/**
* Respect RFC 7231 (section 6.4.2 to 6.4.4).
* Same as CURL_REDIR_POST_301 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_301 = 0x1 << 0,
/**
* Maintain the request method after a 302 redirect.
* Same as CURL_REDIR_POST_302 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_302 = 0x1 << 1,
/**
* Maintain the request method after a 303 redirect.
* Same as CURL_REDIR_POST_303 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_303 = 0x1 << 2,
/**
* Default value.
* Convenience option to enable all flags.
* Same as CURL_REDIR_POST_ALL (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_ALL = POST_301 | POST_302 | POST_303,
/**
* * Convenience option to disable all flags.
**/
NONE = 0x0
};
PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator~(PostRedirectFlags flag);
PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
bool any(PostRedirectFlags flag);
class Redirect {
public:
/**
* The maximum number of redirects to follow.
* 0: Refuse any redirects.
* -1: Infinite number of redirects.
* Default: 50
* https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html
**/
// NOLINTNEXTLINE (google-runtime-int)
long maximum{50L};
/**
* Follow 3xx redirects.
* Default: true
* https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html
**/
bool follow{true};
/**
* Continue to send authentication (user+password) credentials when following locations, even when hostname changed.
* Default: false
* https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html
**/
bool cont_send_cred{false};
/**
* Flags to control how to act after a redirect for a post request.
* Default: POST_ALL
**/
PostRedirectFlags post_flags{PostRedirectFlags::POST_ALL};
Redirect() = default;
// NOLINTNEXTLINE (google-runtime-int)
Redirect(long p_maximum, bool p_follow, bool p_cont_send_cred, PostRedirectFlags p_post_flags) : maximum(p_maximum), follow(p_follow), cont_send_cred(p_cont_send_cred), post_flags(p_post_flags){};
// NOLINTNEXTLINE (google-runtime-int)
explicit Redirect(long p_maximum) : maximum(p_maximum){};
explicit Redirect(bool p_follow) : follow(p_follow){};
Redirect(bool p_follow, bool p_cont_send_cred) : follow(p_follow), cont_send_cred(p_cont_send_cred){};
explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags){};
};
} // namespace cpr
#endif

View file

@ -0,0 +1,17 @@
#ifndef CPR_RESERVE_SIZE_H
#define CPR_RESERVE_SIZE_H
#include <cstdint>
namespace cpr {
class ReserveSize {
public:
ReserveSize(const size_t _size) : size(_size) {}
size_t size = 0;
};
} // namespace cpr
#endif

23
include/cpr/resolve.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef CPR_RESOLVE_H
#define CPR_RESOLVE_H
#include <string>
#include <set>
namespace cpr {
class Resolve {
public:
std::string host;
std::string addr;
std::set<uint16_t> ports;
Resolve(const std::string& host_param, const std::string& addr_param, const std::set<uint16_t>& ports_param = std::set<uint16_t>{80U, 443U}): host(host_param), addr(addr_param), ports(ports_param) {
if (this->ports.empty()) {
this->ports.insert(80U);
this->ports.insert(443U);
}
}
};
} // namespace cpr
#endif

58
include/cpr/response.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef CPR_RESPONSE_H
#define CPR_RESPONSE_H
#include <cassert>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "cpr/cert_info.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/error.h"
#include "cpr/ssl_options.h"
#include "cpr/util.h"
namespace cpr {
class MultiPerform;
class Response {
private:
friend MultiPerform;
std::shared_ptr<CurlHolder> curl_{nullptr};
public:
// Ignored here since libcurl uses a long for this.
// NOLINTNEXTLINE(google-runtime-int)
long status_code{};
std::string text{};
Header header{};
Url url{};
double elapsed{};
Cookies cookies{};
Error error{};
std::string raw_header{};
std::string status_line{};
std::string reason{};
cpr_off_t uploaded_bytes{};
cpr_off_t downloaded_bytes{};
// Ignored here since libcurl uses a long for this.
// NOLINTNEXTLINE(google-runtime-int)
long redirect_count{};
Response() = default;
Response(std::shared_ptr<CurlHolder> curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies, Error&& p_error);
std::vector<CertInfo> GetCertInfos();
Response(const Response& other) = default;
Response(Response&& old) noexcept = default;
~Response() noexcept = default;
Response& operator=(Response&& old) noexcept = default;
Response& operator=(const Response& other) = default;
};
} // namespace cpr
#endif

308
include/cpr/session.h Normal file
View file

@ -0,0 +1,308 @@
#ifndef CPR_SESSION_H
#define CPR_SESSION_H
#include <cstdint>
#include <fstream>
#include <functional>
#include <future>
#include <memory>
#include <queue>
#include "cpr/accept_encoding.h"
#include "cpr/async_wrapper.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/body.h"
#include "cpr/callback.h"
#include "cpr/connect_timeout.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/curlholder.h"
#include "cpr/http_version.h"
#include "cpr/interface.h"
#include "cpr/limit_rate.h"
#include "cpr/local_port.h"
#include "cpr/local_port_range.h"
#include "cpr/low_speed.h"
#include "cpr/multipart.h"
#include "cpr/parameters.h"
#include "cpr/payload.h"
#include "cpr/proxies.h"
#include "cpr/proxyauth.h"
#include "cpr/range.h"
#include "cpr/redirect.h"
#include "cpr/reserve_size.h"
#include "cpr/resolve.h"
#include "cpr/response.h"
#include "cpr/ssl_options.h"
#include "cpr/timeout.h"
#include "cpr/unix_socket.h"
#include "cpr/user_agent.h"
#include "cpr/util.h"
#include "cpr/verbose.h"
namespace cpr {
using AsyncResponse = AsyncWrapper<Response>;
class Interceptor;
class MultiPerform;
class Session : public std::enable_shared_from_this<Session> {
public:
Session();
Session(const Session& other) = delete;
Session(Session&& old) = default;
~Session() = default;
Session& operator=(Session&& old) noexcept = default;
Session& operator=(const Session& other) = delete;
void SetUrl(const Url& url);
void SetParameters(const Parameters& parameters);
void SetParameters(Parameters&& parameters);
void SetHeader(const Header& header);
void UpdateHeader(const Header& header);
void SetTimeout(const Timeout& timeout);
void SetConnectTimeout(const ConnectTimeout& timeout);
void SetAuth(const Authentication& auth);
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void SetBearer(const Bearer& token);
#endif
void SetUserAgent(const UserAgent& ua);
void SetPayload(Payload&& payload);
void SetPayload(const Payload& payload);
void SetProxies(Proxies&& proxies);
void SetProxies(const Proxies& proxies);
void SetProxyAuth(ProxyAuthentication&& proxy_auth);
void SetProxyAuth(const ProxyAuthentication& proxy_auth);
void SetMultipart(Multipart&& multipart);
void SetMultipart(const Multipart& multipart);
void SetRedirect(const Redirect& redirect);
void SetCookies(const Cookies& cookies);
void SetBody(Body&& body);
void SetBody(const Body& body);
void SetLowSpeed(const LowSpeed& low_speed);
void SetVerifySsl(const VerifySsl& verify);
void SetUnixSocket(const UnixSocket& unix_socket);
void SetSslOptions(const SslOptions& options);
void SetReadCallback(const ReadCallback& read);
void SetHeaderCallback(const HeaderCallback& header);
void SetWriteCallback(const WriteCallback& write);
void SetProgressCallback(const ProgressCallback& progress);
void SetDebugCallback(const DebugCallback& debug);
void SetVerbose(const Verbose& verbose);
void SetInterface(const Interface& iface);
void SetLocalPort(const LocalPort& local_port);
void SetLocalPortRange(const LocalPortRange& local_port_range);
void SetHttpVersion(const HttpVersion& version);
void SetRange(const Range& range);
void SetResolve(const Resolve& resolve);
void SetResolves(const std::vector<Resolve>& resolves);
void SetMultiRange(const MultiRange& multi_range);
void SetReserveSize(const ReserveSize& reserve_size);
void SetAcceptEncoding(const AcceptEncoding& accept_encoding);
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);
void SetLimitRate(const LimitRate& limit_rate);
// For cancellable requests
void SetCancellationParam(std::shared_ptr<std::atomic_bool> param);
// Used in templated functions
void SetOption(const Url& url);
void SetOption(const Parameters& parameters);
void SetOption(Parameters&& parameters);
void SetOption(const Header& header);
void SetOption(const Timeout& timeout);
void SetOption(const ConnectTimeout& timeout);
void SetOption(const Authentication& auth);
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void SetOption(const Bearer& auth);
#endif
void SetOption(const UserAgent& ua);
void SetOption(Payload&& payload);
void SetOption(const Payload& payload);
void SetOption(const LimitRate& limit_rate);
void SetOption(Proxies&& proxies);
void SetOption(const Proxies& proxies);
void SetOption(ProxyAuthentication&& proxy_auth);
void SetOption(const ProxyAuthentication& proxy_auth);
void SetOption(Multipart&& multipart);
void SetOption(const Multipart& multipart);
void SetOption(const Redirect& redirect);
void SetOption(const Cookies& cookies);
void SetOption(Body&& body);
void SetOption(const Body& body);
void SetOption(const ReadCallback& read);
void SetOption(const HeaderCallback& header);
void SetOption(const WriteCallback& write);
void SetOption(const ProgressCallback& progress);
void SetOption(const DebugCallback& debug);
void SetOption(const LowSpeed& low_speed);
void SetOption(const VerifySsl& verify);
void SetOption(const Verbose& verbose);
void SetOption(const UnixSocket& unix_socket);
void SetOption(const SslOptions& options);
void SetOption(const Interface& iface);
void SetOption(const LocalPort& local_port);
void SetOption(const LocalPortRange& local_port_range);
void SetOption(const HttpVersion& version);
void SetOption(const Range& range);
void SetOption(const MultiRange& multi_range);
void SetOption(const ReserveSize& reserve_size);
void SetOption(const AcceptEncoding& accept_encoding);
void SetOption(AcceptEncoding&& accept_encoding);
void SetOption(const Resolve& resolve);
void SetOption(const std::vector<Resolve>& resolves);
cpr_off_t GetDownloadFileLength();
/**
* Attempt to preallocate enough memory for specified number of characters in the response string.
* Pass 0 to disable this behavior and let the response string be allocated dynamically on demand.
*
* Example:
* cpr::Session session;
* session.SetUrl(cpr::Url{"http://xxx/file"});
* session.ResponseStringReserve(1024 * 512); // Reserve space for at least 1024 * 512 characters
* cpr::Response r = session.Get();
**/
void ResponseStringReserve(size_t size);
Response Delete();
Response Download(const WriteCallback& write);
Response Download(std::ofstream& file);
Response Get();
Response Head();
Response Options();
Response Patch();
Response Post();
Response Put();
AsyncResponse GetAsync();
AsyncResponse DeleteAsync();
AsyncResponse DownloadAsync(const WriteCallback& write);
AsyncResponse DownloadAsync(std::ofstream& file);
AsyncResponse HeadAsync();
AsyncResponse OptionsAsync();
AsyncResponse PatchAsync();
AsyncResponse PostAsync();
AsyncResponse PutAsync();
template <typename Then>
auto GetCallback(Then then);
template <typename Then>
auto PostCallback(Then then);
template <typename Then>
auto PutCallback(Then then);
template <typename Then>
auto HeadCallback(Then then);
template <typename Then>
auto DeleteCallback(Then then);
template <typename Then>
auto OptionsCallback(Then then);
template <typename Then>
auto PatchCallback(Then then);
std::shared_ptr<CurlHolder> GetCurlHolder();
std::string GetFullRequestUrl();
void PrepareDelete();
void PrepareGet();
void PrepareHead();
void PrepareOptions();
void PreparePatch();
void PreparePost();
void PreparePut();
void PrepareDownload(const WriteCallback& write);
void PrepareDownload(std::ofstream& file);
Response Complete(CURLcode curl_error);
Response CompleteDownload(CURLcode curl_error);
void AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor);
private:
// Interceptors should be able to call the private proceed() function
friend Interceptor;
friend MultiPerform;
bool hasBodyOrPayload_{false};
bool chunkedTransferEncoding_{false};
std::shared_ptr<CurlHolder> curl_;
Url url_;
Parameters parameters_;
Proxies proxies_;
ProxyAuthentication proxyAuth_;
Header header_;
AcceptEncoding acceptEncoding_;
/**
* Will be set by the read callback.
* Ensures that the "Transfer-Encoding" is set to "chunked", if not overriden in header_.
**/
ReadCallback readcb_;
HeaderCallback headercb_;
WriteCallback writecb_;
ProgressCallback progresscb_;
DebugCallback debugcb_;
CancellationCallback cancellationcb_;
size_t response_string_reserve_size_{0};
std::string response_string_;
std::string header_string_;
std::queue<std::shared_ptr<Interceptor>> interceptors_;
bool isUsedInMultiPerform{false};
bool isCancellable{false};
Response makeDownloadRequest();
Response makeRequest();
Response proceed();
Response intercept();
void prepareCommon();
void prepareCommonDownload();
void SetHeaderInternal();
std::shared_ptr<Session> GetSharedPtrFromThis();
CURLcode DoEasyPerform();
};
template <typename Then>
auto Session::GetCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Get()); }, std::move(then));
}
template <typename Then>
auto Session::PostCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Post()); }, std::move(then));
}
template <typename Then>
auto Session::PutCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Put()); }, std::move(then));
}
template <typename Then>
auto Session::HeadCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Head()); }, std::move(then));
}
template <typename Then>
auto Session::DeleteCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Delete()); }, std::move(then));
}
template <typename Then>
auto Session::OptionsCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Options()); }, std::move(then));
}
template <typename Then>
auto Session::PatchCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Patch()); }, std::move(then));
}
} // namespace cpr
#endif

47
include/cpr/singleton.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef CPR_SINGLETON_H
#define CPR_SINGLETON_H
#include <mutex>
#ifndef CPR_DISABLE_COPY
#define CPR_DISABLE_COPY(Class) \
Class(const Class&) = delete; \
Class& operator=(const Class&) = delete;
#endif
#ifndef CPR_SINGLETON_DECL
#define CPR_SINGLETON_DECL(Class) \
public: \
static Class* GetInstance(); \
static void ExitInstance(); \
private: \
CPR_DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::mutex s_mutex;
#endif
#ifndef CPR_SINGLETON_IMPL
#define CPR_SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = nullptr; \
std::mutex Class::s_mutex; \
Class* Class::GetInstance() { \
if (s_pInstance == nullptr) { \
s_mutex.lock(); \
if (s_pInstance == nullptr) { \
s_pInstance = new Class; \
} \
s_mutex.unlock(); \
} \
return s_pInstance; \
} \
void Class::ExitInstance() { \
s_mutex.lock(); \
if (s_pInstance) { \
delete s_pInstance; \
s_pInstance = nullptr; \
} \
s_mutex.unlock(); \
}
#endif
#endif

26
include/cpr/ssl_ctx.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef CPR_SSL_CTX_H
#define CPR_SSL_CTX_H
#include "cpr/ssl_options.h"
#include <curl/curl.h>
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
namespace cpr {
/**
* This callback function loads a CA certificate from raw_cert_buf and gets called by libcurl
* just before the initialization of an SSL connection.
* The raw_cert_buf argument is set with the CURLOPT_SSL_CTX_DATA option and has to be a nul
* terminated buffer.
*
* Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html
* https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html
*/
CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf);
} // Namespace cpr
#endif
#endif

622
include/cpr/ssl_options.h Normal file
View file

@ -0,0 +1,622 @@
#ifndef CPR_SSLOPTIONS_H
#define CPR_SSLOPTIONS_H
#include <memory>
#include <string>
#include <vector>
#include <cpr/filesystem.h>
#include <curl/curl.h>
#include "cpr/util.h"
#include <utility>
#define __LIBCURL_VERSION_GTE(major, minor) ((LIBCURL_VERSION_MAJOR > (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR >= (minor))))
#define __LIBCURL_VERSION_LT(major, minor) ((LIBCURL_VERSION_MAJOR < (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR < (minor))))
#ifndef SUPPORT_ALPN
#define SUPPORT_ALPN __LIBCURL_VERSION_GTE(7, 36)
#endif
#ifndef SUPPORT_NPN
#define SUPPORT_NPN __LIBCURL_VERSION_GTE(7, 36) && __LIBCURL_VERSION_LT(7, 86)
#endif
#ifndef SUPPORT_SSLv2
#define SUPPORT_SSLv2 __LIBCURL_VERSION_LT(7, 19)
#endif
#ifndef SUPPORT_SSLv3
#define SUPPORT_SSLv3 __LIBCURL_VERSION_LT(7, 39)
#endif
#ifndef SUPPORT_TLSv1_0
#define SUPPORT_TLSv1_0 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_1
#define SUPPORT_TLSv1_1 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_2
#define SUPPORT_TLSv1_2 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_3
#define SUPPORT_TLSv1_3 __LIBCURL_VERSION_GTE(7, 52)
#endif
#ifndef SUPPORT_MAX_TLS_VERSION
#define SUPPORT_MAX_TLS_VERSION __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_1
#define SUPPORT_MAX_TLSv1_1 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_2
#define SUPPORT_MAX_TLSv1_2 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_3
#define SUPPORT_MAX_TLSv1_3 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_TLSv13_CIPHERS
#define SUPPORT_TLSv13_CIPHERS __LIBCURL_VERSION_GTE(7, 61)
#endif
#ifndef SUPPORT_SESSIONID_CACHE
#define SUPPORT_SESSIONID_CACHE __LIBCURL_VERSION_GTE(7, 16)
#endif
#ifndef SUPPORT_SSL_FALSESTART
#define SUPPORT_SSL_FALSESTART __LIBCURL_VERSION_GTE(7, 42)
#endif
#ifndef SUPPORT_SSL_NO_REVOKE
#define SUPPORT_SSL_NO_REVOKE __LIBCURL_VERSION_GTE(7, 44)
#endif
#ifndef SUPPORT_CURLOPT_SSLKEY_BLOB
#define SUPPORT_CURLOPT_SSLKEY_BLOB __LIBCURL_VERSION_GTE(7, 71)
#endif
#ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION
#define SUPPORT_CURLOPT_SSL_CTX_FUNCTION __LIBCURL_VERSION_GTE(7, 11)
#endif
namespace cpr {
class VerifySsl {
public:
VerifySsl() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifySsl(bool p_verify) : verify(p_verify) {}
explicit operator bool() const {
return verify;
}
bool verify = true;
};
namespace ssl {
// set SSL client certificate
class CertFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
virtual ~CertFile() = default;
const fs::path filename;
virtual const char* GetCertType() const {
return "PEM";
}
};
using PemCert = CertFile;
class DerCert : public CertFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {}
virtual ~DerCert() = default;
const char* GetCertType() const override {
return "DER";
}
};
// specify private keyfile for TLS and SSL client cert
class KeyFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
template <typename FileType, typename PassType>
KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward<FileType>(p_filename)), password(std::move(p_password)) {}
virtual ~KeyFile() {
util::secureStringClear(password);
}
fs::path filename;
std::string password;
virtual const char* GetKeyType() const {
return "PEM";
}
};
#if SUPPORT_CURLOPT_SSLKEY_BLOB
class KeyBlob {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}
template <typename BlobType, typename PassType>
KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward<BlobType>(p_blob)), password(std::move(p_password)) {}
virtual ~KeyBlob() {
util::secureStringClear(password);
}
std::string blob;
std::string password;
virtual const char* GetKeyType() const {
return "PEM";
}
};
#endif
using PemKey = KeyFile;
class DerKey : public KeyFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {}
template <typename FileType, typename PassType>
DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward<FileType>(p_filename), std::move(p_password)) {}
virtual ~DerKey() = default;
const char* GetKeyType() const override {
return "DER";
}
};
class PinnedPublicKey {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {}
const std::string pinned_public_key;
};
#if SUPPORT_ALPN
// This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to
// use supports it), which can be used to negotiate http2.
class ALPN {
public:
ALPN() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ALPN(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
// This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to
// use supports it), which can be used to negotiate http2.
class NPN {
public:
NPN() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
NPN(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif // SUPPORT_NPN
// This option determines whether libcurl verifies that the server cert is for the server it is
// known as.
class VerifyHost {
public:
VerifyHost() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyHost(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
// This option determines whether libcurl verifies the authenticity of the peer's certificate.
class VerifyPeer {
public:
VerifyPeer() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyPeer(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
// This option determines whether libcurl verifies the status of the server cert using the
// "Certificate Status Request" TLS extension (aka. OCSP stapling).
class VerifyStatus {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyStatus(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
// TLS v1.0 or later
struct TLSv1 {};
#if SUPPORT_SSLv2
// SSL v2 (but not SSLv3)
struct SSLv2 {};
#endif
#if SUPPORT_SSLv3
// SSL v3 (but not SSLv2)
struct SSLv3 {};
#endif
#if SUPPORT_TLSv1_0
// TLS v1.0 or later (Added in 7.34.0)
struct TLSv1_0 {};
#endif
#if SUPPORT_TLSv1_1
// TLS v1.1 or later (Added in 7.34.0)
struct TLSv1_1 {};
#endif
#if SUPPORT_TLSv1_2
// TLS v1.2 or later (Added in 7.34.0)
struct TLSv1_2 {};
#endif
#if SUPPORT_TLSv1_3
// TLS v1.3 or later (Added in 7.52.0)
struct TLSv1_3 {};
#endif
#if SUPPORT_MAX_TLS_VERSION
// The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL
// library is used.
struct MaxTLSVersion {};
#endif
#if SUPPORT_MAX_TLSv1_0
// The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0)
struct MaxTLSv1_0 {};
#endif
#if SUPPORT_MAX_TLSv1_1
// The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0)
struct MaxTLSv1_1 {};
#endif
#if SUPPORT_MAX_TLSv1_2
// The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0)
struct MaxTLSv1_2 {};
#endif
#if SUPPORT_MAX_TLSv1_3
// The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0)
struct MaxTLSv1_3 {};
#endif
// path to Certificate Authority (CA) bundle
class CaInfo {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
// specify directory holding CA certificates
class CaPath {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
class CaBuffer {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {}
const std::string buffer;
};
#endif
// specify a Certificate Revocation List file
class Crl {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
// specify ciphers to use for TLS
class Ciphers {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
std::string ciphers;
};
#if SUPPORT_TLSv13_CIPHERS
// specify ciphers suites to use for TLS 1.3
class TLS13_Ciphers {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
std::string ciphers;
};
#endif
#if SUPPORT_SESSIONID_CACHE
// enable/disable use of the SSL session-ID cache
class SessionIdCache {
public:
SessionIdCache() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
SessionIdCache(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif
#if SUPPORT_SSL_FALSESTART
class SslFastStart {
public:
SslFastStart() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
SslFastStart(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
#endif
class NoRevoke {
public:
NoRevoke() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
NoRevoke(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
} // namespace ssl
struct SslOptions {
// We don't use fs::path here, as this leads to problems using windows
std::string cert_file;
std::string cert_type;
// We don't use fs::path here, as this leads to problems using windows
std::string key_file;
#if SUPPORT_CURLOPT_SSLKEY_BLOB
std::string key_blob;
#endif
std::string key_type;
std::string key_pass;
std::string pinned_public_key;
#if SUPPORT_ALPN
bool enable_alpn = true;
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
bool enable_npn = true;
#endif // SUPPORT_ALPN
bool verify_host = true;
bool verify_peer = true;
bool verify_status = false;
int ssl_version = CURL_SSLVERSION_DEFAULT;
#if SUPPORT_SSL_NO_REVOKE
bool ssl_no_revoke = false;
#endif
#if SUPPORT_MAX_TLS_VERSION
int max_version = CURL_SSLVERSION_MAX_DEFAULT;
#endif
// We don't use fs::path here, as this leads to problems using windows
std::string ca_info;
// We don't use fs::path here, as this leads to problems using windows
std::string ca_path;
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
std::string ca_buffer;
#endif
// We don't use fs::path here, as this leads to problems using windows
std::string crl_file;
std::string ciphers;
#if SUPPORT_TLSv13_CIPHERS
std::string tls13_ciphers;
#endif
#if SUPPORT_SESSIONID_CACHE
bool session_id_cache = true;
#endif
~SslOptions() noexcept {
#if SUPPORT_CURLOPT_SSLKEY_BLOB
util::secureStringClear(key_blob);
#endif
util::secureStringClear(key_pass);
}
void SetOption(const ssl::CertFile& opt) {
cert_file = opt.filename.string();
cert_type = opt.GetCertType();
}
void SetOption(const ssl::KeyFile& opt) {
key_file = opt.filename.string();
key_type = opt.GetKeyType();
key_pass = opt.password;
}
#if SUPPORT_CURLOPT_SSLKEY_BLOB
void SetOption(const ssl::KeyBlob& opt) {
key_blob = opt.blob;
key_type = opt.GetKeyType();
key_pass = opt.password;
}
#endif
void SetOption(const ssl::PinnedPublicKey& opt) {
pinned_public_key = opt.pinned_public_key;
}
#if SUPPORT_ALPN
void SetOption(const ssl::ALPN& opt) {
enable_alpn = opt.enabled;
}
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
void SetOption(const ssl::NPN& opt) {
enable_npn = opt.enabled;
}
#endif // SUPPORT_NPN
void SetOption(const ssl::VerifyHost& opt) {
verify_host = opt.enabled;
}
void SetOption(const ssl::VerifyPeer& opt) {
verify_peer = opt.enabled;
}
void SetOption(const ssl::VerifyStatus& opt) {
verify_status = opt.enabled;
}
void SetOption(const ssl::TLSv1& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1;
}
#if SUPPORT_SSL_NO_REVOKE
void SetOption(const ssl::NoRevoke& opt) {
ssl_no_revoke = opt.enabled;
}
#endif
#if SUPPORT_SSLv2
void SetOption(const ssl::SSLv2& /*opt*/) {
ssl_version = CURL_SSLVERSION_SSLv2;
}
#endif
#if SUPPORT_SSLv3
void SetOption(const ssl::SSLv3& /*opt*/) {
ssl_version = CURL_SSLVERSION_SSLv3;
}
#endif
#if SUPPORT_TLSv1_0
void SetOption(const ssl::TLSv1_0& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_0;
}
#endif
#if SUPPORT_TLSv1_1
void SetOption(const ssl::TLSv1_1& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_1;
}
#endif
#if SUPPORT_TLSv1_2
void SetOption(const ssl::TLSv1_2& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_2;
}
#endif
#if SUPPORT_TLSv1_3
void SetOption(const ssl::TLSv1_3& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_3;
}
#endif
#if SUPPORT_MAX_TLS_VERSION
void SetOption(const ssl::MaxTLSVersion& /*opt*/) {
max_version = CURL_SSLVERSION_DEFAULT;
}
#endif
#if SUPPORT_MAX_TLSv1_0
void SetOption(const ssl::MaxTLSv1_0& opt) {
max_version = CURL_SSLVERSION_MAX_TLSv1_0;
}
#endif
#if SUPPORT_MAX_TLSv1_1
void SetOption(const ssl::MaxTLSv1_1& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_1;
}
#endif
#if SUPPORT_MAX_TLSv1_2
void SetOption(const ssl::MaxTLSv1_2& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_2;
}
#endif
#if SUPPORT_MAX_TLSv1_3
void SetOption(const ssl::MaxTLSv1_3& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_3;
}
#endif
void SetOption(const ssl::CaInfo& opt) {
ca_info = opt.filename.string();
}
void SetOption(const ssl::CaPath& opt) {
ca_path = opt.filename.string();
}
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
void SetOption(const ssl::CaBuffer& opt) {
ca_buffer = opt.buffer;
}
#endif
void SetOption(const ssl::Crl& opt) {
crl_file = opt.filename.string();
}
void SetOption(const ssl::Ciphers& opt) {
ciphers = opt.ciphers;
}
#if SUPPORT_TLSv13_CIPHERS
void SetOption(const ssl::TLS13_Ciphers& opt) {
tls13_ciphers = opt.ciphers;
}
#endif
#if SUPPORT_SESSIONID_CACHE
void SetOption(const ssl::SessionIdCache& opt) {
session_id_cache = opt.enabled;
}
#endif
};
namespace priv {
template <typename T>
void set_ssl_option(SslOptions& opts, T&& t) {
opts.SetOption(std::forward<T>(t));
}
template <typename T, typename... Ts>
void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) {
set_ssl_option(opts, std::forward<T>(t));
set_ssl_option(opts, std::move(ts)...);
}
} // namespace priv
template <typename... Ts>
SslOptions Ssl(Ts&&... ts) {
SslOptions opts;
priv::set_ssl_option(opts, std::move(ts)...);
return opts;
}
} // namespace cpr
#endif

100
include/cpr/status_codes.h Normal file
View file

@ -0,0 +1,100 @@
#ifndef _CPR_STATUS_CODES
#define _CPR_STATUS_CODES
#include <cstdint>
namespace cpr {
namespace status {
// Information responses
constexpr std::int32_t HTTP_CONTINUE = 100;
constexpr std::int32_t HTTP_SWITCHING_PROTOCOL = 101;
constexpr std::int32_t HTTP_PROCESSING = 102;
constexpr std::int32_t HTTP_EARLY_HINTS = 103;
// Successful responses
constexpr std::int32_t HTTP_OK = 200;
constexpr std::int32_t HTTP_CREATED = 201;
constexpr std::int32_t HTTP_ACCEPTED = 202;
constexpr std::int32_t HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
constexpr std::int32_t HTTP_NO_CONTENT = 204;
constexpr std::int32_t HTTP_RESET_CONTENT = 205;
constexpr std::int32_t HTTP_PARTIAL_CONTENT = 206;
constexpr std::int32_t HTTP_MULTI_STATUS = 207;
constexpr std::int32_t HTTP_ALREADY_REPORTED = 208;
constexpr std::int32_t HTTP_IM_USED = 226;
// Redirection messages
constexpr std::int32_t HTTP_MULTIPLE_CHOICE = 300;
constexpr std::int32_t HTTP_MOVED_PERMANENTLY = 301;
constexpr std::int32_t HTTP_FOUND = 302;
constexpr std::int32_t HTTP_SEE_OTHER = 303;
constexpr std::int32_t HTTP_NOT_MODIFIED = 304;
constexpr std::int32_t HTTP_USE_PROXY = 305;
constexpr std::int32_t HTTP_UNUSED = 306;
constexpr std::int32_t HTTP_TEMPORARY_REDIRECT = 307;
constexpr std::int32_t HTTP_PERMANENT_REDIRECT = 308;
// Client error responses
constexpr std::int32_t HTTP_BAD_REQUEST = 400;
constexpr std::int32_t HTTP_UNAUTHORIZED = 401;
constexpr std::int32_t HTTP_PAYMENT_REQUIRED = 402;
constexpr std::int32_t HTTP_FORBIDDEN = 403;
constexpr std::int32_t HTTP_NOT_FOUND = 404;
constexpr std::int32_t HTTP_METHOD_NOT_ALLOWED = 405;
constexpr std::int32_t HTTP_NOT_ACCEPTABLE = 406;
constexpr std::int32_t HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
constexpr std::int32_t HTTP_REQUEST_TIMEOUT = 408;
constexpr std::int32_t HTTP_CONFLICT = 409;
constexpr std::int32_t HTTP_GONE = 410;
constexpr std::int32_t HTTP_LENGTH_REQUIRED = 411;
constexpr std::int32_t HTTP_PRECONDITION_FAILED = 412;
constexpr std::int32_t HTTP_PAYLOAD_TOO_LARGE = 413;
constexpr std::int32_t HTTP_URI_TOO_LONG = 414;
constexpr std::int32_t HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
constexpr std::int32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
constexpr std::int32_t HTTP_EXPECTATION_FAILED = 417;
constexpr std::int32_t HTTP_IM_A_TEAPOT = 418;
constexpr std::int32_t HTTP_MISDIRECTED_REQUEST = 421;
constexpr std::int32_t HTTP_UNPROCESSABLE_ENTITY = 422;
constexpr std::int32_t HTTP_LOCKED = 423;
constexpr std::int32_t HTTP_FAILED_DEPENDENCY = 424;
constexpr std::int32_t HTTP_TOO_EARLY = 425;
constexpr std::int32_t HTTP_UPGRADE_REQUIRED = 426;
constexpr std::int32_t HTTP_PRECONDITION_REQUIRED = 428;
constexpr std::int32_t HTTP_TOO_MANY_REQUESTS = 429;
constexpr std::int32_t HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
constexpr std::int32_t HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
// Server response errors
constexpr std::int32_t HTTP_INTERNAL_SERVER_ERROR = 500;
constexpr std::int32_t HTTP_NOT_IMPLEMENTED = 501;
constexpr std::int32_t HTTP_BAD_GATEWAY = 502;
constexpr std::int32_t HTTP_SERVICE_UNAVAILABLE = 503;
constexpr std::int32_t HTTP_GATEWAY_TIMEOUT = 504;
constexpr std::int32_t HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
constexpr std::int32_t HTTP_VARIANT_ALSO_NEGOTIATES = 506;
constexpr std::int32_t HTTP_INSUFFICIENT_STORAGE = 507;
constexpr std::int32_t HTTP_LOOP_DETECTED = 508;
constexpr std::int32_t HTTP_NOT_EXTENDED = 510;
constexpr std::int32_t HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
constexpr std::int32_t INFO_CODE_OFFSET = 100;
constexpr std::int32_t SUCCESS_CODE_OFFSET = 200;
constexpr std::int32_t REDIRECT_CODE_OFFSET = 300;
constexpr std::int32_t CLIENT_ERROR_CODE_OFFSET = 400;
constexpr std::int32_t SERVER_ERROR_CODE_OFFSET = 500;
constexpr std::int32_t MISC_CODE_OFFSET = 600;
constexpr bool is_informational(const std::int32_t code) {
return (code >= INFO_CODE_OFFSET && code < SUCCESS_CODE_OFFSET);
}
constexpr bool is_success(const std::int32_t code) {
return (code >= SUCCESS_CODE_OFFSET && code < REDIRECT_CODE_OFFSET);
}
constexpr bool is_redirect(const std::int32_t code) {
return (code >= REDIRECT_CODE_OFFSET && code < CLIENT_ERROR_CODE_OFFSET);
}
constexpr bool is_client_error(const std::int32_t code) {
return (code >= CLIENT_ERROR_CODE_OFFSET && code < SERVER_ERROR_CODE_OFFSET);
}
constexpr bool is_server_error(const std::int32_t code) {
return (code >= SERVER_ERROR_CODE_OFFSET && code < MISC_CODE_OFFSET);
}
} // namespace status
} // namespace cpr
#endif

122
include/cpr/threadpool.h Normal file
View file

@ -0,0 +1,122 @@
#ifndef CPR_THREAD_POOL_H
#define CPR_THREAD_POOL_H
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <future>
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()
constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1;
constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{60000};
namespace cpr {
class ThreadPool {
public:
using Task = std::function<void()>;
explicit ThreadPool(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME);
virtual ~ThreadPool();
void SetMinThreadNum(size_t min_threads) {
min_thread_num = min_threads;
}
void SetMaxThreadNum(size_t max_threads) {
max_thread_num = max_threads;
}
void SetMaxIdleTime(std::chrono::milliseconds ms) {
max_idle_time = ms;
}
size_t GetCurrentThreadNum() {
return cur_thread_num;
}
size_t GetIdleThreadNum() {
return idle_thread_num;
}
bool IsStarted() {
return status != STOP;
}
bool IsStopped() {
return status == STOP;
}
int Start(size_t start_threads = 0);
int Stop();
int Pause();
int Resume();
int Wait();
/**
* Return a future, calling future.get() will wait task done and return RetType.
* Submit(fn, args...)
* Submit(std::bind(&Class::mem_fn, &obj))
* Submit(std::mem_fn(&Class::mem_fn, &obj))
**/
template <class Fn, class... Args>
auto Submit(Fn&& fn, Args&&... args) {
if (status == STOP) {
Start();
}
if (idle_thread_num <= 0 && cur_thread_num < max_thread_num) {
CreateThread();
}
using RetType = decltype(fn(args...));
auto task = std::make_shared<std::packaged_task<RetType()> >(std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
std::future<RetType> future = task->get_future();
{
std::lock_guard<std::mutex> locker(task_mutex);
tasks.emplace([task] { (*task)(); });
}
task_cond.notify_one();
return future;
}
private:
bool CreateThread();
void AddThread(std::thread* thread);
void DelThread(std::thread::id id);
public:
size_t min_thread_num;
size_t max_thread_num;
std::chrono::milliseconds max_idle_time;
private:
enum Status {
STOP,
RUNNING,
PAUSE,
};
struct ThreadData {
std::shared_ptr<std::thread> thread;
std::thread::id id;
Status status;
time_t start_time;
time_t stop_time;
};
std::atomic<Status> status;
std::atomic<size_t> cur_thread_num;
std::atomic<size_t> idle_thread_num;
std::list<ThreadData> threads;
std::mutex thread_mutex;
std::queue<Task> tasks;
std::mutex task_mutex;
std::condition_variable task_cond;
};
} // namespace cpr
#endif

27
include/cpr/timeout.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef CPR_TIMEOUT_H
#define CPR_TIMEOUT_H
#include <chrono>
#include <cstdint>
namespace cpr {
class Timeout {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::chrono::milliseconds& duration) : ms{duration} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::int32_t& milliseconds) : Timeout{std::chrono::milliseconds(milliseconds)} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::chrono::seconds& duration) : ms{std::chrono::milliseconds(duration).count()} {}
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
long Milliseconds() const;
std::chrono::milliseconds ms;
};
} // namespace cpr
#endif

21
include/cpr/unix_socket.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef CPR_UNIX_SOCKET_H
#define CPR_UNIX_SOCKET_H
#include <string>
namespace cpr {
class UnixSocket {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UnixSocket(std::string unix_socket) : unix_socket_(std::move(unix_socket)) {}
const char* GetUnixSocketString() const noexcept;
private:
const std::string unix_socket_;
};
} // namespace cpr
#endif

31
include/cpr/user_agent.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef CPR_USERAGENT_H
#define CPR_USERAGENT_H
#include <initializer_list>
#include <string>
#include "cpr/cprtypes.h"
namespace cpr {
class UserAgent : public StringHolder<UserAgent> {
public:
UserAgent() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(std::string useragent) : StringHolder<UserAgent>(std::move(useragent)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(std::string_view useragent) : StringHolder<UserAgent>(useragent) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(const char* useragent) : StringHolder<UserAgent>(useragent) {}
UserAgent(const char* str, size_t len) : StringHolder<UserAgent>(str, len) {}
UserAgent(const std::initializer_list<std::string> args) : StringHolder<UserAgent>(args) {}
UserAgent(const UserAgent& other) = default;
UserAgent(UserAgent&& old) noexcept = default;
~UserAgent() override = default;
UserAgent& operator=(UserAgent&& old) noexcept = default;
UserAgent& operator=(const UserAgent& other) = default;
};
} // namespace cpr
#endif

45
include/cpr/util.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef CPR_UTIL_H
#define CPR_UTIL_H
#include <fstream>
#include <string>
#include <vector>
#include "cpr/callback.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/curlholder.h"
namespace cpr::util {
Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr);
Cookies parseCookies(curl_slist* raw_cookies);
size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read);
size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header);
size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data);
size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file);
size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write);
template <typename T = ProgressCallback>
int progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) {
const int cancel_retval{1};
static_assert(cancel_retval != CURL_PROGRESSFUNC_CONTINUE);
return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : cancel_retval;
}
int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug);
std::vector<std::string> split(const std::string& to_split, char delimiter);
std::string urlEncode(const std::string& s);
std::string urlDecode(const std::string& s);
/**
* Override the content of the provided string to hide sensitive data. The
* string content after invocation is undefined. The string size is reset to zero.
* impl. based on:
* https://github.com/ojeda/secure_clear/blob/master/example-implementation/secure_clear.h
**/
void secureStringClear(std::string& s);
bool isTrue(const std::string& s);
} // namespace cpr::util
#endif

18
include/cpr/verbose.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef CPR_VERBOSE_H_
#define CPR_VERBOSE_H_
namespace cpr {
class Verbose {
public:
Verbose() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Verbose(const bool p_verbose) : verbose{p_verbose} {}
bool verbose = true;
};
} // namespace cpr
#endif /* CPR_VERBOSE_H_ */

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="15.0">
<PropertyGroup>
<LibraryType Condition="'$(Configuration)'=='Debug'">mdd</LibraryType>
<LibraryType Condition="'$(Configuration)'=='Release'">md</LibraryType>
</PropertyGroup>
<ItemGroup>
<CprLibs Include="$(MSBuildThisFileDirectory)\$(Platform)\$(Configuration)\lib\*.lib" />
</ItemGroup>
<ItemGroup>
<CprDlls Include="$(MSBuildThisFileDirectory)\$(Platform)\$(Configuration)\bin\*.dll" />
<None Include="@(CprDlls)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<CprLibraries>@(CprLibs)</CprLibraries>
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>$(CprLibraries);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="PlatformCheck" BeforeTargets="InjectReference" Condition="(('$(Platform)' != 'x86') AND ('$(Platform)' != 'x64')) AND ('$(Platform)' != 'Win32'))">
<Error Text="$(MSBuildThisFileName) does not work correctly on this platform: '$(Platform)'. You need to specify platform x86, x64, or Win32." />
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\$(Platform)\$(Configuration)\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>

19
nuget/libcpr.nuspec Normal file
View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>libcpr</id>
<version>$VERSION$</version>
<title>C++ Requests: Curl for People</title>
<authors>Simon Berger</authors>
<owners>Fabian Sauter, Kilian Traub, many other libcpr contributors</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>resources/cpr.png</icon>
<readme>README.md</readme>
<projectUrl>https://github.com/libcpr</projectUrl>
<description>C++ Requests: Curl for People, a spiritual port of Python Requests.</description>
<tags>Native, native</tags>
<language>english</language>
<repository type="git" url="https://github.com/libcpr/cpr" branch="master" commit="$COMMIT_HASH$" />
</metadata>
</package>

Some files were not shown because too many files have changed in this diff Show more