aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/cpr
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/cpr')
-rw-r--r--Src/external_dependencies/cpr/.clang-format59
-rw-r--r--Src/external_dependencies/cpr/.clang-tidy40
-rw-r--r--Src/external_dependencies/cpr/.gitignore56
-rw-r--r--Src/external_dependencies/cpr/CMakeLists.txt386
-rw-r--r--Src/external_dependencies/cpr/CODE_OF_CONDUCT.md128
-rw-r--r--Src/external_dependencies/cpr/CONTRIBUTING.md27
-rw-r--r--Src/external_dependencies/cpr/CppCheckSuppressions.txt2
-rw-r--r--Src/external_dependencies/cpr/LICENSE25
-rw-r--r--Src/external_dependencies/cpr/README.md162
-rw-r--r--Src/external_dependencies/cpr/cmake/FindMbedTLS.cmake14
-rw-r--r--Src/external_dependencies/cpr/cmake/clang-tidy.cmake13
-rw-r--r--Src/external_dependencies/cpr/cmake/clear_variable.cmake11
-rw-r--r--Src/external_dependencies/cpr/cmake/code_coverage.cmake29
-rw-r--r--Src/external_dependencies/cpr/cmake/cppcheck.cmake10
-rw-r--r--Src/external_dependencies/cpr/cmake/cprConfig.cmake.in8
-rw-r--r--Src/external_dependencies/cpr/cmake/cprver.h.in30
-rw-r--r--Src/external_dependencies/cpr/cmake/mongoose.CMakeLists.txt12
-rw-r--r--Src/external_dependencies/cpr/cmake/sanitizer.cmake73
-rw-r--r--Src/external_dependencies/cpr/cmake/zlib_external.cmake22
-rw-r--r--Src/external_dependencies/cpr/cpr-config.cmake26
-rw-r--r--Src/external_dependencies/cpr/cpr/CMakeLists.txt84
-rw-r--r--Src/external_dependencies/cpr/cpr/accept_encoding.cpp25
-rw-r--r--Src/external_dependencies/cpr/cpr/async.cpp8
-rw-r--r--Src/external_dependencies/cpr/cpr/auth.cpp16
-rw-r--r--Src/external_dependencies/cpr/cpr/bearer.cpp16
-rw-r--r--Src/external_dependencies/cpr/cpr/cert_info.cpp43
-rw-r--r--Src/external_dependencies/cpr/cpr/cookies.cpp106
-rw-r--r--Src/external_dependencies/cpr/cpr/cprtypes.cpp10
-rw-r--r--Src/external_dependencies/cpr/cpr/curl_container.cpp58
-rw-r--r--Src/external_dependencies/cpr/cpr/curlholder.cpp49
-rw-r--r--Src/external_dependencies/cpr/cpr/curlmultiholder.cpp15
-rw-r--r--Src/external_dependencies/cpr/cpr/error.cpp68
-rw-r--r--Src/external_dependencies/cpr/cpr/file.cpp40
-rw-r--r--Src/external_dependencies/cpr/cpr/interceptor.cpp46
-rw-r--r--Src/external_dependencies/cpr/cpr/multipart.cpp5
-rw-r--r--Src/external_dependencies/cpr/cpr/multiperform.cpp273
-rw-r--r--Src/external_dependencies/cpr/cpr/parameters.cpp10
-rw-r--r--Src/external_dependencies/cpr/cpr/payload.cpp10
-rw-r--r--Src/external_dependencies/cpr/cpr/proxies.cpp21
-rw-r--r--Src/external_dependencies/cpr/cpr/proxyauth.cpp21
-rw-r--r--Src/external_dependencies/cpr/cpr/redirect.cpp40
-rw-r--r--Src/external_dependencies/cpr/cpr/response.cpp44
-rw-r--r--Src/external_dependencies/cpr/cpr/session.cpp964
-rw-r--r--Src/external_dependencies/cpr/cpr/ssl_ctx.cpp70
-rw-r--r--Src/external_dependencies/cpr/cpr/threadpool.cpp148
-rw-r--r--Src/external_dependencies/cpr/cpr/timeout.cpp31
-rw-r--r--Src/external_dependencies/cpr/cpr/unix_socket.cpp8
-rw-r--r--Src/external_dependencies/cpr/cpr/util.cpp236
-rw-r--r--Src/external_dependencies/cpr/include/CMakeLists.txt67
-rw-r--r--Src/external_dependencies/cpr/include/cpr/accept_encoding.h36
-rw-r--r--Src/external_dependencies/cpr/include/cpr/api.h321
-rw-r--r--Src/external_dependencies/cpr/include/cpr/async.h49
-rw-r--r--Src/external_dependencies/cpr/include/cpr/auth.h33
-rw-r--r--Src/external_dependencies/cpr/include/cpr/bearer.h36
-rw-r--r--Src/external_dependencies/cpr/include/cpr/body.h54
-rw-r--r--Src/external_dependencies/cpr/include/cpr/buffer.h35
-rw-r--r--Src/external_dependencies/cpr/include/cpr/callback.h89
-rw-r--r--Src/external_dependencies/cpr/include/cpr/cert_info.h35
-rw-r--r--Src/external_dependencies/cpr/include/cpr/connect_timeout.h18
-rw-r--r--Src/external_dependencies/cpr/include/cpr/cookies.h92
-rw-r--r--Src/external_dependencies/cpr/include/cpr/cpr.h46
-rw-r--r--Src/external_dependencies/cpr/include/cpr/cprtypes.h137
-rw-r--r--Src/external_dependencies/cpr/include/cpr/curl_container.h53
-rw-r--r--Src/external_dependencies/cpr/include/cpr/curlholder.h54
-rw-r--r--Src/external_dependencies/cpr/include/cpr/curlmultiholder.h18
-rw-r--r--Src/external_dependencies/cpr/include/cpr/error.h53
-rw-r--r--Src/external_dependencies/cpr/include/cpr/file.h55
-rw-r--r--Src/external_dependencies/cpr/include/cpr/filesystem.h19
-rw-r--r--Src/external_dependencies/cpr/include/cpr/http_version.h67
-rw-r--r--Src/external_dependencies/cpr/include/cpr/interceptor.h36
-rw-r--r--Src/external_dependencies/cpr/include/cpr/interface.h34
-rw-r--r--Src/external_dependencies/cpr/include/cpr/limit_rate.h18
-rw-r--r--Src/external_dependencies/cpr/include/cpr/local_port.h23
-rw-r--r--Src/external_dependencies/cpr/include/cpr/local_port_range.h23
-rw-r--r--Src/external_dependencies/cpr/include/cpr/low_speed.h18
-rw-r--r--Src/external_dependencies/cpr/include/cpr/multipart.h45
-rw-r--r--Src/external_dependencies/cpr/include/cpr/multiperform.h119
-rw-r--r--Src/external_dependencies/cpr/include/cpr/parameters.h18
-rw-r--r--Src/external_dependencies/cpr/include/cpr/payload.h23
-rw-r--r--Src/external_dependencies/cpr/include/cpr/proxies.h23
-rw-r--r--Src/external_dependencies/cpr/include/cpr/proxyauth.h44
-rw-r--r--Src/external_dependencies/cpr/include/cpr/range.h44
-rw-r--r--Src/external_dependencies/cpr/include/cpr/redirect.h84
-rw-r--r--Src/external_dependencies/cpr/include/cpr/reserve_size.h17
-rw-r--r--Src/external_dependencies/cpr/include/cpr/resolve.h23
-rw-r--r--Src/external_dependencies/cpr/include/cpr/response.h58
-rw-r--r--Src/external_dependencies/cpr/include/cpr/session.h297
-rw-r--r--Src/external_dependencies/cpr/include/cpr/singleton.h47
-rw-r--r--Src/external_dependencies/cpr/include/cpr/ssl_ctx.h26
-rw-r--r--Src/external_dependencies/cpr/include/cpr/ssl_options.h622
-rw-r--r--Src/external_dependencies/cpr/include/cpr/status_codes.h100
-rw-r--r--Src/external_dependencies/cpr/include/cpr/threadpool.h122
-rw-r--r--Src/external_dependencies/cpr/include/cpr/timeout.h27
-rw-r--r--Src/external_dependencies/cpr/include/cpr/unix_socket.h21
-rw-r--r--Src/external_dependencies/cpr/include/cpr/user_agent.h33
-rw-r--r--Src/external_dependencies/cpr/include/cpr/util.h45
-rw-r--r--Src/external_dependencies/cpr/include/cpr/verbose.h18
-rw-r--r--Src/external_dependencies/cpr/nuget/build/native/libcpr.props29
-rw-r--r--Src/external_dependencies/cpr/nuget/build/native/libcpr.targets6
-rw-r--r--Src/external_dependencies/cpr/nuget/libcpr.nuspec19
-rw-r--r--Src/external_dependencies/cpr/nuget/resources/cpr.pngbin0 -> 6047 bytes
-rw-r--r--Src/external_dependencies/cpr/package-build/build-package.sh27
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/README.Debian5
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/changelog6
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/control39
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/copyright52
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr-dev.install3
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr1.install1
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/rules14
-rw-r--r--Src/external_dependencies/cpr/package-build/debian-libcpr/source/format1
-rw-r--r--Src/external_dependencies/cpr/test/CMakeLists.txt80
-rw-r--r--Src/external_dependencies/cpr/test/LICENSE677
-rw-r--r--Src/external_dependencies/cpr/test/abstractServer.cpp143
-rw-r--r--Src/external_dependencies/cpr/test/abstractServer.hpp70
-rw-r--r--Src/external_dependencies/cpr/test/alternating_tests.cpp163
-rw-r--r--Src/external_dependencies/cpr/test/async_tests.cpp79
-rw-r--r--Src/external_dependencies/cpr/test/callback_tests.cpp931
-rw-r--r--Src/external_dependencies/cpr/test/data/certificates/client.crt10
-rw-r--r--Src/external_dependencies/cpr/test/data/certificates/root-ca.crt12
-rw-r--r--Src/external_dependencies/cpr/test/data/certificates/server.crt10
-rw-r--r--Src/external_dependencies/cpr/test/data/client.cnf8
-rw-r--r--Src/external_dependencies/cpr/test/data/generate-certificates.sh76
-rw-r--r--Src/external_dependencies/cpr/test/data/keys/client.key3
-rw-r--r--Src/external_dependencies/cpr/test/data/keys/root-ca.key3
-rw-r--r--Src/external_dependencies/cpr/test/data/keys/server.key3
-rw-r--r--Src/external_dependencies/cpr/test/data/keys/server.pub3
-rw-r--r--Src/external_dependencies/cpr/test/data/root-ca.cnf69
-rw-r--r--Src/external_dependencies/cpr/test/data/server.cnf12
-rw-r--r--Src/external_dependencies/cpr/test/delete_tests.cpp259
-rw-r--r--Src/external_dependencies/cpr/test/download_tests.cpp118
-rw-r--r--Src/external_dependencies/cpr/test/encoded_auth_tests.cpp20
-rw-r--r--Src/external_dependencies/cpr/test/error_tests.cpp97
-rw-r--r--Src/external_dependencies/cpr/test/get_tests.cpp1305
-rw-r--r--Src/external_dependencies/cpr/test/head_tests.cpp226
-rw-r--r--Src/external_dependencies/cpr/test/httpServer.cpp914
-rw-r--r--Src/external_dependencies/cpr/test/httpServer.hpp65
-rw-r--r--Src/external_dependencies/cpr/test/httpsServer.cpp65
-rw-r--r--Src/external_dependencies/cpr/test/httpsServer.hpp42
-rw-r--r--Src/external_dependencies/cpr/test/interceptor_tests.cpp369
-rw-r--r--Src/external_dependencies/cpr/test/multiperform_tests.cpp673
-rw-r--r--Src/external_dependencies/cpr/test/options_tests.cpp73
-rw-r--r--Src/external_dependencies/cpr/test/patch_tests.cpp276
-rw-r--r--Src/external_dependencies/cpr/test/post_tests.cpp756
-rw-r--r--Src/external_dependencies/cpr/test/prepare_tests.cpp138
-rw-r--r--Src/external_dependencies/cpr/test/proxy_auth_tests.cpp94
-rw-r--r--Src/external_dependencies/cpr/test/proxy_tests.cpp92
-rw-r--r--Src/external_dependencies/cpr/test/put_tests.cpp276
-rw-r--r--Src/external_dependencies/cpr/test/raw_body_tests.cpp134
-rw-r--r--Src/external_dependencies/cpr/test/resolve_tests.cpp44
-rw-r--r--Src/external_dependencies/cpr/test/session_tests.cpp1462
-rw-r--r--Src/external_dependencies/cpr/test/ssl_tests.cpp166
-rw-r--r--Src/external_dependencies/cpr/test/structures_tests.cpp62
-rw-r--r--Src/external_dependencies/cpr/test/util_tests.cpp237
-rw-r--r--Src/external_dependencies/cpr/test/version_tests.cpp65
154 files changed, 17555 insertions, 0 deletions
diff --git a/Src/external_dependencies/cpr/.clang-format b/Src/external_dependencies/cpr/.clang-format
new file mode 100644
index 00000000..784d9b49
--- /dev/null
+++ b/Src/external_dependencies/cpr/.clang-format
@@ -0,0 +1,59 @@
+---
+Language: Cpp
+# BasedOnStyle: Google
+AccessModifierOffset: -2
+AlignAfterOpenBracket: true
+AlignEscapedNewlinesLeft: true
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AlwaysBreakAfterDefinitionReturnType: false
+AlwaysBreakTemplateDeclarations: true
+AlwaysBreakBeforeMultilineStrings: true
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BinPackParameters: true
+BinPackArguments: true
+ColumnLimit: 500
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 8
+DerivePointerAlignment: false
+ExperimentalAutoDetectBinPacking: false
+IndentCaseLabels: true
+IndentWrappedFunctionNames: false
+IndentFunctionDeclarationAfterType: false
+MaxEmptyLinesToKeep: 2
+KeepEmptyLinesAtTheStartOfBlocks: false
+NamespaceIndentation: None
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakString: 1000
+PenaltyBreakFirstLessLess: 120
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+SpacesBeforeTrailingComments: 1
+Cpp11BracedListStyle: true
+Standard: Auto
+IndentWidth: 4
+TabWidth: 8
+UseTab: Never
+BreakBeforeBraces: Attach
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpacesInAngles: false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterCStyleCast: true
+SpacesInContainerLiterals: true
+SpaceBeforeAssignmentOperators: true
+ContinuationIndentWidth: 8
+CommentPragmas: '^ IWYU pragma:'
+SpaceBeforeParens: ControlStatements
+...
diff --git a/Src/external_dependencies/cpr/.clang-tidy b/Src/external_dependencies/cpr/.clang-tidy
new file mode 100644
index 00000000..d18b254d
--- /dev/null
+++ b/Src/external_dependencies/cpr/.clang-tidy
@@ -0,0 +1,40 @@
+---
+Checks: '*,
+-cppcoreguidelines-pro-type-static-cast-downcast,
+-fuchsia-default-arguments-calls,
+-fuchsia-default-arguments,
+-fuchsia-default-arguments-declarations,
+-fuchsia-overloaded-operator,
+-fuchsia-statically-constructed-objects,
+-hicpp-use-auto,
+-modernize-use-auto,
+-modernize-use-trailing-return-type,
+-readability-implicit-bool-conversion,
+-readability-const-return-type,
+-google-runtime-references,
+-misc-non-private-member-variables-in-classes,
+-llvm-include-order,
+-cppcoreguidelines-non-private-member-variables-in-classes,
+-cppcoreguidelines-pro-type-vararg,
+-hicpp-vararg,
+-cppcoreguidelines-owning-memory,
+-llvmlibc-callee-namespace,
+-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
+-hicpp-no-array-decay,
+-modernize-pass-by-value,
+-cppcoreguidelines-pro-bounds-constant-array-index,
+-hicpp-signed-bitwise,
+-llvmlibc-implementation-in-namespace,
+-llvmlibc-restrict-system-libc-headers,
+-readability-function-cognitive-complexity,
+-readability-identifier-length,
+-altera-unroll-loops,
+-altera-id-dependent-backward-branch,
+-bugprone-easily-swappable-parameters,
+-modernize-return-braced-init-list,
+-cppcoreguidelines-avoid-magic-numbers,
+-readability-magic-numbers
+'
+WarningsAsErrors: '*'
+HeaderFilterRegex: 'src/*.hpp'
+FormatStyle: file
diff --git a/Src/external_dependencies/cpr/.gitignore b/Src/external_dependencies/cpr/.gitignore
new file mode 100644
index 00000000..5894bf1d
--- /dev/null
+++ b/Src/external_dependencies/cpr/.gitignore
@@ -0,0 +1,56 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# CMake
+CMakeCache.txt
+CMakeFiles
+Makefile
+cmake_install.cmake
+install_manifest.txt
+
+# Custom
+build/
+!nuget/build
+
+# Jekyll stuff
+_includes/
+_site/
+
+# Vim
+.ycm_extra_conf.py*
+
+# VSCode
+.vscode/
+.vs/
+
+# clangd
+.cache/
+
+# macOS
+.DS_Store
diff --git a/Src/external_dependencies/cpr/CMakeLists.txt b/Src/external_dependencies/cpr/CMakeLists.txt
new file mode 100644
index 00000000..886275dd
--- /dev/null
+++ b/Src/external_dependencies/cpr/CMakeLists.txt
@@ -0,0 +1,386 @@
+cmake_minimum_required(VERSION 3.15)
+project(cpr VERSION 1.9.0 LANGUAGES CXX)
+
+math(EXPR cpr_VERSION_NUM "${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL)
+configure_file("${cpr_SOURCE_DIR}/cmake/cprver.h.in" "${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h")
+
+# Only change the folder 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 build 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_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 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 on OSX." 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()
+
+get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if (NOT isMultiConfig)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${ALLOWED_BUILD_TYPES}")
+ if (NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
+ elseif(NOT CMAKE_BUILD_TYPE IN_LIST ALLOWED_BUILD_TYPES)
+ message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
+ endif()
+else ()
+ unset(CMAKE_BUILD_TYPE)
+ foreach(TYPE ${ALLOWED_BUILD_TYPES})
+ if (NOT ${TYPE} IN_LIST CMAKE_CONFIGURATION_TYPES)
+ list(APPEND CMAKE_CONFIGURATION_TYPES ${TYPE})
+ endif()
+ endforeach()
+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 build 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 build 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 build 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 build in version set CPR_USE_SYSTEM_CURL to OFF.")
+ endif()
+ endif()
+else()
+ message(STATUS "Configuring build 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()
+
+ # We only need HTTP (and HTTPS) support:
+ set(HTTP_ONLY ON CACHE INTERNAL "" FORCE)
+ 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(SSL_ENABLED 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(CMAKE_USE_SCHANNEL ON CACHE INTERNAL "" FORCE)
+ endif()
+
+ if(SSL_BACKEND_USED STREQUAL "OpenSSL")
+ set(CMAKE_USE_OPENSSL ON CACHE INTERNAL "" FORCE)
+ endif()
+
+ if(SSL_BACKEND_USED STREQUAL "DarwinSSL")
+ set(CMAKE_USE_SECTRANSP ON CACHE INTERNAL "" FORCE)
+ endif()
+
+ if(SSL_BACKEND_USED STREQUAL "MbedTLS")
+ set(CMAKE_USE_MBEDTLS ON CACHE INTERNAL "" FORCE)
+ endif()
+
+ message(STATUS "Enabled curl SSL")
+ else()
+ set(SSL_ENABLED OFF CACHE INTERNAL "" FORCE)
+ set(CURL_CA_PATH "none" CACHE INTERNAL "" FORCE)
+ set(CMAKE_USE_SCHANNEL OFF CACHE INTERNAL "" FORCE)
+ set(CMAKE_USE_OPENSSL OFF CACHE INTERNAL "" FORCE)
+ set(CMAKE_USE_SECTRANSP OFF CACHE INTERNAL "" FORCE)
+ set(CMAKE_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)
+
+ FetchContent_Declare(curl
+ URL https://github.com/curl/curl/releases/download/curl-7_80_0/curl-7.80.0.tar.xz
+ URL_HASH SHA256=a132bd93188b938771135ac7c1f3ac1d3ce507c1fcbef8c471397639214ae2ab # the file hash for curl-7.80.0.tar.xz
+ USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress
+ FetchContent_MakeAvailable(curl)
+
+ restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP)
+
+ # Group under the "external" project folder in IDEs such as Visual Studio.
+ if(BUILD_CURL_EXE)
+ set_property(TARGET curl PROPERTY FOLDER "external")
+ endif()
+
+ set_property(TARGET libcurl PROPERTY FOLDER "external")
+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()
diff --git a/Src/external_dependencies/cpr/CODE_OF_CONDUCT.md b/Src/external_dependencies/cpr/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..1c9ee05c
--- /dev/null
+++ b/Src/external_dependencies/cpr/CODE_OF_CONDUCT.md
@@ -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.
diff --git a/Src/external_dependencies/cpr/CONTRIBUTING.md b/Src/external_dependencies/cpr/CONTRIBUTING.md
new file mode 100644
index 00000000..abfb97c3
--- /dev/null
+++ b/Src/external_dependencies/cpr/CONTRIBUTING.md
@@ -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).
diff --git a/Src/external_dependencies/cpr/CppCheckSuppressions.txt b/Src/external_dependencies/cpr/CppCheckSuppressions.txt
new file mode 100644
index 00000000..7b811b07
--- /dev/null
+++ b/Src/external_dependencies/cpr/CppCheckSuppressions.txt
@@ -0,0 +1,2 @@
+noExplicitConstructor
+ConfigurationNotChecked
diff --git a/Src/external_dependencies/cpr/LICENSE b/Src/external_dependencies/cpr/LICENSE
new file mode 100644
index 00000000..c63edeb3
--- /dev/null
+++ b/Src/external_dependencies/cpr/LICENSE
@@ -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. \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/README.md b/Src/external_dependencies/cpr/README.md
new file mode 100644
index 00000000..3e65f321
--- /dev/null
+++ b/Src/external_dependencies/cpr/README.md
@@ -0,0 +1,162 @@
+# 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
+
+* Like you probably have noticed, `cpr` moved to a new home from https://github.com/whoshuu/cpr to https://github.com/libcpr/cpr. Read more [here](https://github.com/libcpr/cpr/issues/636).
+* 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.
+
+## 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 871ed52d350214a034f6ef8a3b8f51c5ce1bd400) # The commit hash for 1.9.0. 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 me know
+* in case you only have a `C++11` compatible compiler available, all versions below cpr 1.9.x are for you. With the upcoming release of cpr 1.10.0, we are switching 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.
diff --git a/Src/external_dependencies/cpr/cmake/FindMbedTLS.cmake b/Src/external_dependencies/cpr/cmake/FindMbedTLS.cmake
new file mode 100644
index 00000000..61ec4641
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/FindMbedTLS.cmake
@@ -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)
diff --git a/Src/external_dependencies/cpr/cmake/clang-tidy.cmake b/Src/external_dependencies/cpr/cmake/clang-tidy.cmake
new file mode 100644
index 00000000..26defad2
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/clang-tidy.cmake
@@ -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()
diff --git a/Src/external_dependencies/cpr/cmake/clear_variable.cmake b/Src/external_dependencies/cpr/cmake/clear_variable.cmake
new file mode 100644
index 00000000..469ac827
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/clear_variable.cmake
@@ -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()
diff --git a/Src/external_dependencies/cpr/cmake/code_coverage.cmake b/Src/external_dependencies/cpr/cmake/code_coverage.cmake
new file mode 100644
index 00000000..eefc4fb3
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/code_coverage.cmake
@@ -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()
diff --git a/Src/external_dependencies/cpr/cmake/cppcheck.cmake b/Src/external_dependencies/cpr/cmake/cppcheck.cmake
new file mode 100644
index 00000000..8ef46880
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/cppcheck.cmake
@@ -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()
diff --git a/Src/external_dependencies/cpr/cmake/cprConfig.cmake.in b/Src/external_dependencies/cpr/cmake/cprConfig.cmake.in
new file mode 100644
index 00000000..9c0bda5f
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/cprConfig.cmake.in
@@ -0,0 +1,8 @@
+include(CMakeFindDependencyMacro)
+@PACKAGE_INIT@
+
+find_dependency(CURL REQUIRED)
+
+include(${CMAKE_CURRENT_LIST_DIR}/cprTargets.cmake)
+
+check_required_components(cpr) \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/cmake/cprver.h.in b/Src/external_dependencies/cpr/cmake/cprver.h.in
new file mode 100644
index 00000000..e3533249
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/cprver.h.in
@@ -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
diff --git a/Src/external_dependencies/cpr/cmake/mongoose.CMakeLists.txt b/Src/external_dependencies/cpr/cmake/mongoose.CMakeLists.txt
new file mode 100644
index 00000000..f46a3d49
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/mongoose.CMakeLists.txt
@@ -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()
diff --git a/Src/external_dependencies/cpr/cmake/sanitizer.cmake b/Src/external_dependencies/cpr/cmake/sanitizer.cmake
new file mode 100644
index 00000000..09095980
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/sanitizer.cmake
@@ -0,0 +1,73 @@
+include(CheckCXXCompilerFlag)
+include(CheckCXXSourceRuns)
+
+set(ALLOWED_BUILD_TYPES Debug Release RelWithDebInfo MinSizeRel)
+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})
+ if(THREAD_SANITIZER_AVAILABLE)
+ list(APPEND ALLOWED_BUILD_TYPES ThreadSan)
+ # Do not add Thread sanitizer to all sanitizer because it is incompatible with other sanitizer
+ endif()
+ set(CMAKE_C_FLAGS_THREADSAN "${CMAKE_C_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during thread sanitizer builds." FORCE)
+ set(CMAKE_CXX_FLAGS_THREADSAN "${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_THREADSAN "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during thread sanitizer builds" FORCE)
+
+ # 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)
+ list(APPEND ALLOWED_BUILD_TYPES AddrSan)
+ set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${ADDR_SAN_FLAGS}")
+ endif()
+ set(CMAKE_C_FLAGS_ADDRSAN "${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_ADDRSAN "${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_ADDRSAN "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during address sanitizer builds" FORCE)
+
+ # Leak sanitizer
+ set(LEAK_SAN_FLAGS "-fsanitize=leak")
+ check_cxx_compiler_flag(${LEAK_SAN_FLAGS} LEAK_SANITIZER_AVAILABLE)
+ if(LEAK_SANITIZER_AVAILABLE)
+ list(APPEND ALLOWED_BUILD_TYPES LeakSan)
+ set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${LEAK_SAN_FLAGS}")
+ endif()
+ set(CMAKE_C_FLAGS_LEAKSAN "${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_LEAKSAN "${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_LEAKSAN "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during leak sanitizer builds" FORCE)
+
+ # 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)
+ list(APPEND ALLOWED_BUILD_TYPES UdefSan)
+ set(ALL_SAN_FLAGS "${ALL_SAN_FLAGS} ${UDEF_SAN_FLAGS}")
+ endif()
+ set(CMAKE_C_FLAGS_UDEFSAN "${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_UDEFSAN "${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_UDEFSAN "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during undefined behaviour sanitizer builds" FORCE)
+
+ # 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})
+ if(ALL_SANITIZERS_AVAILABLE)
+ list(APPEND ALLOWED_BUILD_TYPES AllSan)
+ endif()
+ endif()
+
+ set(CMAKE_C_FLAGS_ALLSAN "${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_ALLSAN "${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_ALLSAN "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE INTERNAL "Flags used for the linker during most possible sanitizer builds" FORCE)
+endif()
diff --git a/Src/external_dependencies/cpr/cmake/zlib_external.cmake b/Src/external_dependencies/cpr/cmake/zlib_external.cmake
new file mode 100644
index 00000000..0b494f14
--- /dev/null
+++ b/Src/external_dependencies/cpr/cmake/zlib_external.cmake
@@ -0,0 +1,22 @@
+# 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_Declare(zlib
+ GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng
+ GIT_TAG 2.0.6
+ USES_TERMINAL_DOWNLOAD TRUE)
+FetchContent_MakeAvailable(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()
diff --git a/Src/external_dependencies/cpr/cpr-config.cmake b/Src/external_dependencies/cpr/cpr-config.cmake
new file mode 100644
index 00000000..58ab4832
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr-config.cmake
@@ -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()
diff --git a/Src/external_dependencies/cpr/cpr/CMakeLists.txt b/Src/external_dependencies/cpr/cpr/CMakeLists.txt
new file mode 100644
index 00000000..9d05ad4a
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 3.15)
+
+add_library(cpr
+ accept_encoding.cpp
+ async.cpp
+ auth.cpp
+ bearer.cpp
+ cert_info.cpp
+ cookies.cpp
+ cprtypes.cpp
+ curl_container.cpp
+ curlholder.cpp
+ error.cpp
+ file.cpp
+ multipart.cpp
+ parameters.cpp
+ payload.cpp
+ proxies.cpp
+ proxyauth.cpp
+ session.cpp
+ threadpool.cpp
+ timeout.cpp
+ unix_socket.cpp
+ util.cpp
+ response.cpp
+ redirect.cpp
+ interceptor.cpp
+ ssl_ctx.cpp
+ curlmultiholder.cpp
+ multiperform.cpp)
+
+add_library(cpr::cpr ALIAS cpr)
+
+target_link_libraries(cpr PUBLIC CURL::libcurl) # todo should be private, but first dependencys 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()
diff --git a/Src/external_dependencies/cpr/cpr/accept_encoding.cpp b/Src/external_dependencies/cpr/cpr/accept_encoding.cpp
new file mode 100644
index 00000000..f6449d5f
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/accept_encoding.cpp
@@ -0,0 +1,25 @@
+#include "cpr/accept_encoding.h"
+
+#include <algorithm>
+#include <iterator>
+#include <numeric>
+
+namespace cpr {
+
+AcceptEncoding::AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods) {
+ methods_.clear();
+ std::transform(methods.begin(), methods.end(), std::back_inserter(methods_), [&](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 {
+ std::string methodsString = std::accumulate(std::next(methods_.begin()), methods_.end(), methods_[0], [](std::string a, std::string b) { return std::move(a) + ", " + std::move(b); });
+ return methodsString;
+}
+
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/async.cpp b/Src/external_dependencies/cpr/cpr/async.cpp
new file mode 100644
index 00000000..e10d09e1
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/async.cpp
@@ -0,0 +1,8 @@
+#include "cpr/async.h"
+
+namespace cpr {
+
+// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
+CPR_SINGLETON_IMPL(GlobalThreadPool)
+
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/auth.cpp b/Src/external_dependencies/cpr/cpr/auth.cpp
new file mode 100644
index 00000000..b3576f5c
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/auth.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/bearer.cpp b/Src/external_dependencies/cpr/cpr/bearer.cpp
new file mode 100644
index 00000000..02bd728b
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/bearer.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/cert_info.cpp b/Src/external_dependencies/cpr/cpr/cert_info.cpp
new file mode 100644
index 00000000..a77a0277
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/cert_info.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/cookies.cpp b/Src/external_dependencies/cpr/cpr/cookies.cpp
new file mode 100644
index 00000000..9d351fd7
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/cookies.cpp
@@ -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{};
+ 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
diff --git a/Src/external_dependencies/cpr/cpr/cprtypes.cpp b/Src/external_dependencies/cpr/cpr/cprtypes.cpp
new file mode 100644
index 00000000..7927b03b
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/cprtypes.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/curl_container.cpp b/Src/external_dependencies/cpr/cpr/curl_container.cpp
new file mode 100644
index 00000000..81f7cfb5
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/curl_container.cpp
@@ -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 += "&";
+ }
+
+ std::string escapedKey = encode ? holder.urlEncode(parameter.key) : parameter.key;
+ if (parameter.value.empty()) {
+ content += escapedKey;
+ } else {
+ 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 += "&";
+ }
+ 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
diff --git a/Src/external_dependencies/cpr/cpr/curlholder.cpp b/Src/external_dependencies/cpr/cpr/curlholder.cpp
new file mode 100644
index 00000000..acbc838f
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/curlholder.cpp
@@ -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_formfree(formpost);
+ 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
diff --git a/Src/external_dependencies/cpr/cpr/curlmultiholder.cpp b/Src/external_dependencies/cpr/cpr/curlmultiholder.cpp
new file mode 100644
index 00000000..76079370
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/curlmultiholder.cpp
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/cpr/error.cpp b/Src/external_dependencies/cpr/cpr/error.cpp
new file mode 100644
index 00000000..f085051f
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/error.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/file.cpp b/Src/external_dependencies/cpr/cpr/file.cpp
new file mode 100644
index 00000000..de64109e
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/file.cpp
@@ -0,0 +1,40 @@
+#include "cpr/file.h"
+
+namespace cpr {
+
+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();
+}
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/interceptor.cpp b/Src/external_dependencies/cpr/cpr/interceptor.cpp
new file mode 100644
index 00000000..91dd8425
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/interceptor.cpp
@@ -0,0 +1,46 @@
+#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 procceed 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!"};
+}
+
+} // namespace cpr \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/cpr/multipart.cpp b/Src/external_dependencies/cpr/cpr/multipart.cpp
new file mode 100644
index 00000000..d82d9a41
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/multipart.cpp
@@ -0,0 +1,5 @@
+#include "cpr/multipart.h"
+
+namespace cpr {
+Multipart::Multipart(const std::initializer_list<Part>& p_parts) : parts{p_parts} {}
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/multiperform.cpp b/Src/external_dependencies/cpr/cpr/multiperform.cpp
new file mode 100644
index 00000000..393185c0
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/multiperform.cpp
@@ -0,0 +1,273 @@
+#include "cpr/multiperform.h"
+
+#include <algorithm>
+#include <iostream>
+
+namespace cpr {
+
+MultiPerform::MultiPerform() : multicurl_(new CurlMultiHolder()) {}
+
+MultiPerform::~MultiPerform() {
+ // Unock all sessions
+ for (std::pair<std::shared_ptr<Session>, HttpMethod>& pair : sessions_) {
+ pair.first->isUsedInMultiPerform = false;
+ }
+}
+
+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
+ 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
+ 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;
+ }
+}
+
+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;
+ }
+ 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 (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; });
+ 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() {
+ DoMultiPerform();
+ return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.Complete(curl_error); });
+}
+
+std::vector<Response> MultiPerform::MakeDownloadRequest() {
+ DoMultiPerform();
+ return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.CompleteDownload(curl_error); });
+}
+
+void MultiPerform::PrepareSessions() {
+ for (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) {
+ 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) {
+ 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();
+}
+
+} // namespace cpr \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/cpr/parameters.cpp b/Src/external_dependencies/cpr/cpr/parameters.cpp
new file mode 100644
index 00000000..a24c3936
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/parameters.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/payload.cpp b/Src/external_dependencies/cpr/cpr/payload.cpp
new file mode 100644
index 00000000..78373fa3
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/payload.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/proxies.cpp b/Src/external_dependencies/cpr/cpr/proxies.cpp
new file mode 100644
index 00000000..0d3fe989
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/proxies.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/proxyauth.cpp b/Src/external_dependencies/cpr/cpr/proxyauth.cpp
new file mode 100644
index 00000000..01e63033
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/proxyauth.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/redirect.cpp b/Src/external_dependencies/cpr/cpr/redirect.cpp
new file mode 100644
index 00000000..fade1304
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/redirect.cpp
@@ -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));
+ 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
diff --git a/Src/external_dependencies/cpr/cpr/response.cpp b/Src/external_dependencies/cpr/cpr/response.cpp
new file mode 100644
index 00000000..effe23d9
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/response.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/session.cpp b/Src/external_dependencies/cpr/cpr/session.cpp
new file mode 100644
index 00000000..659cc710
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/session.cpp
@@ -0,0 +1,964 @@
+#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);
+ 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()) {
+ std::shared_ptr<Interceptor> interceptor = interceptors_.front();
+ interceptors_.pop();
+ return interceptor->intercept(*this);
+ }
+
+ 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()) {
+ 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:
+ 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_MAJOR >= 7
+#if LIBCURL_VERSION_MINOR >= 21
+ if (acceptEncoding_.empty()) {
+ /* enable all supported built-in compressions */
+ curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, "");
+ } else {
+ curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str());
+ }
+#endif
+#endif
+
+#if LIBCURL_VERSION_MAJOR >= 7
+#if LIBCURL_VERSION_MINOR >= 71
+ // Fix loading certs from Windows cert store when using OpenSSL:
+ curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
+#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()) {
+ 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());
+ }
+
+ 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()) {
+ // At least one interceptor exists -> Execute its intercept function
+ std::shared_ptr<Interceptor> interceptor = interceptors_.front();
+ interceptors_.pop();
+ return interceptor->intercept(*this);
+ }
+
+ 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 LIBCURL_VERSION_NUM < 0x072000
+ curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction);
+ curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &progresscb_);
+#else
+ curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction);
+ 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) {
+ curl_httppost* formpost = nullptr;
+ curl_httppost* lastptr = nullptr;
+
+ for (const Part& part : multipart.parts) {
+ std::vector<curl_forms> formdata;
+ if (!part.content_type.empty()) {
+ formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.c_str()});
+ }
+ if (part.is_file) {
+ for (const File& file : part.files) {
+ formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()});
+ formdata.push_back({CURLFORM_FILE, file.filepath.c_str()});
+ if (file.hasOverridedFilename()) {
+ formdata.push_back({CURLFORM_FILENAME, file.overrided_filename.c_str()});
+ }
+ formdata.push_back({CURLFORM_END, nullptr});
+ curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
+ formdata.clear();
+ }
+ } else if (part.is_buffer) {
+ // Do not use formdata, to prevent having to use reinterpreter_cast:
+ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, part.name.c_str(), CURLFORM_BUFFER, part.value.c_str(), CURLFORM_BUFFERPTR, part.data, CURLFORM_BUFFERLENGTH, part.datalen, CURLFORM_END);
+ } else {
+ formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()});
+ formdata.push_back({CURLFORM_COPYCONTENTS, part.value.c_str()});
+ formdata.push_back({CURLFORM_END, nullptr});
+ curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
+ }
+ }
+ curl_easy_setopt(curl_->handle, CURLOPT_HTTPPOST, formpost);
+ hasBodyOrPayload_ = true;
+
+ curl_formfree(curl_->formpost);
+ curl_->formpost = formpost;
+}
+
+void Session::SetMultipart(Multipart&& multipart) {
+ curl_httppost* formpost = nullptr;
+ curl_httppost* lastptr = nullptr;
+
+ for (const Part& part : multipart.parts) {
+ std::vector<curl_forms> formdata;
+ if (!part.content_type.empty()) {
+ formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.c_str()});
+ }
+ if (part.is_file) {
+ for (const File& file : part.files) {
+ formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()});
+ formdata.push_back({CURLFORM_FILE, file.filepath.c_str()});
+ if (file.hasOverridedFilename()) {
+ formdata.push_back({CURLFORM_FILENAME, file.overrided_filename.c_str()});
+ }
+ formdata.push_back({CURLFORM_END, nullptr});
+ curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
+ formdata.clear();
+ }
+ } else if (part.is_buffer) {
+ // Do not use formdata, to prevent having to use reinterpreter_cast:
+ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, part.name.c_str(), CURLFORM_BUFFER, part.value.c_str(), CURLFORM_BUFFERPTR, part.data, CURLFORM_BUFFERLENGTH, part.datalen, CURLFORM_END);
+ } else {
+ formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()});
+ formdata.push_back({CURLFORM_COPYCONTENTS, part.value.c_str()});
+ formdata.push_back({CURLFORM_END, nullptr});
+ curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
+ }
+ }
+ curl_easy_setopt(curl_->handle, CURLOPT_HTTPPOST, formpost);
+ hasBodyOrPayload_ = true;
+
+ curl_formfree(curl_->formpost);
+ curl_->formpost = formpost;
+}
+
+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) {
+ std::string range_str = range.str();
+ curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str());
+}
+
+void Session::SetMultiRange(const MultiRange& multi_range) {
+ 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());
+
+ 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) {
+ 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();
+}
+
+// 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
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/ssl_ctx.cpp b/Src/external_dependencies/cpr/cpr/ssl_ctx.cpp
new file mode 100644
index 00000000..f3ad9e72
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/ssl_ctx.cpp
@@ -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
+ 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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/cpr/threadpool.cpp b/Src/external_dependencies/cpr/cpr/threadpool.cpp
new file mode 100644
index 00000000..638b0932
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/threadpool.cpp
@@ -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;
+ } else if (initialRun) {
+ ++idle_thread_num;
+ initialRun = false;
+ }
+ }
+ });
+ AddThread(thread);
+ return true;
+}
+
+void ThreadPool::AddThread(std::thread* thread) {
+ thread_mutex.lock();
+ ++cur_thread_num;
+ ThreadData data;
+ data.thread = std::shared_ptr<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) {
+ 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
diff --git a/Src/external_dependencies/cpr/cpr/timeout.cpp b/Src/external_dependencies/cpr/cpr/timeout.cpp
new file mode 100644
index 00000000..5bcd73b2
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/timeout.cpp
@@ -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
diff --git a/Src/external_dependencies/cpr/cpr/unix_socket.cpp b/Src/external_dependencies/cpr/cpr/unix_socket.cpp
new file mode 100644
index 00000000..3171dbc8
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/unix_socket.cpp
@@ -0,0 +1,8 @@
+
+#include "cpr/unix_socket.h"
+
+namespace cpr {
+const char* UnixSocket::GetUnixSocketString() const noexcept {
+ return unix_socket_.data();
+}
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/cpr/util.cpp b/Src/external_dependencies/cpr/cpr/util.cpp
new file mode 100644
index 00000000..81fd3a28
--- /dev/null
+++ b/Src/external_dependencies/cpr/cpr/util.cpp
@@ -0,0 +1,236 @@
+#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("");
+ }
+ 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) {
+ 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) {
+ 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;
+}
+
+#if LIBCURL_VERSION_NUM < 0x072000
+int progressUserFunction(const ProgressCallback* progress, double dltotal, double dlnow, double ultotal, double ulnow) {
+#else
+int progressUserFunction(const ProgressCallback* progress, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
+#endif
+ return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : 1;
+} // namespace cpr::util
+
+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) {
+ 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) {
+ 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
diff --git a/Src/external_dependencies/cpr/include/CMakeLists.txt b/Src/external_dependencies/cpr/include/CMakeLists.txt
new file mode 100644
index 00000000..fbc657a2
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/CMakeLists.txt
@@ -0,0 +1,67 @@
+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/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 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})
diff --git a/Src/external_dependencies/cpr/include/cpr/accept_encoding.h b/Src/external_dependencies/cpr/include/cpr/accept_encoding.h
new file mode 100644
index 00000000..e09ad06b
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/accept_encoding.h
@@ -0,0 +1,36 @@
+#ifndef CPR_ACCEPT_ENCODING_H
+#define CPR_ACCEPT_ENCODING_H
+
+#include <curl/curlver.h>
+#include <initializer_list>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace cpr {
+
+enum class AcceptEncodingMethods {
+ identity,
+ deflate,
+ zlib,
+ gzip,
+};
+
+static const std::map<AcceptEncodingMethods, std::string> AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}};
+
+class AcceptEncoding {
+ public:
+ AcceptEncoding() = default;
+ AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods);
+ AcceptEncoding(const std::initializer_list<std::string>& methods);
+
+ bool empty() const noexcept;
+ const std::string getString() const;
+
+ private:
+ std::vector<std::string> methods_;
+};
+
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/api.h b/Src/external_dependencies/cpr/include/cpr/api.h
new file mode 100644
index 00000000..7d335237
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/api.h
@@ -0,0 +1,321 @@
+#ifndef CPR_API_H
+#define CPR_API_H
+
+#include <fstream>
+#include <functional>
+#include <future>
+#include <string>
+#include <utility>
+
+#include "cpr/async.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>
+#include <utility>
+
+namespace cpr {
+
+using AsyncResponse = std::future<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)...);
+}
+
+} // 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 std::async(
+ std::launch::async,
+ [](fs::path local_path_, Ts... ts_) {
+#ifdef CPR_USE_BOOST_FILESYSTEM
+ std::ofstream f(local_path_.string());
+#else
+ std::ofstream f(local_path_);
+#endif
+ 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();
+}
+
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/async.h b/Src/external_dependencies/cpr/include/cpr/async.h
new file mode 100644
index 00000000..6e7db4c1
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/async.h
@@ -0,0 +1,49 @@
+#ifndef CPR_ASYNC_H
+#define CPR_ASYNC_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 future, calling future.get() will wait task 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 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
diff --git a/Src/external_dependencies/cpr/include/cpr/auth.h b/Src/external_dependencies/cpr/include/cpr/auth.h
new file mode 100644
index 00000000..3354565f
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/auth.h
@@ -0,0 +1,33 @@
+#ifndef CPR_AUTH_H
+#define CPR_AUTH_H
+
+#include <string>
+
+#include <utility>
+
+namespace cpr {
+
+enum class AuthMode { BASIC, DIGEST, NTLM };
+
+class Authentication {
+ public:
+ Authentication(const std::string& username, const std::string& password, const AuthMode& auth_mode) : auth_string_{username + ":" + password}, auth_mode_{auth_mode} {}
+ 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
diff --git a/Src/external_dependencies/cpr/include/cpr/bearer.h b/Src/external_dependencies/cpr/include/cpr/bearer.h
new file mode 100644
index 00000000..3d254e2d
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/bearer.h
@@ -0,0 +1,36 @@
+#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(const std::string& token) : token_string_{token} {}
+ // 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
diff --git a/Src/external_dependencies/cpr/include/cpr/body.h b/Src/external_dependencies/cpr/include/cpr/body.h
new file mode 100644
index 00000000..0c191c54
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/body.h
@@ -0,0 +1,54 @@
+#ifndef CPR_BODY_H
+#define CPR_BODY_H
+
+#include <exception>
+#include <initializer_list>
+#include <string>
+#include <vector>
+#include <fstream>
+
+#include "cpr/buffer.h"
+#include "cpr/cprtypes.h"
+#include "cpr/file.h"
+
+namespace cpr {
+
+class Body : public StringHolder<Body> {
+ public:
+ Body() : StringHolder<Body>() {}
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ Body(const std::string& body) : StringHolder<Body>(body) {}
+ // 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) {}
+ Body(const Buffer& buffer) : StringHolder<Body>(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}
+ 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[0], 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
diff --git a/Src/external_dependencies/cpr/include/cpr/buffer.h b/Src/external_dependencies/cpr/include/cpr/buffer.h
new file mode 100644
index 00000000..17065eba
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/buffer.h
@@ -0,0 +1,35 @@
+#ifndef CPR_BUFFER_H
+#define CPR_BUFFER_H
+
+#include <string>
+
+#include <cpr/filesystem.h>
+
+namespace cpr {
+
+struct Buffer {
+ using data_t = const unsigned 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<long>(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;
+ // Ignored here since libcurl reqires a long:
+ // NOLINTNEXTLINE(google-runtime-int)
+ long datalen;
+ const fs::path filename;
+};
+
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/callback.h b/Src/external_dependencies/cpr/include/cpr/callback.h
new file mode 100644
index 00000000..e54c4fb1
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/callback.h
@@ -0,0 +1,89 @@
+#ifndef CPR_CALLBACK_H
+#define CPR_CALLBACK_H
+
+#include "cprtypes.h"
+
+#include <functional>
+#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_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
+ bool operator()(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow) const {
+ return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata);
+ }
+
+ intptr_t userdata;
+ std::function<bool(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_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;
+};
+
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/cert_info.h b/Src/external_dependencies/cpr/include/cpr/cert_info.h
new file mode 100644
index 00000000..6c328ee7
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/cert_info.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/connect_timeout.h b/Src/external_dependencies/cpr/include/cpr/connect_timeout.h
new file mode 100644
index 00000000..546e8a58
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/connect_timeout.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/cookies.h b/Src/external_dependencies/cpr/include/cpr/cookies.h
new file mode 100644
index 00000000..c018ea4a
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/cookies.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/cpr.h b/Src/external_dependencies/cpr/include/cpr/cpr.h
new file mode 100644
index 00000000..fbad1726
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/cpr.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/cprtypes.h b/Src/external_dependencies/cpr/include/cpr/cprtypes.h
new file mode 100644
index 00000000..4ad9e29b
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/cprtypes.h
@@ -0,0 +1,137 @@
+#ifndef CPR_CPR_TYPES_H
+#define CPR_CPR_TYPES_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;
+
+template <class T>
+class StringHolder {
+ public:
+ StringHolder() = default;
+ explicit StringHolder(const std::string& str) : str_(str) {}
+ 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(const std::string& url) : StringHolder<Url>(url) {}
+ // 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
diff --git a/Src/external_dependencies/cpr/include/cpr/curl_container.h b/Src/external_dependencies/cpr/include/cpr/curl_container.h
new file mode 100644
index 00000000..91a013b3
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/curl_container.h
@@ -0,0 +1,53 @@
+#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(const std::string& p_key, const std::string& p_value) : key{p_key}, value{p_value} {}
+ 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(const std::string& p_key, const std::string& p_value) : key(p_key), value(p_value) {}
+ 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 //
diff --git a/Src/external_dependencies/cpr/include/cpr/curlholder.h b/Src/external_dependencies/cpr/include/cpr/curlholder.h
new file mode 100644
index 00000000..e6913b81
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/curlholder.h
@@ -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};
+ struct curl_httppost* formpost{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
diff --git a/Src/external_dependencies/cpr/include/cpr/curlmultiholder.h b/Src/external_dependencies/cpr/include/cpr/curlmultiholder.h
new file mode 100644
index 00000000..ccd504b6
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/curlmultiholder.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/error.h b/Src/external_dependencies/cpr/include/cpr/error.h
new file mode 100644
index 00000000..bb59a4cc
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/error.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/file.h b/Src/external_dependencies/cpr/include/cpr/file.h
new file mode 100644
index 00000000..5c47b638
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/file.h
@@ -0,0 +1,55 @@
+#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_overrided_filename = {}) : filepath(std::move(p_filepath)), overrided_filename(p_overrided_filename) {}
+ explicit File(const std::string& p_filepath, const std::string& p_overrided_filename = {}) : filepath(p_filepath), overrided_filename(p_overrided_filename) {}
+
+ const std::string filepath;
+ const std::string overrided_filename;
+
+ bool hasOverridedFilename() const noexcept {
+ return !overrided_filename.empty();
+ };
+};
+
+class Files {
+ public:
+ Files() = default;
+ Files(const File& p_file) : files{p_file} {};
+ Files(const std::initializer_list<File>& p_files) : files{p_files} {};
+ Files(const std::initializer_list<std::string>& p_filepaths) {
+ for (const std::string& filepath : p_filepaths) {
+ files.emplace_back(File(filepath));
+ }
+ };
+ ~Files() noexcept = default;
+
+ using iterator = std::vector<File>::iterator;
+ using const_iterator = std::vector<File>::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 File& file);
+ void push_back(const File& file);
+ void pop_back();
+
+ private:
+ std::vector<File> files;
+};
+
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/filesystem.h b/Src/external_dependencies/cpr/include/cpr/filesystem.h
new file mode 100644
index 00000000..f498f80f
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/filesystem.h
@@ -0,0 +1,19 @@
+#ifndef CPR_FILESYSTEM_H
+#define CPR_FILESYSTEM_H
+
+// Include filesystem into the namespace "fs" from either "filesystem" or "experimental/filesystem" or "boost/filesystem"
+#if __has_include(<filesystem>)
+#include <filesystem>
+namespace fs = std::filesystem;
+#elif __has_include("experimental/filesystem")
+#include <experimental/filesystem>
+namespace fs = std::experimental::filesystem;
+//cppcheck-suppress preprocessorErrorDirective
+#elif defined(CPR_USE_BOOST_FILESYSTEM) && __has_include(<boost/filesystem.hpp>)
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
+#else
+#error "Failed to include <filesystem> header!"
+#endif
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/http_version.h b/Src/external_dependencies/cpr/include/cpr/http_version.h
new file mode 100644
index 00000000..45b50287
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/http_version.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/interceptor.h b/Src/external_dependencies/cpr/include/cpr/interceptor.h
new file mode 100644
index 00000000..fdfe69c2
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/interceptor.h
@@ -0,0 +1,36 @@
+#ifndef CPR_INTERCEPTOR_H
+#define CPR_INTERCEPTOR_H
+
+#include "cpr/response.h"
+#include "cpr/session.h"
+
+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,
+ };
+
+ virtual ~Interceptor() = 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);
+};
+
+} // namespace cpr
+
+
+#endif \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/interface.h b/Src/external_dependencies/cpr/include/cpr/interface.h
new file mode 100644
index 00000000..ded2fda1
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/interface.h
@@ -0,0 +1,34 @@
+#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() : StringHolder<Interface>() {}
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ Interface(const std::string& iface) : StringHolder<Interface>(iface) {}
+ // 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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/limit_rate.h b/Src/external_dependencies/cpr/include/cpr/limit_rate.h
new file mode 100644
index 00000000..2b0e8a24
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/limit_rate.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/local_port.h b/Src/external_dependencies/cpr/include/cpr/local_port.h
new file mode 100644
index 00000000..e853425a
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/local_port.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/local_port_range.h b/Src/external_dependencies/cpr/include/cpr/local_port_range.h
new file mode 100644
index 00000000..cc2d7e25
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/local_port_range.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/low_speed.h b/Src/external_dependencies/cpr/include/cpr/low_speed.h
new file mode 100644
index 00000000..ff77fd2e
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/low_speed.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/multipart.h b/Src/external_dependencies/cpr/include/cpr/multipart.h
new file mode 100644
index 00000000..1c4c9dbf
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/multipart.h
@@ -0,0 +1,45 @@
+#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}, value{}, 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}, value{}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{std::move(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};
+ // Ignored here since libcurl reqires a long:
+ // NOLINTNEXTLINE(google-runtime-int)
+ long 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
diff --git a/Src/external_dependencies/cpr/include/cpr/multiperform.h b/Src/external_dependencies/cpr/include/cpr/multiperform.h
new file mode 100644
index 00000000..67f1637c
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/multiperform.h
@@ -0,0 +1,119 @@
+#ifndef CPR_MULTIPERFORM_H
+#define CPR_MULTIPERFORM_H
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <vector>
+
+#include "cpr/curlmultiholder.h"
+#include "cpr/response.h"
+#include "cpr/session.h"
+
+namespace cpr {
+
+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();
+
+ 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);
+
+ private:
+ 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> 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};
+};
+
+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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/parameters.h b/Src/external_dependencies/cpr/include/cpr/parameters.h
new file mode 100644
index 00000000..0e34d4d7
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/parameters.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/payload.h b/Src/external_dependencies/cpr/include/cpr/payload.h
new file mode 100644
index 00000000..686b540e
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/payload.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/proxies.h b/Src/external_dependencies/cpr/include/cpr/proxies.h
new file mode 100644
index 00000000..6970442d
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/proxies.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/proxyauth.h b/Src/external_dependencies/cpr/include/cpr/proxyauth.h
new file mode 100644
index 00000000..16369ff1
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/proxyauth.h
@@ -0,0 +1,44 @@
+#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(const std::string& username, const std::string& password) : auth_string_{cpr::util::urlEncode(username) + ":" + cpr::util::urlEncode(password)} {}
+ 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
diff --git a/Src/external_dependencies/cpr/include/cpr/range.h b/Src/external_dependencies/cpr/include/cpr/range.h
new file mode 100644
index 00000000..2c5a145d
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/range.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/redirect.h b/Src/external_dependencies/cpr/include/cpr/redirect.h
new file mode 100644
index 00000000..32b4372c
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/redirect.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/reserve_size.h b/Src/external_dependencies/cpr/include/cpr/reserve_size.h
new file mode 100644
index 00000000..5eae4c80
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/reserve_size.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/resolve.h b/Src/external_dependencies/cpr/include/cpr/resolve.h
new file mode 100644
index 00000000..86a7c892
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/resolve.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/response.h b/Src/external_dependencies/cpr/include/cpr/response.h
new file mode 100644
index 00000000..5c296daa
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/response.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/session.h b/Src/external_dependencies/cpr/include/cpr/session.h
new file mode 100644
index 00000000..7b500926
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/session.h
@@ -0,0 +1,297 @@
+#ifndef CPR_SESSION_H
+#define CPR_SESSION_H
+
+#include <cstdint>
+#include <fstream>
+#include <future>
+#include <memory>
+#include <queue>
+
+#include "cpr/accept_encoding.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/verbose.h"
+
+namespace cpr {
+
+using AsyncResponse = std::future<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);
+
+ // 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 procceed() 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_;
+ 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};
+
+ Response makeDownloadRequest();
+ Response makeRequest();
+ Response proceed();
+ 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
diff --git a/Src/external_dependencies/cpr/include/cpr/singleton.h b/Src/external_dependencies/cpr/include/cpr/singleton.h
new file mode 100644
index 00000000..e2ea13bb
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/singleton.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/ssl_ctx.h b/Src/external_dependencies/cpr/include/cpr/ssl_ctx.h
new file mode 100644
index 00000000..d495fb2b
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/ssl_ctx.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/ssl_options.h b/Src/external_dependencies/cpr/include/cpr/ssl_options.h
new file mode 100644
index 00000000..7a1784ef
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/ssl_options.h
@@ -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)
+#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
diff --git a/Src/external_dependencies/cpr/include/cpr/status_codes.h b/Src/external_dependencies/cpr/include/cpr/status_codes.h
new file mode 100644
index 00000000..6c7acd6b
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/status_codes.h
@@ -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 \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/include/cpr/threadpool.h b/Src/external_dependencies/cpr/include/cpr/threadpool.h
new file mode 100644
index 00000000..bb7e7f21
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/threadpool.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/timeout.h b/Src/external_dependencies/cpr/include/cpr/timeout.h
new file mode 100644
index 00000000..492470ec
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/timeout.h
@@ -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{1000 * 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
diff --git a/Src/external_dependencies/cpr/include/cpr/unix_socket.h b/Src/external_dependencies/cpr/include/cpr/unix_socket.h
new file mode 100644
index 00000000..9d4d77c0
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/unix_socket.h
@@ -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
diff --git a/Src/external_dependencies/cpr/include/cpr/user_agent.h b/Src/external_dependencies/cpr/include/cpr/user_agent.h
new file mode 100644
index 00000000..369a80d9
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/user_agent.h
@@ -0,0 +1,33 @@
+#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() : StringHolder<UserAgent>() {}
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ UserAgent(const std::string& useragent) : StringHolder<UserAgent>(useragent) {}
+ // 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
diff --git a/Src/external_dependencies/cpr/include/cpr/util.h b/Src/external_dependencies/cpr/include/cpr/util.h
new file mode 100644
index 00000000..f35ad473
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/util.h
@@ -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 {
+namespace 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);
+#if LIBCURL_VERSION_NUM < 0x072000
+int progressUserFunction(const ProgressCallback* progress, double dltotal, double dlnow, double ultotal, double ulnow);
+#else
+int progressUserFunction(const ProgressCallback* progress, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
+#endif
+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 util
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/include/cpr/verbose.h b/Src/external_dependencies/cpr/include/cpr/verbose.h
new file mode 100644
index 00000000..2bf0df8f
--- /dev/null
+++ b/Src/external_dependencies/cpr/include/cpr/verbose.h
@@ -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_ */
diff --git a/Src/external_dependencies/cpr/nuget/build/native/libcpr.props b/Src/external_dependencies/cpr/nuget/build/native/libcpr.props
new file mode 100644
index 00000000..ec62c503
--- /dev/null
+++ b/Src/external_dependencies/cpr/nuget/build/native/libcpr.props
@@ -0,0 +1,29 @@
+<?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>
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\$(Platform)\$(Configuration)\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(CprLibraries);%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+</Project> \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/nuget/build/native/libcpr.targets b/Src/external_dependencies/cpr/nuget/build/native/libcpr.targets
new file mode 100644
index 00000000..700232bf
--- /dev/null
+++ b/Src/external_dependencies/cpr/nuget/build/native/libcpr.targets
@@ -0,0 +1,6 @@
+<?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>
+</Project> \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/nuget/libcpr.nuspec b/Src/external_dependencies/cpr/nuget/libcpr.nuspec
new file mode 100644
index 00000000..e47a8c08
--- /dev/null
+++ b/Src/external_dependencies/cpr/nuget/libcpr.nuspec
@@ -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> \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/nuget/resources/cpr.png b/Src/external_dependencies/cpr/nuget/resources/cpr.png
new file mode 100644
index 00000000..61ad7901
--- /dev/null
+++ b/Src/external_dependencies/cpr/nuget/resources/cpr.png
Binary files differ
diff --git a/Src/external_dependencies/cpr/package-build/build-package.sh b/Src/external_dependencies/cpr/package-build/build-package.sh
new file mode 100644
index 00000000..eef22da9
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/build-package.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+SRC_DIR=$1
+
+LIB='libcpr'
+
+LIB_DIR="${LIB}_${VERSION}"
+DEBIAN_DIR="${LIB_DIR}/debian"
+
+ARCHIVE_NAME="$LIB_DIR.orig.tar.gz"
+
+echo -e "Preparing tar archive and directory\n"
+cp -r $SRC_DIR $LIB_DIR
+
+tar --exclude-vcs -czf $ARCHIVE_NAME $LIB_DIR
+tar -xzf $ARCHIVE_NAME
+
+cd $LIB_DIR
+
+echo -e "\n\nCopying prepared debian files to directory\n"
+mkdir debian
+cp -r package-build/debian-libcpr/* debian/
+sed -i "s/\%VERSION/$VERSION/g" debian/changelog
+sed -i "s/\%DATE/$(date -R)/g" debian/changelog
+
+echo -e "\n\nCalling debuild\n"
+debuild
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/README.Debian b/Src/external_dependencies/cpr/package-build/debian-libcpr/README.Debian
new file mode 100644
index 00000000..e159461a
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/README.Debian
@@ -0,0 +1,5 @@
+libcpr for Debian
+
+A package of the libcpr library.
+
+ -- Philip Saendig <philip.saendig@gmail.com> Tue, 24 May 2022 10:37:24 +0200
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/changelog b/Src/external_dependencies/cpr/package-build/debian-libcpr/changelog
new file mode 100644
index 00000000..dbe84a94
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/changelog
@@ -0,0 +1,6 @@
+libcpr (%VERSION-1) UNRELEASED; urgency=low
+
+ [ Philip Saendig ]
+ * First package of libcpr %VERSION for debian.
+
+ -- Philip Saendig <deb@libcpr.org> %DATE
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/control b/Src/external_dependencies/cpr/package-build/debian-libcpr/control
new file mode 100644
index 00000000..e4526b1c
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/control
@@ -0,0 +1,39 @@
+Source: libcpr
+Section: libs
+Priority: optional
+Maintainer: Philip Saendig <philip.saendig@gmail.com>
+Build-Depends:
+ debhelper-compat (= 12),
+ cmake,
+ libcurl4-openssl-dev,
+ libssl-dev,
+Standards-Version: 4.5.0
+Homepage: https://github.com/libcpr/cpr
+
+Package: libcpr-dev
+Architecture: any
+Multi-Arch: same
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${misc:Depends}, ${shlibs:Depends}, libcpr1
+Description: C++ wrapper around the libcurl library - development kit
+ This package contains the header files and development
+ libraries of cpr, Curl for People.
+ .
+ The project is inspried by the Python Request project.
+ Using the more expressive language facilities of C++11,
+ it captures the essence of making network calls into a
+ few concise idioms.
+
+Package: libcpr1
+Architecture: any
+Multi-Arch: same
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${misc:Depends}, ${shlibs:Depends},
+Description: C++ wrapper around the libcurl library - runtime library
+ This package contains the runtime, shared library of cpr,
+ Curl for People.
+ .
+ The project is inspried by the Python Request project.
+ Using the more expressive language facilities of C++11,
+ it captures the essence of making network calls into a
+ few concise idioms.
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/copyright b/Src/external_dependencies/cpr/package-build/debian-libcpr/copyright
new file mode 100644
index 00000000..33b7ffc2
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/copyright
@@ -0,0 +1,52 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: libcpr
+Source: https://github.com/libcpr/cpr
+
+Files: .clang-format
+ .clang-tidy
+ .github/*
+ CMakeLists.txt
+ CODE_OF_CONDUCT.md
+ CONTRIBUTING.md
+ CppCheckSuppressions.txt
+ README.md
+ cmake/*
+ cpr-config.cmake
+ cpr/*
+ include/*
+ nuget/*
+ package-build/*
+ debian/*
+Copyright: 2017-2021 Huu Nguyen
+ 2022 libcpr and many other contributors
+License: Expat
+ MIT License
+ .
+ Copyright (c) 2017-2021 Huu Nguyen
+ Copyright (c) 2022 libcpr and many other contributors
+ .
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+Files: test/*
+Copyright: 2022 libcpr and many other contributors
+License: GPL-3
+ On Debian systems, the full text of the GNU General Public
+ License version 3 can be found in the file
+ `/usr/share/common-licenses/GPL-3'.
+
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr-dev.install b/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr-dev.install
new file mode 100644
index 00000000..bb722673
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr-dev.install
@@ -0,0 +1,3 @@
+usr/include
+usr/lib/*/*.so
+usr/lib/*/cmake
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr1.install b/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr1.install
new file mode 100644
index 00000000..3de3b10a
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/libcpr1.install
@@ -0,0 +1 @@
+usr/lib/*/*.so.*
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/rules b/Src/external_dependencies/cpr/package-build/debian-libcpr/rules
new file mode 100644
index 00000000..11b4eb1c
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/rules
@@ -0,0 +1,14 @@
+#!/usr/bin/make -f
+export DH_VERBOSE = 1
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
+export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
+
+%:
+ dh $@
+
+override_dh_auto_configure:
+ dh_auto_configure -- \
+ -DCMAKE_LIBRARY_ARCHITECTURE="$(DEB_TARGET_MULTIARCH)" -DCMAKE_BUILD_TYPE=Release \
+ -DCPR_USE_SYSTEM_CURL=ON -DCURL_ZLIB=OFF -DBUILD_SHARED_LIBS=ON
+
diff --git a/Src/external_dependencies/cpr/package-build/debian-libcpr/source/format b/Src/external_dependencies/cpr/package-build/debian-libcpr/source/format
new file mode 100644
index 00000000..163aaf8d
--- /dev/null
+++ b/Src/external_dependencies/cpr/package-build/debian-libcpr/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/Src/external_dependencies/cpr/test/CMakeLists.txt b/Src/external_dependencies/cpr/test/CMakeLists.txt
new file mode 100644
index 00000000..d84044ff
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/CMakeLists.txt
@@ -0,0 +1,80 @@
+cmake_minimum_required(VERSION 3.15)
+
+find_package(Threads REQUIRED)
+
+if (ENABLE_SSL_TESTS)
+ add_library(test_server STATIC
+ abstractServer.cpp
+ httpServer.cpp
+ httpsServer.cpp)
+else ()
+ add_library(test_server STATIC
+ abstractServer.cpp
+ httpServer.cpp)
+endif()
+if(WIN32)
+ target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest
+ PUBLIC mongoose ws2_32 wsock32)
+else()
+ target_link_libraries(test_server PRIVATE Threads::Threads cpr::cpr GTest::GTest
+ PUBLIC mongoose)
+endif()
+
+macro(add_cpr_test _TEST_NAME)
+ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)
+ target_link_libraries(${_TEST_NAME}_tests PRIVATE
+ test_server
+ GTest::GTest
+ cpr::cpr
+ CURL::libcurl)
+ add_test(NAME cpr_${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests)
+ # Group under the "tests" project folder in IDEs such as Visual Studio.
+ set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
+ if(WIN32 AND BUILD_SHARED_LIBS)
+ add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:libcurl> $<TARGET_FILE_DIR:${_TEST_NAME}_tests>)
+ add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:cpr> $<TARGET_FILE_DIR:${_TEST_NAME}_tests>)
+ endif()
+endmacro()
+
+add_cpr_test(get)
+add_cpr_test(post)
+add_cpr_test(session)
+add_cpr_test(prepare)
+add_cpr_test(async)
+if(CPR_BUILD_TESTS_PROXY)
+ add_cpr_test(proxy)
+ add_cpr_test(proxy_auth)
+endif()
+add_cpr_test(head)
+add_cpr_test(delete)
+add_cpr_test(put)
+add_cpr_test(callback)
+add_cpr_test(raw_body)
+add_cpr_test(options)
+add_cpr_test(patch)
+add_cpr_test(error)
+add_cpr_test(alternating)
+add_cpr_test(util)
+add_cpr_test(structures)
+add_cpr_test(encoded_auth)
+add_cpr_test(version)
+add_cpr_test(download)
+add_cpr_test(interceptor)
+add_cpr_test(multiperform)
+add_cpr_test(resolve)
+
+if (ENABLE_SSL_TESTS)
+ add_cpr_test(ssl)
+
+ # Install all ssl keys and certs. Explicit copy for each file to prevent issues when copying on Windows.
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:ssl_tests>/data/certificates $<TARGET_FILE_DIR:ssl_tests>/data/keys)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/client.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/client.crt)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/root-ca.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/root-ca.crt)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/certificates/server.crt $<TARGET_FILE_DIR:ssl_tests>/data/certificates/server.crt)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/client.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/client.key)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/root-ca.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/root-ca.key)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.key $<TARGET_FILE_DIR:ssl_tests>/data/keys/server.key)
+ add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.pub $<TARGET_FILE_DIR:ssl_tests>/data/keys/server.pub)
+endif()
+
+file(INSTALL data DESTINATION data)
diff --git a/Src/external_dependencies/cpr/test/LICENSE b/Src/external_dependencies/cpr/test/LICENSE
new file mode 100644
index 00000000..4d188aa7
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/LICENSE
@@ -0,0 +1,677 @@
+This license applies to everything inside this directory and all
+subdirectories.
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>. \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/test/abstractServer.cpp b/Src/external_dependencies/cpr/test/abstractServer.cpp
new file mode 100644
index 00000000..bb8eaeb0
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/abstractServer.cpp
@@ -0,0 +1,143 @@
+#include "abstractServer.hpp"
+
+namespace cpr {
+void AbstractServer::SetUp() {
+ Start();
+}
+
+void AbstractServer::TearDown() {
+ Stop();
+}
+
+void AbstractServer::Start() {
+ should_run = true;
+ serverThread = std::make_shared<std::thread>(&AbstractServer::Run, this);
+ serverThread->detach();
+ std::unique_lock<std::mutex> server_lock(server_mutex);
+ server_start_cv.wait(server_lock);
+}
+
+void AbstractServer::Stop() {
+ should_run = false;
+ std::unique_lock<std::mutex> server_lock(server_mutex);
+ server_stop_cv.wait(server_lock);
+}
+
+static void EventHandler(mg_connection* conn, int event, void* event_data, void* context) {
+ switch (event) {
+ case MG_EV_READ:
+ case MG_EV_WRITE:
+ /** Do nothing. Just for housekeeping. **/
+ break;
+ case MG_EV_POLL:
+ /** Do nothing. Just for housekeeping. **/
+ break;
+ case MG_EV_CLOSE:
+ /** Do nothing. Just for housekeeping. **/
+ break;
+ case MG_EV_ACCEPT:
+ /* Initialize HTTPS connection if Server is an HTTPS Server */
+ static_cast<AbstractServer*>(context)->acceptConnection(conn);
+ break;
+ case MG_EV_CONNECT:
+ /** Do nothing. Just for housekeeping. **/
+ break;
+
+ case MG_EV_HTTP_CHUNK: {
+ /** Do nothing. Just for housekeeping. **/
+ } break;
+
+ case MG_EV_HTTP_MSG: {
+ AbstractServer* server = static_cast<AbstractServer*>(context);
+ server->OnRequest(conn, static_cast<mg_http_message*>(event_data));
+ } break;
+
+ default:
+ break;
+ }
+}
+
+void AbstractServer::Run() {
+ // Setup a new mongoose http server.
+ memset(&mgr, 0, sizeof(mg_mgr));
+ initServer(&mgr, EventHandler);
+
+ // Notify the main thread that the server is up and runing:
+ server_start_cv.notify_all();
+
+ // Main server loop:
+ while (should_run) {
+ // NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers)
+ mg_mgr_poll(&mgr, 100);
+ }
+
+ // Shutdown and cleanup:
+ timer_args.clear();
+ mg_mgr_free(&mgr);
+
+ // Notify the main thread that we have shut down everything:
+ server_stop_cv.notify_all();
+}
+
+static const std::string base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+/**
+ * Decodes the given BASE64 string to a normal string.
+ * Source: https://gist.github.com/williamdes/308b95ac9ef1ee89ae0143529c361d37
+ **/
+std::string AbstractServer::Base64Decode(const std::string& in) {
+ std::string out;
+
+ std::vector<int> T(256, -1);
+ for (size_t i = 0; i < 64; i++)
+ T[base64_chars[i]] = static_cast<int>(i);
+
+ int val = 0;
+ int valb = -8;
+ for (unsigned char c : in) {
+ if (T[c] == -1) {
+ break;
+ }
+ val = (val << 6) + T[c];
+ valb += 6;
+ if (valb >= 0) {
+ out.push_back(char((val >> valb) & 0xFF));
+ valb -= 8;
+ }
+ }
+ return out;
+}
+
+// Sends error similar like in mongoose 6 method mg_http_send_error
+// https://github.com/cesanta/mongoose/blob/6.18/mongoose.c#L7081-L7089
+void AbstractServer::SendError(mg_connection* conn, int code, std::string& reason) {
+ std::string headers{"Content-Type: text/plain\r\nConnection: close\r\n"};
+ mg_http_reply(conn, code, headers.c_str(), reason.c_str());
+}
+
+// Checks whether a pointer to a connection is still managed by a mg_mgr.
+// This check tells whether it is still possible to send a message via the given connection
+// Note that it is still possible that the pointer of an old connection object may be reused by mongoose.
+// In this case, the active connection might refer to a different connection than the one the caller refers to
+bool AbstractServer::IsConnectionActive(mg_mgr* mgr, mg_connection* conn) {
+ mg_connection* c{mgr->conns};
+ while (c) {
+ if (c == conn) {
+ return true;
+ }
+ c = c->next;
+ }
+ return false;
+}
+
+uint16_t AbstractServer::GetRemotePort(const mg_connection* conn) {
+ return (conn->rem.port >> 8) | (conn->rem.port << 8);
+}
+
+uint16_t AbstractServer::GetLocalPort(const mg_connection* conn) {
+ return (conn->loc.port >> 8) | (conn->loc.port << 8);
+}
+
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/test/abstractServer.hpp b/Src/external_dependencies/cpr/test/abstractServer.hpp
new file mode 100644
index 00000000..d2daec26
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/abstractServer.hpp
@@ -0,0 +1,70 @@
+#ifndef CPR_TEST_ABSTRACT_SERVER_SERVER_H
+#define CPR_TEST_ABSTRACT_SERVER_SERVER_H
+
+#include <atomic>
+#include <condition_variable>
+#include <gtest/gtest.h>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "cpr/cpr.h"
+#include "mongoose.h"
+
+namespace cpr {
+
+// Helper struct for functions using timers to simulate slow connections
+struct TimerArg {
+ mg_mgr* mgr;
+ mg_connection* connection;
+ unsigned long connection_id;
+ mg_timer timer;
+ unsigned counter;
+
+ explicit TimerArg(mg_mgr* m, mg_connection* c, mg_timer&& t) : mgr{m}, connection{c}, connection_id{0}, timer{t}, counter{0} {}
+
+ ~TimerArg() {
+ mg_timer_free(&mgr->timers, &timer);
+ }
+};
+
+class AbstractServer : public testing::Environment {
+ public:
+ ~AbstractServer() override = default;
+
+ void SetUp() override;
+ void TearDown() override;
+
+ void Start();
+ void Stop();
+
+ virtual std::string GetBaseUrl() = 0;
+ virtual uint16_t GetPort() = 0;
+
+ virtual void acceptConnection(mg_connection* conn) = 0;
+ virtual void OnRequest(mg_connection* conn, mg_http_message* msg) = 0;
+
+ private:
+ std::shared_ptr<std::thread> serverThread{nullptr};
+ std::mutex server_mutex;
+ std::condition_variable server_start_cv;
+ std::condition_variable server_stop_cv;
+ std::atomic<bool> should_run{false};
+
+ void Run();
+
+ protected:
+ mg_mgr mgr{};
+ std::vector<std::unique_ptr<TimerArg>> timer_args{};
+ virtual mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) = 0;
+
+ static std::string Base64Decode(const std::string& in);
+ static void SendError(mg_connection* conn, int code, std::string& reason);
+ static bool IsConnectionActive(mg_mgr* mgr, mg_connection* conn);
+
+ static uint16_t GetRemotePort(const mg_connection* conn);
+ static uint16_t GetLocalPort(const mg_connection* conn);
+};
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/test/alternating_tests.cpp b/Src/external_dependencies/cpr/test/alternating_tests.cpp
new file mode 100644
index 00000000..d0466f67
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/alternating_tests.cpp
@@ -0,0 +1,163 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(AlternatingTests, PutGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Header reflect PUT"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Response response = cpr::Get(url);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(AlternatingTests, PutGetPutGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Header reflect PUT"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Response response = cpr::Get(url);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Header reflect PUT"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Response response = cpr::Get(url);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(AlternatingTests, HeadGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+
+ {
+ // Head shouldn't return a body
+ Response response = cpr::Head(url);
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Response response = cpr::Get(url);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(AlternatingTests, PutHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Header reflect PUT"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ // Head shouldn't return a body
+ Response response = cpr::Head(url);
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(AlternatingTests, PutPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Header reflect PUT"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+
+ {
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Post(url, payload);
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/async_tests.cpp b/Src/external_dependencies/cpr/test/async_tests.cpp
new file mode 100644
index 00000000..c4f15830
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/async_tests.cpp
@@ -0,0 +1,79 @@
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <cpr/cpr.h>
+#include <cpr/filesystem.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+bool write_data(std::string /*data*/, intptr_t /*userdata*/) {
+ return true;
+}
+
+TEST(UrlEncodedPostTests, AsyncGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ cpr::AsyncResponse future = cpr::GetAsync(url);
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+}
+
+TEST(UrlEncodedPostTests, AsyncGetMultipleTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::GetAsync(url));
+ }
+ for (cpr::AsyncResponse& future : responses) {
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ }
+}
+
+TEST(UrlEncodedPostTests, AsyncGetMultipleReflectTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 100; ++i) {
+ Parameters p{{"key", std::to_string(i)}};
+ responses.emplace_back(cpr::GetAsync(url, p));
+ }
+ int i = 0;
+ for (cpr::AsyncResponse& future : responses) {
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ Url expected_url{url + "?key=" + std::to_string(i)};
+ EXPECT_EQ(expected_url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ ++i;
+ }
+}
+
+TEST(UrlEncodedPostTests, AsyncDownloadTest) {
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::AsyncResponse future = cpr::DownloadAsync(fs::path{"/tmp/aync_download"}, url, cpr::Header{{"Accept-Encoding", "gzip"}}, cpr::WriteCallback{write_data, 0});
+ cpr::Response response = future.get();
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/callback_tests.cpp b/Src/external_dependencies/cpr/test/callback_tests.cpp
new file mode 100644
index 00000000..834f960a
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/callback_tests.cpp
@@ -0,0 +1,931 @@
+#include <cstddef>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <cpr/cpr.h>
+
+#include "cpr/cprtypes.h"
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+std::chrono::milliseconds sleep_time{50};
+std::chrono::seconds zero{0};
+
+int status_callback(int& status_code, Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+}
+
+int status_callback_ref(int& status_code, const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+}
+
+std::string text_callback(std::string& expected_text, Response r) {
+ expected_text = r.text;
+ return r.text;
+}
+
+std::string text_callback_ref(std::string& expected_text, const Response& r) {
+ expected_text = r.text;
+ return r.text;
+}
+
+TEST(CallbackGetTests, CallbackGetLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::GetCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::GetCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::GetCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::GetCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::GetCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::GetCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::GetCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackGetTests, CallbackGetFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::GetCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ int status_code = 0;
+ auto future = cpr::DeleteCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::string expected_text{};
+ auto future = cpr::DeleteCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ int status_code = 0;
+ auto future = cpr::DeleteCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::string expected_text{};
+ auto future = cpr::DeleteCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::DeleteCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::DeleteCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::DeleteCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackDeleteTests, CallbackDeleteFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::DeleteCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::HeadCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::HeadCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::HeadCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::HeadCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::HeadCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::HeadCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::HeadCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackHeadTests, CallbackHeadFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::HeadCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PostCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PostCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PostCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PostCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PostCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PostCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PostCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPostTests, CallbackPostFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PostCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PutCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PutCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PutCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PutCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PutCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PutCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PutCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPutTests, CallbackPutFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PutCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::OptionsCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::OptionsCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto future = cpr::OptionsCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto future = cpr::OptionsCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::OptionsCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::OptionsCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::OptionsCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackOptionsTests, CallbackOptionsFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::OptionsCallback(callback, url);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchLambdaStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PatchCallback(
+ [&status_code](Response r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchLambdaTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PatchCallback(
+ [&expected_text](Response r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchLambdaStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto future = cpr::PatchCallback(
+ [&status_code](const Response& r) {
+ status_code = r.status_code;
+ return r.status_code;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchLambdaTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto future = cpr::PatchCallback(
+ [&expected_text](const Response& r) {
+ expected_text = r.text;
+ return r.text;
+ },
+ url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchFunctionStatusTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PatchCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PatchCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchFunctionStatusReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ int status_code = 0;
+ auto callback = std::function<int(Response)>(std::bind(status_callback_ref, std::ref(status_code), std::placeholders::_1));
+ auto future = cpr::PatchCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(status_code, future.get());
+}
+
+TEST(CallbackPatchTests, CallbackPatchFunctionTextReferenceTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::string expected_text{};
+ auto callback = std::function<std::string(Response)>(std::bind(text_callback_ref, std::ref(expected_text), std::placeholders::_1));
+ auto future = cpr::PatchCallback(callback, url, payload);
+ std::this_thread::sleep_for(sleep_time);
+ EXPECT_EQ(future.wait_for(zero), std::future_status::ready);
+ EXPECT_EQ(expected_text, future.get());
+}
+
+TEST(CallbackDataTests, CallbackReadFunctionCancelTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, cpr::ReadCallback([](char* /*buffer*/, size_t& /*size*/, intptr_t /*userdata*/) -> size_t { return false; }));
+ EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
+}
+
+TEST(CallbackDataTests, CallbackReadFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ unsigned count = 0;
+ Response response = cpr::Post(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t {
+ std::string data;
+ ++count;
+ switch (count) {
+ case 1:
+ data = "x=";
+ break;
+ case 2:
+ data = "5";
+ break;
+ default:
+ return false;
+ }
+ std::copy(data.begin(), data.end(), buffer);
+ size = data.size();
+ return true;
+ }});
+ EXPECT_EQ(2, count);
+ EXPECT_EQ(expected_text, response.text);
+}
+
+TEST(CallbackDataTests, CallbackReadFunctionTextTestPut) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ unsigned count = 0;
+ Response response = cpr::Put(url, cpr::ReadCallback{3, [&](char* buffer, size_t& size, intptr_t /*userdata*/) -> size_t {
+ std::string data;
+ ++count;
+ switch (count) {
+ case 1:
+ data = "x=";
+ break;
+ case 2:
+ data = "5";
+ break;
+ default:
+ return false;
+ }
+ std::copy(data.begin(), data.end(), buffer);
+ size = data.size();
+ return true;
+ }});
+ EXPECT_EQ(2, count);
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+/**
+ * Checks if the "Transfer-Encoding" header will be kept when using headers and a read callback.
+ * Issue: https://github.com/whoshuu/cpr/issues/517
+ **/
+TEST(CallbackDataTests, CallbackReadFunctionHeaderTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::string data = "Test";
+ Response response = cpr::Post(url,
+ cpr::ReadCallback{-1,
+ [&](char* /*buffer*/, size_t& size, intptr_t /*userdata*/) -> size_t {
+ size = 0;
+ return true;
+ }},
+ Header{{"TestHeader", "42"}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+
+ // Check Header:
+ EXPECT_EQ(std::string{"42"}, response.header["TestHeader"]); // Set by us
+ EXPECT_TRUE(response.header.find("TestHeader") != response.header.end());
+ EXPECT_EQ(std::string{"chunked"}, response.header["Transfer-Encoding"]); // Set by the read callback
+ EXPECT_TRUE(response.header.find("Transfer-Encoding") != response.header.end());
+}
+
+/* cesanta mongoose doesn't support chunked requests yet
+TEST(CallbackDataTests, CallbackReadFunctionChunkedTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ unsigned count = 0;
+ Response response = cpr::Post(url, cpr::ReadCallback{[&count](char* buffer, size_t & size) -> size_t {
+ std::string data;
+ ++ count;
+ switch (count) {
+ case 1:
+ data = "x=";
+ break;
+ case 2:
+ data = "5";
+ break;
+ default:
+ data = "";
+ break;
+ }
+ std::copy(data.begin(), data.end(), buffer);
+ size = data.size();
+ return true;
+ }});
+ EXPECT_EQ(3, count);
+ EXPECT_EQ(expected_text, response.text);
+}
+*/
+
+TEST(CallbackDataTests, CallbackHeaderFunctionCancelTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = Post(url, HeaderCallback{[](std::string /*header*/, intptr_t /*userdata*/) -> bool { return false; }});
+ EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
+}
+
+TEST(CallbackDataTests, CallbackHeaderFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::vector<std::string> expected_headers{"HTTP/1.1 201 Created\r\n", "Content-Type: application/json\r\n", "\r\n"};
+ std::set<std::string> response_headers;
+ Post(url, HeaderCallback{[&response_headers](std::string header, intptr_t /*userdata*/) -> bool {
+ response_headers.insert(header);
+ return true;
+ }});
+ for (std::string& header : expected_headers) {
+ std::cout << header << std::endl;
+ EXPECT_TRUE(response_headers.count(header));
+ }
+}
+
+TEST(CallbackDataTests, CallbackWriteFunctionCancelTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = Post(url, WriteCallback{[](std::string /*header*/, intptr_t /*userdata*/) -> bool { return false; }});
+ EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
+}
+
+TEST(CallbackDataTests, CallbackWriteFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ std::string response_text;
+ Post(url, Payload{{"x", "5"}}, WriteCallback{[&response_text](std::string header, intptr_t /*userdata*/) -> bool {
+ response_text.append(header);
+ return true;
+ }});
+ EXPECT_EQ(expected_text, response_text);
+}
+
+TEST(CallbackDataTests, CallbackProgressFunctionCancelTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = Post(url, ProgressCallback{[](size_t /*downloadTotal*/, size_t /*downloadNow*/, size_t /*uploadTotal*/, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool { return false; }});
+ EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
+}
+
+TEST(CallbackDataTests, CallbackProgressFunctionTotalTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Body body{"x=5"};
+ size_t response_upload = 0, response_download = 0;
+ Response response = Post(url, body, ProgressCallback{[&](size_t downloadTotal, size_t /*downloadNow*/, size_t uploadTotal, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool {
+ response_upload = uploadTotal;
+ response_download = downloadTotal;
+ return true;
+ }});
+ EXPECT_EQ(body.str().length(), response_upload);
+ EXPECT_EQ(response.text.length(), response_download);
+}
+
+TEST(CallbackDataTests, CallbackDebugFunctionTextTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Body body{"x=5"};
+ std::string debug_body;
+ Response response = Post(url, body, DebugCallback{[&](DebugCallback::InfoType type, std::string data, intptr_t /*userdata*/) {
+ if (type == DebugCallback::InfoType::DATA_OUT) {
+ debug_body = data;
+ }
+ }});
+ EXPECT_EQ(body.str(), debug_body);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/data/certificates/client.crt b/Src/external_dependencies/cpr/test/data/certificates/client.crt
new file mode 100644
index 00000000..0583f543
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/certificates/client.crt
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBejCCASygAwIBAgIQKMJShx7GKmJqmABrC/KIkDAFBgMrZXAwMTELMAkGA1UE
+BhMCR0IxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMjIw
+NjI5MTEzMzA3WhcNMjcwNjI4MTEzMzA3WjAWMRQwEgYDVQQDDAt0ZXN0LWNsaWVu
+dDAqMAUGAytlcAMhAOGArRN1SIicY6uB/2CRB668fBEDTQb1oLcCoTsYQetho3Uw
+czAfBgNVHSMEGDAWgBTk8vOFDreFdYR240PRtp0UuOKktzAMBgNVHRMBAf8EAjAA
+MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQU
+a5RqAAt7DpJN8iHcLvTjH2TIKtowBQYDK2VwA0EApzcNlIuTMToyqyWZ0FhxikP/
+c2TS6u5qkP+YHgcJJkvJ0rRTXs164k4LpvlMG0gNxle4zfoAJQ8mAAMZcQKyAg==
+-----END CERTIFICATE-----
diff --git a/Src/external_dependencies/cpr/test/data/certificates/root-ca.crt b/Src/external_dependencies/cpr/test/data/certificates/root-ca.crt
new file mode 100644
index 00000000..32d7ba97
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/certificates/root-ca.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBrjCCAWCgAwIBAgIQKMJShx7GKmJqmABrC/KIjjAFBgMrZXAwMTELMAkGA1UE
+BhMCR0IxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMjIw
+NjI5MTEzMzA3WhcNMzIwNjI2MTEzMzA3WjAxMQswCQYDVQQGEwJHQjEQMA4GA1UE
+CgwHRXhhbXBsZTEQMA4GA1UEAwwHUm9vdCBDQTAqMAUGAytlcAMhAJqzaumMKuMm
+htBGbS+UCrCmXbGb+lRcuO71mPRey7HXo4GNMIGKMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBTk8vOFDreFdYR240PRtp0UuOKktzBI
+BgNVHR4EQTA/oD0wC4IJbG9jYWxob3N0MAqHCH8AAAH/AAAAMCKHIAAAAAAAAAAA
+AAAAAAAAAAH/////////////////////MAUGAytlcANBAESQBu1/oyaeYouu3q+h
+VbIDkQiyZT4sPRYautZZ+xrN4MkNWDtwLeVJ+a9N0YU9vDpOviJpvXN4H/EEBwBF
+3AA=
+-----END CERTIFICATE-----
diff --git a/Src/external_dependencies/cpr/test/data/certificates/server.crt b/Src/external_dependencies/cpr/test/data/certificates/server.crt
new file mode 100644
index 00000000..da572028
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/certificates/server.crt
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBdTCCASegAwIBAgIQKMJShx7GKmJqmABrC/KIjzAFBgMrZXAwMTELMAkGA1UE
+BhMCR0IxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMjIw
+NjI5MTEzMzA3WhcNMjcwNjI4MTEzMzA3WjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl
+cjAqMAUGAytlcAMhAI64JU5RjfdEG1KQMxS5DQWkiGlKIQO7ye4mNFq9QleTo3Aw
+bjAsBgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEw
+HQYDVR0OBBYEFDnBgTgB3FU45S9OetBMhHu3J9OvMB8GA1UdIwQYMBaAFOTy84UO
+t4V1hHbjQ9G2nRS44qS3MAUGAytlcANBAC4NoQ31kHfp64R9gGNjTYrr2SNXHyEq
+7YG0qFi5ABvLXJAbM2v27EIgY1TWYO43FBsclQsz6mcp1MzZfjT9RwQ=
+-----END CERTIFICATE-----
diff --git a/Src/external_dependencies/cpr/test/data/client.cnf b/Src/external_dependencies/cpr/test/data/client.cnf
new file mode 100644
index 00000000..d387d39d
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/client.cnf
@@ -0,0 +1,8 @@
+# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html
+[req]
+prompt = no
+distinguished_name = dn
+
+[dn]
+CN = test-client
+
diff --git a/Src/external_dependencies/cpr/test/data/generate-certificates.sh b/Src/external_dependencies/cpr/test/data/generate-certificates.sh
new file mode 100644
index 00000000..f20d7729
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/generate-certificates.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# Generate a CA with a self-signed root certificate that then signs the server certificate
+# Based on the OpenSSL Cookbook by Ivan Ristic:
+# https://www.feistyduck.com/library/openssl-cookbook/online/
+#
+# Especially, see chapter 1.5. Creating a private Certification Authority:
+# https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca.html
+
+export KEY_PATH=keys
+export CRT_PATH=certificates
+export CA_PATH=ca
+
+# Create environment.
+# $CA_PATH is deleted in the end.
+# If new certificates need to be issued, this needs to be done before the cleanup in the end.
+mkdir -p $KEY_PATH $CRT_PATH $CA_PATH/db $CA_PATH/private $CA_PATH/certificates
+touch $CA_PATH/db/index
+openssl rand -hex 16 > $CA_PATH/db/serial
+
+
+# Generate all private keys
+openssl genpkey -algorithm ed25519 -out $KEY_PATH/root-ca.key
+openssl genpkey -algorithm ed25519 -out $KEY_PATH/server.key
+openssl genpkey -algorithm ed25519 -out $KEY_PATH/client.key
+
+# For the server, we also need the public key
+openssl pkey -in $KEY_PATH/server.key -pubout -out $KEY_PATH/server.pub
+
+
+# Generate a Certificate Signing Request for the Root CA based on a config file
+openssl req -new \
+ -config root-ca.cnf -out root-ca.csr \
+ -key $KEY_PATH/root-ca.key
+
+# Self-sign the root certificate
+openssl ca -batch \
+ -selfsign -config root-ca.cnf \
+ -extensions ca_ext \
+ -in root-ca.csr -out $CRT_PATH/root-ca.crt -notext
+
+
+# Create a Certificate Signing request for the server certificate
+openssl req -new \
+ -config server.cnf -out server.csr \
+ -key $KEY_PATH/server.key
+openssl req -text -in server.csr -noout
+
+# Issue the server certificate
+openssl ca -batch \
+ -config root-ca.cnf \
+ -extensions server_ext \
+ -extfile server.cnf -extensions ext \
+ -in server.csr -out $CRT_PATH/server.crt -notext \
+ -days 1825
+
+
+# Create a Certificate Signing request for the client certificate
+openssl req -new \
+ -config client.cnf -out client.csr \
+ -key $KEY_PATH/client.key
+
+# Issue the client certificate
+openssl ca -batch \
+ -config root-ca.cnf \
+ -extensions client_ext \
+ -in client.csr -out $CRT_PATH/client.crt -notext \
+ -days 1825
+
+
+
+# Clean up
+# IMPORTANT: If new certificates should be issued, $CA_PATH and its files MUST NOT be deleted!
+# New certificates can be created in this script before cleaning up.
+rm -rf *.csr $CA_PATH
+
diff --git a/Src/external_dependencies/cpr/test/data/keys/client.key b/Src/external_dependencies/cpr/test/data/keys/client.key
new file mode 100644
index 00000000..120f9e05
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/keys/client.key
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIPTCPxm8reXOE2aIrafTcibvg4f6Rg1/F2LVk12EILzJ
+-----END PRIVATE KEY-----
diff --git a/Src/external_dependencies/cpr/test/data/keys/root-ca.key b/Src/external_dependencies/cpr/test/data/keys/root-ca.key
new file mode 100644
index 00000000..a574c0be
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/keys/root-ca.key
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIHbCvDGMRz5Ky+7gJvQYZ5t+5sZyHI+UcAKWvS20CoLU
+-----END PRIVATE KEY-----
diff --git a/Src/external_dependencies/cpr/test/data/keys/server.key b/Src/external_dependencies/cpr/test/data/keys/server.key
new file mode 100644
index 00000000..bfdefcb1
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/keys/server.key
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIGVXwKYyi/u52mmDVC56TSorC/GGNqgyiW4+jsDno81i
+-----END PRIVATE KEY-----
diff --git a/Src/external_dependencies/cpr/test/data/keys/server.pub b/Src/external_dependencies/cpr/test/data/keys/server.pub
new file mode 100644
index 00000000..715576ad
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/keys/server.pub
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAjrglTlGN90QbUpAzFLkNBaSIaUohA7vJ7iY0Wr1CV5M=
+-----END PUBLIC KEY-----
diff --git a/Src/external_dependencies/cpr/test/data/root-ca.cnf b/Src/external_dependencies/cpr/test/data/root-ca.cnf
new file mode 100644
index 00000000..9a1fd65d
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/root-ca.cnf
@@ -0,0 +1,69 @@
+# Based on: https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-creating-root.html
+[default]
+name = root-ca
+default_ca = ca_default
+name_opt = utf8,esc_ctrl,multiline,lname,align
+
+[ca_dn]
+countryName = "GB"
+organizationName = "Example"
+commonName = "Root CA"
+
+[ca_default]
+home = ./${ENV::CA_PATH}
+database = $home/db/index
+serial = $home/db/serial
+certificate = ./${ENV::CRT_PATH}/$name.crt
+private_key = ./${ENV::KEY_PATH}/$name.key
+RANDFILE = $home/private/random
+new_certs_dir = $home/certificates
+unique_subject = no
+copy_extensions = none
+default_days = 3650
+default_md = sha256
+policy = policy_cn_supplied
+
+[policy_cn_supplied]
+countryName = optional
+stateOrProvinceName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[req]
+default_bits = 4096
+encrypt_key = yes
+default_md = sha256
+utf8 = yes
+string_mask = utf8only
+prompt = no
+distinguished_name = ca_dn
+req_extensions = ca_ext
+
+[ca_ext]
+basicConstraints = critical,CA:true
+keyUsage = critical,keyCertSign
+subjectKeyIdentifier = hash
+nameConstraints = @name_constraints
+
+
+[server_ext]
+authorityKeyIdentifier = keyid:always
+basicConstraints = critical,CA:false
+extendedKeyUsage = clientAuth,serverAuth
+keyUsage = critical,digitalSignature,keyEncipherment
+subjectKeyIdentifier = hash
+
+[client_ext]
+authorityKeyIdentifier = keyid:always
+basicConstraints = critical,CA:false
+extendedKeyUsage = clientAuth
+keyUsage = critical,digitalSignature
+subjectKeyIdentifier = hash
+
+[name_constraints]
+permitted;DNS.0=localhost
+permitted;IP.0=127.0.0.1/255.0.0.0
+permitted;IP.1=::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+
diff --git a/Src/external_dependencies/cpr/test/data/server.cnf b/Src/external_dependencies/cpr/test/data/server.cnf
new file mode 100644
index 00000000..a67fe34a
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/data/server.cnf
@@ -0,0 +1,12 @@
+# Based on https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/private-ca-create-subordinate.html
+[req]
+prompt = no
+distinguished_name = dn
+req_extensions = ext
+
+[dn]
+CN = test-server
+
+[ext]
+subjectAltName = DNS:localhost,IP:127.0.0.1,IP:::1
+
diff --git a/Src/external_dependencies/cpr/test/delete_tests.cpp b/Src/external_dependencies/cpr/test/delete_tests.cpp
new file mode 100644
index 00000000..50856dfa
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/delete_tests.cpp
@@ -0,0 +1,259 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(DeleteTests, DeleteTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ Response response = cpr::Delete(url);
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, DeleteUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ Response response = cpr::Delete(url);
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, DeleteJsonBodyTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ Response response = cpr::Delete(url, cpr::Body{"'foo': 'bar'"}, cpr::Header{{"Content-Type", "application/json"}});
+ std::string expected_text{"'foo': 'bar'"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ Session session;
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ Session session;
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteJsonBodyTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Content-Type", "application/json"}});
+ session.SetBody(cpr::Body{"{'foo': 'bar'}"});
+ Response response = session.Delete();
+ std::string expected_text{"{'foo': 'bar'}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteUnallowedAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteUnallowedAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, SessionDeleteUnallowedAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, AsyncDeleteTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ cpr::AsyncResponse future_response = cpr::DeleteAsync(url);
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, AsyncDeleteUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ cpr::AsyncResponse future_response = cpr::DeleteAsync(url);
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DeleteTests, AsyncMultipleDeleteTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::DeleteAsync(url));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(DeleteTests, AsyncMultipleDeleteUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/delete_unallowed.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::DeleteAsync(url));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/download_tests.cpp b/Src/external_dependencies/cpr/test/download_tests.cpp
new file mode 100644
index 00000000..e9fa7206
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/download_tests.cpp
@@ -0,0 +1,118 @@
+#include <cstddef>
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "cpr/api.h"
+#include "cpr/callback.h"
+#include "cpr/cprtypes.h"
+#include "cpr/session.h"
+#include "httpServer.hpp"
+
+
+static cpr::HttpServer* server = new cpr::HttpServer();
+
+bool write_data(std::string /*data*/, intptr_t /*userdata*/) {
+ return true;
+}
+
+TEST(DownloadTests, DownloadGzip) {
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetUrl(url);
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+}
+
+TEST(DownloadTests, RangeTestWholeFile) {
+ const int64_t download_size = 9;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetRange(cpr::Range{std::nullopt, std::nullopt});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+TEST(DownloadTests, RangeTestLowerLimit) {
+ const int64_t download_size = 8;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetRange(cpr::Range{1, std::nullopt});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(206, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+TEST(DownloadTests, RangeTestUpperLimit) {
+ const int64_t download_size = 6;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetRange(cpr::Range{std::nullopt, download_size - 1});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(206, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+TEST(DownloadTests, RangeTestLowerAndUpperLimit) {
+ const int64_t download_size = 2;
+ const int64_t start_from = 2;
+ const int64_t finish_at = start_from + download_size - 1;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetRange(cpr::Range{start_from, finish_at});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(206, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+TEST(DownloadTests, RangeTestMultipleRangesSet) {
+ const int64_t num_parts = 2;
+ const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 6 /*data*/;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetMultiRange(cpr::MultiRange{cpr::Range{std::nullopt, 3}, cpr::Range{5, 6}});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(206, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+TEST(DownloadTests, RangeTestMultipleRangesOption) {
+ const int64_t num_parts = 3;
+ const int64_t download_size = num_parts * (26 /*content range*/ + 4 /*\n*/) + ((num_parts + 1) * 15 /*boundary*/) + 2 /*--*/ + 7 /*data*/;
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetOption(cpr::MultiRange{cpr::Range{std::nullopt, 2}, cpr::Range{4, 5}, cpr::Range{7, 8}});
+ cpr::Response response = session.Download(cpr::WriteCallback{write_data, 0});
+ EXPECT_EQ(206, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+ EXPECT_EQ(download_size, response.downloaded_bytes);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/encoded_auth_tests.cpp b/Src/external_dependencies/cpr/test/encoded_auth_tests.cpp
new file mode 100644
index 00000000..c8b89ce0
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/encoded_auth_tests.cpp
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+using namespace cpr;
+
+TEST(EncodedAuthenticationTests, UnicodeEncoderTest) {
+ std::string user = "一二三";
+ std::string pass = "Hello World!";
+ EncodedAuthentication pa{user, pass};
+ std::string expected = "%E4%B8%80%E4%BA%8C%E4%B8%89:Hello%20World%21";
+ EXPECT_EQ(pa.GetAuthString(), expected);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/error_tests.cpp b/Src/external_dependencies/cpr/test/error_tests.cpp
new file mode 100644
index 00000000..13831ef5
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/error_tests.cpp
@@ -0,0 +1,97 @@
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+
+#include <cpr/cpr.h>
+#include <curl/curl.h>
+
+#include "httpServer.hpp"
+#include "httpsServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(ErrorTests, UnsupportedProtocolFailure) {
+ Url url{"urk://wat.is.this"};
+ Response response = cpr::Get(url);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_PROTOCOL, response.error.code);
+}
+
+TEST(ErrorTests, InvalidURLFailure) {
+ Url url{"???"};
+ Response response = cpr::Get(url);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::INVALID_URL_FORMAT, response.error.code);
+}
+
+TEST(ErrorTests, TimeoutFailure) {
+ Url url{server->GetBaseUrl() + "/timeout.html"};
+ Response response = cpr::Get(url, cpr::Timeout{1});
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(ErrorTests, ChronoTimeoutFailure) {
+ Url url{server->GetBaseUrl() + "/timeout.html"};
+ Response response = cpr::Get(url, cpr::Timeout{std::chrono::milliseconds{1}});
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(ErrorTests, ConnectTimeoutFailure) {
+ Url url{"http://localhost:67"};
+ Response response = cpr::Get(url, cpr::ConnectTimeout{1});
+ EXPECT_EQ(0, response.status_code);
+ // Sometimes a CONNECTION_FAILURE happens before the OPERATION_TIMEDOUT:
+ EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::CONNECTION_FAILURE);
+}
+
+TEST(ErrorTests, ChronoConnectTimeoutFailure) {
+ Url url{"http://localhost:67"};
+ Response response = cpr::Get(url, cpr::ConnectTimeout{std::chrono::milliseconds{1}});
+ EXPECT_EQ(0, response.status_code);
+ // Sometimes a CONNECTION_FAILURE happens before the OPERATION_TIMEDOUT:
+ EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::CONNECTION_FAILURE);
+}
+
+TEST(ErrorTests, LowSpeedTimeFailure) {
+ Url url{server->GetBaseUrl() + "/low_speed.html"};
+ Response response = cpr::Get(url, cpr::LowSpeed{1000, 1});
+ // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(ErrorTests, LowSpeedBytesFailure) {
+ Url url{server->GetBaseUrl() + "/low_speed_bytes.html"};
+ Response response = cpr::Get(url, cpr::LowSpeed{1000, 1});
+ // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(ErrorTests, ProxyFailure) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, cpr::Proxies{{"http", "http://bad_host/"}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::PROXY_RESOLUTION_FAILURE, response.error.code);
+}
+
+TEST(ErrorTests, BoolFalseTest) {
+ Error error;
+ EXPECT_FALSE(error);
+}
+
+TEST(ErrorTests, BoolTrueTest) {
+ Error error;
+ error.code = ErrorCode::UNSUPPORTED_PROTOCOL;
+ EXPECT_TRUE(error);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/get_tests.cpp b/Src/external_dependencies/cpr/test/get_tests.cpp
new file mode 100644
index 00000000..d93bd173
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/get_tests.cpp
@@ -0,0 +1,1305 @@
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+
+#include "cpr/cpr.h"
+#include "cpr/cprtypes.h"
+#include "cpr/redirect.h"
+#include "cpr/session.h"
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(BasicTests, HelloWorldTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, HelloWorldStringViewUrlTest) {
+ Url url{static_cast<std::string_view>(server->GetBaseUrl() + "/hello.html")};
+ Response response = cpr::Get(url);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, HelloWorldNoInterfaceTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Interface iface{""}; // Do not specify any specific interface
+ Response response = cpr::Get(url, iface);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, HelloWorldNoInterfaceStringViewTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Interface iface{std::string_view{}}; // Do not specify any specific interface
+ Response response = cpr::Get(url, iface);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, TimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, Timeout{0L});
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, BasicJsonTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Response response = cpr::Get(url);
+ std::string expected_text{
+ "[\n"
+ " {\n"
+ " \"first_key\": \"first_value\",\n"
+ " \"second_key\": \"second_value\"\n"
+ " }\n"
+ "]"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, ResourceNotFoundTest) {
+ Url url{server->GetBaseUrl() + "/error.html"};
+ Response response = cpr::Get(url);
+ std::string expected_text{"Not Found"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(404, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, BadHostTest) {
+ Url url{"http://bad_host/"};
+ Response response = cpr::Get(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
+}
+
+TEST(CookiesTests, BasicCookiesTest) {
+ Url url{server->GetBaseUrl() + "/basic_cookies.html"};
+ Response response = cpr::Get(url);
+ cpr::Cookies res_cookies{response.cookies};
+ std::string expected_text{"Basic Cookies"};
+ cpr::Cookies expectedCookies{
+ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ {"lang", "en-US", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ };
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {
+ EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());
+ EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());
+ EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());
+ EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());
+ EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());
+ EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());
+ EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());
+ }
+}
+
+TEST(CookiesTests, EmptyCookieTest) {
+ Url url{server->GetBaseUrl() + "/empty_cookies.html"};
+ Response response = cpr::Get(url);
+ cpr::Cookies res_cookies{response.cookies};
+ std::string expected_text{"Empty Cookies"};
+ cpr::Cookies expectedCookies{
+ {"SID", "", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ {"lang", "", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ };
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {
+ EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());
+ EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());
+ EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());
+ EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());
+ EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());
+ EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());
+ EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());
+ }
+}
+
+TEST(CookiesTests, ClientSetCookiesTest) {
+ Url url{server->GetBaseUrl() + "/cookies_reflect.html"};
+ Cookies cookies{
+ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ {"lang", "en-US", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ };
+ Response response = cpr::Get(url, cookies);
+ std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterTests, SingleParameterTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Parameters parameters{{"key", "value"}};
+ Response response = cpr::Get(url, parameters);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterTests, SingleParameterOnlyKeyTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Parameters parameters{{"key", ""}};
+ Response response = cpr::Get(url, parameters);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+}
+
+TEST(ParameterTests, MultipleParametersTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}});
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterTests, MultipleDynamicParametersTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Parameters parameters{{"key", "value"}};
+ parameters.Add({"hello", "world"});
+ parameters.Add({"test", "case"});
+ Response response = cpr::Get(url, parameters);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationTests, BasicAuthenticationSuccessTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationTests, BasicBearerSuccessTest) {
+ Url url{server->GetBaseUrl() + "/bearer_token.html"};
+#if CPR_LIBCURL_VERSION_NUM >= 0x073D00
+ Response response = cpr::Get(url, Bearer{"the_token"});
+#else
+ Response response = cpr::Get(url, Header{{"Authorization", "Bearer the_token"}});
+#endif
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationTests, BasicDigestSuccessTest) {
+ Url url{server->GetBaseUrl() + "/digest_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::DIGEST});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAthenticationParameterTests, BasicAuthenticationSuccessSingleParameterTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessSingleParameterReverseTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterTests, BasicAuthenticationSuccessMultipleParametersReverseTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}}, Authentication{"user", "password", AuthMode::BASIC});
+
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"key", "value"}, {"hello", "world"}, {"test", "case"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(std::string{"case"}, response.header["test"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessSingleHeaderReverseTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationHeaderTests, BasicAuthenticationSuccessMultipleHeadersReverseTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"key", "value"}, {"hello", "world"}, {"test", "case"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(std::string{"case"}, response.header["test"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationTests, BasicAuthenticationNullFailureTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url);
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationTests, BasicAuthenticationFailureTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureSingleParameterTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"hello", "world"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterTests, BasicAuthenticationFailureMultipleParametersTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"key", "value"}, {"hello", "world"}, {"test", "case"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?key=value&hello=world&test=case"}, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderJsonTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Response response = cpr::Get(url, Header{{"content-type", "application/json"}});
+ std::string expected_text{
+ "[\n"
+ " {\n"
+ " \"first_key\": \"first_value\",\n"
+ " \"second_key\": \"second_value\"\n"
+ " }\n"
+ "]"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectNoneTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectUpdateHeaderAddSessionTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetHeader(Header{{"Header1", "Value1"}});
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]);
+ EXPECT_EQ(std::string{}, response.header["Header2"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+
+ session.UpdateHeader(Header{{"Header2", "Value2"}});
+ response = session.Get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]);
+ EXPECT_EQ(std::string{"Value2"}, response.header["Header2"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+/**
+ * Test case for #532
+ * https://github.com/whoshuu/cpr/issues/532
+ **/
+TEST(HeaderTests, SessionHeaderReflectTest) {
+ std::unique_ptr<cpr::Session> session(new cpr::Session());
+ session->SetUrl({server->GetBaseUrl() + "/header_reflect.html"});
+ session->SetBody("Some Body to post");
+ session->SetHeader({{"Content-Type", "application/json"}});
+ cpr::Response response = session->Post();
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(std::string{"Header reflect POST"}, response.text);
+ EXPECT_EQ(std::string{"application/json"}, response.header["Content-Type"]);
+}
+
+TEST(HeaderTests, HeaderReflectUpdateHeaderUpdateSessionTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetHeader(Header{{"Header1", "Value1"}});
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"Value1"}, response.header["Header1"]);
+ EXPECT_EQ(std::string{}, response.header["Header2"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+
+ session.UpdateHeader(Header{{"Header1", "Value2"}});
+ response = session.Get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"Value2"}, response.header["Header1"]);
+ EXPECT_EQ(std::string{}, response.header["Header2"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectEmptyTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectSingleTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectMultipleTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(std::string{"case"}, response.header["test"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, HeaderReflectCaseInsensitiveTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"HeLlO", "wOrLd"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeaderTests, SetEmptyHeaderTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"hello", ""}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectNoneParametersTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectEmptyParametersTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectSingleParametersTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectMultipleParametersTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(std::string{"case"}, response.header["test"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Header{{"HeLlO", "wOrLd"}}, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectEmptyParametersReverseTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectSingleParametersReverseTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectMultipleParametersReverseTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"hello", "world"}, {"key", "value"}, {"test", "case"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(std::string{"case"}, response.header["test"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterHeaderTests, HeaderReflectCaseInsensitiveParametersReverseTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}, {"three", "four"}, {"five", "six"}}, Header{{"HeLlO", "wOrLd"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two&three=four&five=six"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hello"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["HELLO"]);
+ EXPECT_EQ(std::string{"wOrLd"}, response.header["hElLo"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderABTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}, Header{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderACTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderADTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{}, Header{{"hello", "world"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderAHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}}, Header{{"hello", "world"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBBTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBCTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBDTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderBHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCBTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCCTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCDTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "password", AuthMode::BASIC}, Parameters{{"one", "two"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderCHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Parameters{{"one", "two"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{}, Parameters{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDBTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}, Parameters{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDCTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{}, Parameters{{"one", "two"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDDTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{}, Parameters{{"one", "two"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{{"one", "two"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderDHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}}, Parameters{{"one", "two"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEBTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderECTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEDTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{}, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderEHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Header{{"hello", "world"}}, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFATest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFBTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFCTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}, Header{});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFDTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFETest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFFTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFGTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "password", AuthMode::BASIC}, Header{{"hello", "world"}});
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicAuthenticationParameterHeaderTests, BasicAuthenticationParameterHeaderFHTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Get(url, Parameters{{"one", "two"}}, Authentication{"user", "bad_password", AuthMode::BASIC}, Header{{"hello", "world"}});
+ EXPECT_EQ("Unauthorized", response.text);
+ EXPECT_EQ(Url{url + "?one=two"}, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(GetRedirectTests, RedirectTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Response response = cpr::Get(url, Redirect(false));
+ std::string expected_text{"Moved Temporarily"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(302, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(GetRedirectTests, ZeroMaxRedirectsSuccessTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, Redirect(0L));
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(GetRedirectTests, ZeroMaxRedirectsFailureTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Response response = cpr::Get(url, Redirect(0L));
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(301, response.status_code);
+ EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);
+}
+
+TEST(GetRedirectTests, BasicAuthenticationRedirectSuccessTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Response response = cpr::Get(url, Authentication{"user", "password", AuthMode::BASIC}, Header{{"RedirectLocation", "basic_auth.html"}}, Redirect(true, true));
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ std::string resultUrl = "http://user:password@127.0.0.1:" + std::to_string(server->GetPort()) + "/basic_auth.html";
+ EXPECT_EQ(response.url, resultUrl);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, RequestBodyTest) {
+ Url url{server->GetBaseUrl() + "/body_get.html"};
+ Body body{"message=abc123"};
+ Response response = cpr::Get(url, body);
+ std::string expected_text{"abc123"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, RequestBodyStringViewTest) {
+ Url url{server->GetBaseUrl() + "/body_get.html"};
+ Body body{static_cast<std::string_view>("message=abc123")};
+ Response response = cpr::Get(url, body);
+ std::string expected_text{"abc123"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(LimitRateTests, HelloWorldTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, LimitRate(1024, 1024));
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/head_tests.cpp b/Src/external_dependencies/cpr/test/head_tests.cpp
new file mode 100644
index 00000000..43c18314
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/head_tests.cpp
@@ -0,0 +1,226 @@
+#include <chrono>
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(HeadTests, BasicHeadTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, ComplexHeadTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, ResourceNotFoundHeadTest) {
+ Url url{server->GetBaseUrl() + "/error.html"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(404, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, BadHostHeadTest) {
+ Url url{"http://bad_host/"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
+}
+
+TEST(HeadTests, CookieHeadTest) {
+ Url url{server->GetBaseUrl() + "/basic_cookies.html"};
+ Response response = cpr::Head(url);
+ cpr::Cookies expectedCookies{
+ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ {"lang", "en-US", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ };
+ cpr::Cookies res_cookies{response.cookies};
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {
+ EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());
+ EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());
+ EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());
+ EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());
+ EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());
+ EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());
+ EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());
+ }
+}
+
+TEST(HeadTests, ParameterHeadTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Parameters parameters{{"key", "value"}};
+ Response response = cpr::Head(url, parameters);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(Url{url + "?key=value"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, AuthenticationSuccessHeadTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Head(url, Authentication{"user", "password", AuthMode::BASIC});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, AuthenticationNullFailureHeadTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, AuthenticationFailureHeadTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Response response = cpr::Head(url, Authentication{"user", "bad_password", AuthMode::BASIC});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ("text/plain", response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, BearerSuccessHeadTest) {
+ Url url{server->GetBaseUrl() + "/bearer_token.html"};
+#if CPR_LIBCURL_VERSION_NUM >= 0x073D00
+ Response response = cpr::Get(url, Bearer{"the_token"});
+#else
+ Response response = cpr::Get(url, Header{{"Authorization", "Bearer the_token"}});
+#endif
+ EXPECT_EQ(std::string{"Header reflect GET"}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, DigestSuccessHeadTest) {
+ Url url{server->GetBaseUrl() + "/digest_auth.html"};
+ Response response = cpr::Head(url, Authentication{"user", "password", AuthMode::DIGEST});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, HeaderReflectNoneHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Head(url);
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, HeaderReflectEmptyHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Head(url, Header{});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, HeaderReflectHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Head(url, Header{{"hello", "world"}});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, SetEmptyHeaderHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Head(url, Header{{"hello", ""}});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, RedirectHeadTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Response response = cpr::Head(url, Redirect(false));
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(302, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, ZeroMaxRedirectsHeadTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Head(url, Redirect(0L));
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(HeadTests, BasicHeadAsyncTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::HeadAsync(url));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/httpServer.cpp b/Src/external_dependencies/cpr/test/httpServer.cpp
new file mode 100644
index 00000000..edf3b099
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/httpServer.cpp
@@ -0,0 +1,914 @@
+#include "httpServer.hpp"
+#include <chrono>
+#include <string>
+#include <system_error>
+#include <thread>
+
+namespace cpr {
+
+std::string HttpServer::GetBaseUrl() {
+ return "http://127.0.0.1:" + std::to_string(GetPort());
+}
+
+uint16_t HttpServer::GetPort() {
+ // Unassigned port number in the ephemeral range
+ return 61936;
+}
+
+mg_connection* HttpServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) {
+ // Based on: https://mongoose.ws/tutorials/http-server/
+ mg_mgr_init(mgr);
+ std::string port = std::to_string(GetPort());
+ mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this);
+ if (!c) {
+ throw std::system_error(errno, std::system_category(), "Failed to listen at port " + port);
+ }
+ return c;
+}
+
+void HttpServer::acceptConnection(mg_connection* /* conn */) {}
+
+void HttpServer::OnRequestHello(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) {
+ OnRequestOptions(conn, msg);
+ } else {
+ std::string response{"Hello world!"};
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ }
+}
+
+void HttpServer::OnRequestRoot(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) {
+ OnRequestOptions(conn, msg);
+ } else {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestNotFound(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"OPTIONS"}) {
+ OnRequestOptions(conn, msg);
+ } else {
+ std::string errorMessage{"Not Found"};
+ SendError(conn, 404, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestOptions(mg_connection* conn, mg_http_message* /*msg*/) {
+ std::string headers =
+ "Content-Type: text/plain\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS\r\n"
+ "Access-Control-Max-Age: 3600\r\n";
+
+ std::string response;
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestTimeout(mg_connection* conn, mg_http_message* msg) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ OnRequestHello(conn, msg);
+}
+
+void HttpServer::OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg) {
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ OnRequestHello(conn, msg);
+}
+
+// Send the header, then send "Hello world!" every 100ms
+// For this, we use a mongoose timer
+void HttpServer::OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* /* msg */, TimerArg* timer_arg) {
+ std::string response{"Hello world!"};
+ mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length() * 20);
+ timer_arg->connection_id = conn->id;
+ mg_timer_init(
+ &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT,
+ // The following lambda function gets executed each time the timer is called.
+ // It sends "Hello world!" to the client each 100ms at most 20 times.
+ [](void* arg) {
+ TimerArg* timer_arg = static_cast<TimerArg*>(arg);
+ if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) {
+ std::string response{"Hello world!"};
+ mg_send(timer_arg->connection, response.c_str(), response.length());
+ ++timer_arg->counter;
+ } else {
+ timer_arg->counter = 20; // Make sure that this timer is never called again
+ }
+ },
+ timer_arg);
+}
+
+// Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed
+// The nested call to mg_mgr_poll can lead to problems otherwise
+void HttpServer::OnRequestLowSpeed(mg_connection* conn, mg_http_message* /*msg*/, mg_mgr* mgr) {
+ std::string response{"Hello world!"};
+ mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length());
+ mg_timer_add(
+ mgr, 2000, MG_TIMER_ONCE,
+ [](void* connection) {
+ std::string response{"Hello world!"};
+ mg_send(static_cast<mg_connection*>(connection), response.c_str(), response.length());
+ },
+ conn);
+}
+
+// Before and after calling an endpoint that calls this method, the test needs to wait until all previous connections are closed
+// The nested call to mg_mgr_poll can lead to problems otherwise
+void HttpServer::OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* /*msg*/, TimerArg* timer_arg) {
+ std::string response{'a'};
+ mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", response.length() * 20);
+
+ mg_timer_init(
+ &timer_arg->mgr->timers, &timer_arg->timer, 100, MG_TIMER_REPEAT,
+ // The following lambda function gets executed each time the timer is called.
+ // It first waits for 2 seconds, then sends "a" to the client each 100ms at most 20 times.
+ [](void* arg) {
+ TimerArg* timer_arg = static_cast<TimerArg*>(arg);
+ if (timer_arg->counter == 0) {
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ }
+ if (timer_arg->counter < 20 && IsConnectionActive(timer_arg->mgr, timer_arg->connection) && timer_arg->connection->id == timer_arg->connection_id) {
+ std::string response{'a'};
+ mg_send(timer_arg->connection, response.c_str(), response.length());
+ ++timer_arg->counter;
+ } else {
+ timer_arg->counter = 20; // Make sure that this timer is never called again
+ }
+ },
+ timer_arg);
+}
+
+void HttpServer::OnRequestBasicCookies(mg_connection* conn, mg_http_message* /*msg*/) {
+ time_t expires_time = 3905119080; // Expires=Wed, 30 Sep 2093 03:18:00 GMT
+ std::array<char, EXPIRES_STRING_SIZE> expires_string;
+ std::strftime(expires_string.data(), expires_string.size(), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&expires_time));
+
+ std::string cookie1{"SID=31d4d96e407aad42; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string cookie2{"lang=en-US; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string headers =
+ "Content-Type: text/html\r\n"
+ "Set-Cookie: " +
+ cookie1 +
+ "\r\n"
+ "Set-Cookie: " +
+ cookie2 + "\r\n";
+ std::string response{"Basic Cookies"};
+
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestEmptyCookies(mg_connection* conn, mg_http_message* /*msg*/) {
+ time_t expires_time = 3905119080; // Expires=Wed, 30 Sep 2093 03:18:00 GMT
+ std::array<char, EXPIRES_STRING_SIZE> expires_string;
+ std::strftime(expires_string.data(), sizeof(expires_string), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&expires_time));
+
+ std::string cookie1{"SID=; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string cookie2{"lang=; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string headers =
+ "Content-Type: text/html\r\n"
+ "Set-Cookie: " +
+ cookie1 +
+ "\r\n"
+ "Set-Cookie: " +
+ cookie2 + "\r\n";
+ std::string response{"Empty Cookies"};
+
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg) {
+ mg_str* request_cookies;
+ if ((request_cookies = mg_http_get_header(msg, "Cookie")) == nullptr) {
+ std::string errorMessage{"Cookie not found"};
+ SendError(conn, 400, errorMessage);
+ return;
+ }
+ std::string cookie_str{request_cookies->ptr, request_cookies->len};
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str());
+}
+
+void HttpServer::OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg) {
+ time_t expires_time = 3905119080; // Expires=Wed, 30 Sep 2093 03:18:00 GMT
+ std::array<char, EXPIRES_STRING_SIZE> expires_string;
+ std::strftime(expires_string.data(), sizeof(expires_string), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&expires_time));
+
+ mg_str* request_cookies;
+ std::string cookie_str;
+ if ((request_cookies = mg_http_get_header(msg, "Cookie")) != nullptr) {
+ cookie_str = std::string{request_cookies->ptr, request_cookies->len};
+ }
+
+ if (cookie_str.find("SID=31d4d96e407aad42") == std::string::npos) {
+ std::string cookie1{"SID=31d4d96e407aad42; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string cookie2{"lang=en-US; Expires=" + std::string(expires_string.data()) + "; Secure"};
+ std::string headers =
+ "Content-Type: text/html\r\n"
+ "Location: http://127.0.0.1:61936/redirection_with_changing_cookies.html\r\n"
+ "Set-Cookie: " +
+ cookie1 +
+ "\r\n"
+ "Set-Cookie: " +
+ cookie2 + "\r\n";
+
+ mg_http_reply(conn, 302, headers.c_str(), "");
+ } else {
+ cookie_str = "Received cookies are: " + cookie_str;
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), cookie_str.c_str());
+ }
+}
+
+void HttpServer::OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg) {
+ mg_str* requested_auth;
+ std::string auth{"Basic"};
+ if ((requested_auth = mg_http_get_header(msg, "Authorization")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) {
+ std::string errorMessage{"Unauthorized"};
+ SendError(conn, 401, errorMessage);
+ return;
+ }
+ std::string auth_string{requested_auth->ptr, requested_auth->len};
+ size_t basic_token = auth_string.find(' ') + 1;
+ auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token);
+ auth_string = Base64Decode(auth_string);
+ size_t colon = auth_string.find(':');
+ std::string username = auth_string.substr(0, colon);
+ std::string password = auth_string.substr(colon + 1, auth_string.length() - colon - 1);
+ if (username == "user" && password == "password") {
+ OnRequestHeaderReflect(conn, msg);
+ } else {
+ std::string errorMessage{"Unauthorized"};
+ SendError(conn, 401, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg) {
+ mg_str* requested_auth;
+ std::string auth{"Bearer"};
+ if ((requested_auth = mg_http_get_header(msg, "Authorization")) == nullptr || mg_ncasecmp(requested_auth->ptr, auth.c_str(), auth.length()) != 0) {
+ std::string errorMessage{"Unauthorized"};
+ SendError(conn, 401, errorMessage);
+ return;
+ }
+ std::string auth_string{requested_auth->ptr, requested_auth->len};
+ size_t basic_token = auth_string.find(' ') + 1;
+ auth_string = auth_string.substr(basic_token, auth_string.length() - basic_token);
+ if (auth_string == "the_token") {
+ OnRequestHeaderReflect(conn, msg);
+ } else {
+ std::string errorMessage{"Unauthorized"};
+ SendError(conn, 401, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestBasicJson(mg_connection* conn, mg_http_message* /*msg*/) {
+ std::string response =
+ "[\n"
+ " {\n"
+ " \"first_key\": \"first_value\",\n"
+ " \"second_key\": \"second_value\"\n"
+ " }\n"
+ "]";
+ std::string headers = "Content-Type: application/json\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg) {
+ std::string response = "Header reflect " + std::string{msg->method.ptr, msg->method.len};
+ std::string headers;
+ bool hasContentTypeHeader = false;
+ for (const mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"Content-Type"} == name) {
+ hasContentTypeHeader = true;
+ }
+
+ if (std::string{"Host"} != name && std::string{"Accept"} != name) {
+ if (header.value.ptr) {
+ headers.append(name + ": " + std::string(header.value.ptr, header.value.len) + "\r\n");
+ }
+ }
+ }
+
+ if (!hasContentTypeHeader) {
+ headers.append("Content-Type: text/html\r\n");
+ }
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg) {
+ // Get the requested target location:
+ std::string location;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"RedirectLocation"} == name) {
+ location = std::string(header.value.ptr, header.value.len);
+ break;
+ }
+ }
+
+ // Check if the request contains a valid location, else default to 'hello.html':
+ if (location.empty()) {
+ location = "hello.html";
+ }
+ std::string headers = "Location: " + location + "\r\n";
+ std::string response = "Moved Temporarily";
+ mg_http_reply(conn, 302, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg) {
+ // Get the requested target location:
+ std::string location;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"RedirectLocation"} == name) {
+ location = std::string(header.value.ptr, header.value.len);
+ break;
+ }
+ }
+
+ // Check if the request contains a valid location, else default to 'hello.html':
+ if (location.empty()) {
+ location = "hello.html";
+ }
+ std::string headers = "Location: " + location + "\r\n";
+ std::string response = "Moved Permanently";
+
+ mg_http_reply(conn, 301, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg) {
+ // Get the requested target location:
+ std::string location;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"RedirectLocation"} == name) {
+ location = std::string(header.value.ptr, header.value.len);
+ break;
+ }
+ }
+
+ if(location.empty()) {
+ std::string errorMessage{"Redirect location missing"};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+
+ std::string headers = "Location: " + location + "\r\n";
+ std::string response = "Moved Permanently";
+
+ mg_http_reply(conn, 301, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestTwoRedirects(mg_connection* conn, mg_http_message* /*msg*/) {
+ std::string response = "Moved Permanently";
+ std::string headers = "Location: permanent_redirect.html\r\n";
+ mg_http_reply(conn, 301, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestUrlPost(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} != std::string{"POST"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+
+ std::string headers = "Content-Type: application/json\r\n";
+
+ char x[100];
+ char y[100];
+ mg_http_get_var(&(msg->body), "x", x, sizeof(x));
+ mg_http_get_var(&(msg->body), "y", y, sizeof(y));
+ std::string x_string{x};
+ std::string y_string{y};
+ std::string response;
+ if (y_string.empty()) {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ "\n"
+ "}"};
+ } else {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ ",\n"
+ " \"y\": " +
+ y_string +
+ ",\n"
+ " \"sum\": " +
+ std::to_string(atoi(x) + atoi(y)) +
+ "\n"
+ "}"};
+ }
+ mg_http_reply(conn, 201, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestBodyGet(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} != std::string{"GET"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+ std::array<char, 100> message{};
+ mg_http_get_var(&(msg->body), "message", message.data(), message.size());
+ if (msg->body.len <= 0) {
+ std::string errorMessage{"No Content"};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+ std::string response = message.data();
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestJsonPost(mg_connection* conn, mg_http_message* msg) {
+ mg_str* content_type{nullptr};
+ if ((content_type = mg_http_get_header(msg, "Content-Type")) == nullptr || std::string{content_type->ptr, content_type->len} != "application/json") {
+ std::string errorMessage{"Unsupported Media Type"};
+ SendError(conn, 415, errorMessage);
+ return;
+ }
+
+ std::string headers = "Content-Type: application/json\r\n";
+ mg_http_reply(conn, 201, headers.c_str(), msg->body.ptr);
+}
+
+void HttpServer::OnRequestFormPost(mg_connection* conn, mg_http_message* msg) {
+ size_t pos{0};
+ mg_http_part part{};
+
+ std::string headers = "Content-Type: application/json\r\n";
+ std::string response{};
+ response += "{\n";
+ while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) {
+ response += " \"" + std::string(part.name.ptr, part.name.len) + "\": \"";
+ if (!std::string(part.filename.ptr, part.filename.len).empty()) {
+ response += std::string(part.filename.ptr, part.filename.len) + "=";
+ }
+ response += std::string(part.body.ptr, part.body.len) + "\",\n";
+ }
+ response.erase(response.find_last_not_of(",\n") + 1);
+ response += "\n}";
+
+ mg_http_reply(conn, 201, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestDelete(mg_connection* conn, mg_http_message* msg) {
+ bool has_json_header = false;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ std::string value = std::string(header.value.ptr, header.value.len);
+ if (std::string{"Content-Type"} == name && std::string{"application/json"} == value) {
+ has_json_header = true;
+ break;
+ }
+ }
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"DELETE"}) {
+ std::string headers;
+ std::string response = "Patch success";
+ if (!has_json_header) {
+ headers = "Content-Type: text/html\r\n";
+ response = "Delete success";
+ } else {
+ headers = "Content-Type: application/json\r\n";
+ response = std::string{msg->body.ptr, msg->body.len};
+ }
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ } else {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"DELETE"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ } else {
+ std::string headers = "Content-Type: text/html\r\n";
+ std::string response = "Delete success";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ }
+}
+
+void HttpServer::OnRequestPut(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"PUT"}) {
+ char x[100];
+ char y[100];
+ mg_http_get_var(&(msg->body), "x", x, sizeof(x));
+ mg_http_get_var(&(msg->body), "y", y, sizeof(y));
+ std::string x_string = std::string{x};
+ std::string y_string = std::string{y};
+ std::string headers = "Content-Type: application/json\r\n";
+ std::string response;
+ if (y_string.empty()) {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ "\n"
+ "}"};
+ } else {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ ",\n"
+ " \"y\": " +
+ y_string +
+ ",\n"
+ " \"sum\": " +
+ std::to_string(atoi(x) + atoi(y)) +
+ "\n"
+ "}"};
+ }
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ } else {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestPostReflect(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} != std::string{"POST"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ }
+
+ std::string response = std::string{msg->body.ptr, msg->body.len};
+ std::string headers;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name{header.name.ptr, header.name.len};
+ if (std::string{"Host"} != name && std::string{"Accept"} != name) {
+ if (header.value.ptr) {
+ headers.append(name + ": " + std::string(header.value.ptr, header.value.len) + "\r\n");
+ }
+ }
+ }
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"PUT"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ } else {
+ std::string headers = "Content-Type: text/html\r\n";
+ std::string response = "Delete success";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ }
+}
+
+void HttpServer::OnRequestPatch(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"PATCH"}) {
+ char x[100];
+ char y[100];
+ mg_http_get_var(&(msg->body), "x", x, sizeof(x));
+ mg_http_get_var(&(msg->body), "y", y, sizeof(y));
+ std::string x_string = std::string{x};
+ std::string y_string = std::string{y};
+ std::string headers = "Content-Type: application/json\r\n";
+ std::string response;
+ if (y_string.empty()) {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ "\n"
+ "}"};
+ } else {
+ response = std::string{
+ "{\n"
+ " \"x\": " +
+ x_string +
+ ",\n"
+ " \"y\": " +
+ y_string +
+ ",\n"
+ " \"sum\": " +
+ std::to_string(atoi(x) + atoi(y)) +
+ "\n"
+ "}"};
+ }
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ } else {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ }
+}
+
+void HttpServer::OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"PATCH"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ } else {
+ std::string headers = "Content-Type: text/html\r\n";
+ std::string response = "Delete success";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+ }
+}
+
+void HttpServer::OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg) {
+ if (std::string{msg->method.ptr, msg->method.len} == std::string{"DOWNLOAD"}) {
+ std::string errorMessage{"Method Not Allowed"};
+ SendError(conn, 405, errorMessage);
+ } else {
+ std::string encoding;
+ std::string range;
+ std::vector<std::pair<int64_t, int64_t>> ranges;
+
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"Accept-Encoding"} == name) {
+ encoding = std::string(header.value.ptr, header.value.len);
+ } else if (std::string{"Range"} == name) {
+ range = std::string(header.value.ptr, header.value.len);
+ }
+ }
+ if (encoding.find("gzip") == std::string::npos) {
+ std::string errorMessage{"Invalid encoding: " + encoding};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+ if (!range.empty()) {
+ std::string::size_type eq_pos = range.find('=');
+ if (eq_pos == std::string::npos) {
+ std::string errorMessage{"Invalid range header: " + range};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+
+ int64_t current_start_index = eq_pos + 1;
+ int64_t current_end_index;
+ std::string::size_type range_len = range.length();
+ std::string::size_type com_pos;
+ std::string::size_type sep_pos;
+ bool more_ranges_exists;
+
+ do {
+ com_pos = range.find(',', current_start_index);
+ if (com_pos < range_len) {
+ current_end_index = com_pos - 1;
+ } else {
+ current_end_index = range_len - 1;
+ }
+
+ std::pair<int64_t, int64_t> current_range{0, -1};
+
+ sep_pos = range.find('-', current_start_index);
+ if (sep_pos == std::string::npos) {
+ std::string errorMessage{"Invalid range format " + range.substr(current_start_index, current_end_index)};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+ if (sep_pos == eq_pos + 1) {
+ std::string errorMessage{"Suffix ranage not supported: " + range.substr(current_start_index, current_end_index)};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+
+ current_range.first = std::strtoll(range.substr(current_start_index, sep_pos - 1).c_str(), nullptr, 10);
+ if (current_range.first == LLONG_MAX || current_range.first == LLONG_MIN) {
+ std::string errorMessage{"Start range is invalid number: " + range.substr(current_start_index, current_end_index)};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+
+ std::string er_str = range.substr(sep_pos + 1, current_end_index);
+ if (!er_str.empty()) {
+ current_range.second = std::strtoll(er_str.c_str(), nullptr, 10);
+ if (current_range.second == 0 || current_range.second == LLONG_MAX || current_range.second == LLONG_MIN) {
+ std::string errorMessage{"End range is invalid number: " + range.substr(current_start_index, current_end_index)};
+ SendError(conn, 405, errorMessage);
+ return;
+ }
+ }
+
+ ranges.push_back(current_range);
+
+ if (current_end_index >= static_cast<int64_t>(range.length() - 1)) {
+ more_ranges_exists = false;
+ } else {
+ // Multiple ranges are separated by ', '
+ more_ranges_exists = true;
+ current_start_index = current_end_index + 3;
+ }
+ } while (more_ranges_exists);
+ }
+
+ std::string response = "Download!";
+ int status_code = 200;
+ std::string headers;
+
+ if (!ranges.empty()) {
+ // Create response parts
+ std::vector<std::string> responses;
+ for (std::pair<int64_t, int64_t> local_range : ranges) {
+ if (local_range.first >= 0) {
+ if (local_range.first >= (int64_t) response.length()) {
+ responses.push_back("");
+ } else if (local_range.second == -1 || local_range.second >= (int64_t) response.length()) {
+ responses.push_back(response.substr(local_range.first));
+ } else {
+ responses.push_back(response.substr(local_range.first, local_range.second - local_range.first + 1));
+ }
+ }
+ }
+
+ if (responses.size() > 1) {
+ // Create mime multipart response
+ std::string boundary = "3d6b6a416f9b5";
+ status_code = 206;
+ response.clear();
+
+ for (size_t i{0}; i < responses.size(); ++i) {
+ response += "--" + boundary + "\n";
+ response += "Content-Range: bytes " + std::to_string(ranges.at(i).first) + "-";
+ if (ranges.at(i).second > 0) {
+ response += std::to_string(ranges.at(i).second);
+ } else {
+ response += std::to_string(responses.at(i).length());
+ }
+ response += "/" + std::to_string(responses.at(i).length()) + "\n\n";
+ response += responses.at(i) + "\n";
+ }
+ response += "--" + boundary + "--";
+ } else {
+ if (ranges.at(0).second == -1 || ranges.at(0).second >= (int64_t) response.length()) {
+ status_code = ranges.at(0).first > 0 ? 206 : 200;
+ } else {
+ status_code = 206;
+ }
+ response = responses.at(0);
+
+ if (status_code == 206) {
+ headers = "Content-Range: bytes " + std::to_string(ranges.at(0).first) + "-";
+ if (ranges.at(0).second > 0) {
+ headers += std::to_string(ranges.at(0).second);
+ } else {
+ headers += std::to_string(response.length());
+ }
+ headers += "/" + std::to_string(response.length());
+ }
+ }
+ }
+ if (!headers.empty()) {
+ headers += "\r\n";
+ }
+
+ mg_http_reply(conn, status_code, headers.c_str(), response.c_str());
+ }
+}
+
+void HttpServer::OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg) {
+ std::string response;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"Accept-Encoding"} == name) {
+ response = std::string(header.value.ptr, header.value.len);
+ }
+ }
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg) {
+ std::string response;
+ for (mg_http_header& header : msg->headers) {
+ if (!header.name.ptr) {
+ continue;
+ }
+ std::string name = std::string(header.name.ptr, header.name.len);
+ if (std::string{"Expect"} == name) {
+ response = std::string(header.value.ptr, header.value.len);
+ }
+ }
+ std::string headers = "Content-Type: text/html\r\n";
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+void HttpServer::OnRequest(mg_connection* conn, mg_http_message* msg) {
+ std::string uri = std::string(msg->uri.ptr, msg->uri.len);
+ if (uri == "/") {
+ OnRequestRoot(conn, msg);
+ } else if (uri == "/hello.html") {
+ OnRequestHello(conn, msg);
+ } else if (uri == "/timeout.html") {
+ OnRequestTimeout(conn, msg);
+ } else if (uri == "/long_timeout.html") {
+ OnRequestLongTimeout(conn, msg);
+ } else if (uri == "/low_speed_timeout.html") {
+ timer_args.emplace_back(std::make_unique<TimerArg>(&mgr, conn, mg_timer{}));
+ OnRequestLowSpeedTimeout(conn, msg, timer_args.back().get());
+ } else if (uri == "/low_speed.html") {
+ OnRequestLowSpeed(conn, msg, &mgr);
+ } else if (uri == "/low_speed_bytes.html") {
+ timer_args.emplace_back(std::make_unique<TimerArg>(&mgr, conn, mg_timer{}));
+ OnRequestLowSpeedBytes(conn, msg, timer_args.back().get());
+ } else if (uri == "/basic_cookies.html") {
+ OnRequestBasicCookies(conn, msg);
+ } else if (uri == "/empty_cookies.html") {
+ OnRequestEmptyCookies(conn, msg);
+ } else if (uri == "/cookies_reflect.html") {
+ OnRequestCookiesReflect(conn, msg);
+ } else if (uri == "/redirection_with_changing_cookies.html") {
+ OnRequestRedirectionWithChangingCookies(conn, msg);
+ } else if (uri == "/basic_auth.html") {
+ OnRequestBasicAuth(conn, msg);
+ } else if (uri == "/bearer_token.html") {
+ OnRequestBearerAuth(conn, msg);
+ } else if (uri == "/digest_auth.html") {
+ OnRequestHeaderReflect(conn, msg);
+ } else if (uri == "/basic.json") {
+ OnRequestBasicJson(conn, msg);
+ } else if (uri == "/header_reflect.html") {
+ OnRequestHeaderReflect(conn, msg);
+ } else if (uri == "/temporary_redirect.html") {
+ OnRequestTempRedirect(conn, msg);
+ } else if (uri == "/permanent_redirect.html") {
+ OnRequestPermRedirect(conn, msg);
+ } else if (uri == "/resolve_permanent_redirect.html") {
+ OnRequestResolvePermRedirect(conn, msg);
+ } else if (uri == "/two_redirects.html") {
+ OnRequestTwoRedirects(conn, msg);
+ } else if (uri == "/url_post.html") {
+ OnRequestUrlPost(conn, msg);
+ } else if (uri == "/body_get.html") {
+ OnRequestBodyGet(conn, msg);
+ } else if (uri == "/json_post.html") {
+ OnRequestJsonPost(conn, msg);
+ } else if (uri == "/form_post.html") {
+ OnRequestFormPost(conn, msg);
+ } else if (uri == "/post_reflect.html") {
+ OnRequestPostReflect(conn, msg);
+ } else if (uri == "/delete.html") {
+ OnRequestDelete(conn, msg);
+ } else if (uri == "/delete_unallowed.html") {
+ OnRequestDeleteNotAllowed(conn, msg);
+ } else if (uri == "/put.html") {
+ OnRequestPut(conn, msg);
+ } else if (uri == "/put_unallowed.html") {
+ OnRequestPutNotAllowed(conn, msg);
+ } else if (uri == "/patch.html") {
+ OnRequestPatch(conn, msg);
+ } else if (uri == "/patch_unallowed.html") {
+ OnRequestPatchNotAllowed(conn, msg);
+ } else if (uri == "/download_gzip.html") {
+ OnRequestDownloadGzip(conn, msg);
+ } else if (uri == "/local_port.html") {
+ OnRequestLocalPort(conn, msg);
+ } else if (uri == "/check_accept_encoding.html") {
+ OnRequestCheckAcceptEncoding(conn, msg);
+ } else if (uri == "/check_expect_100_continue.html") {
+ OnRequestCheckExpect100Continue(conn, msg);
+ } else {
+ OnRequestNotFound(conn, msg);
+ }
+}
+
+void HttpServer::OnRequestLocalPort(mg_connection* conn, mg_http_message* /*msg*/) {
+ // send source port number as response for checking SetLocalPort/SetLocalPortRange
+ std::string headers = "Content-Type: text/plain\r\n";
+ // Convert from big endian to little endian
+ std::string response = std::to_string(AbstractServer::GetRemotePort(conn));
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/test/httpServer.hpp b/Src/external_dependencies/cpr/test/httpServer.hpp
new file mode 100644
index 00000000..386dace4
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/httpServer.hpp
@@ -0,0 +1,65 @@
+#ifndef CPR_TEST_HTTP_SERVER_H
+#define CPR_TEST_HTTP_SERVER_H
+
+#include <memory>
+#include <string>
+
+#include "abstractServer.hpp"
+#include "cpr/cpr.h"
+#include "mongoose.h"
+
+namespace cpr {
+class HttpServer : public AbstractServer {
+ public:
+ ~HttpServer() override = default;
+
+ std::string GetBaseUrl() override;
+ uint16_t GetPort() override;
+
+ void OnRequest(mg_connection* conn, mg_http_message* msg) override;
+
+ private:
+ static void OnRequestHello(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestRoot(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestOptions(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestTimeout(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestLongTimeout(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestLowSpeedTimeout(mg_connection* conn, mg_http_message* msg, TimerArg* arg);
+ static void OnRequestLowSpeed(mg_connection* conn, mg_http_message* msg, mg_mgr* mgr);
+ static void OnRequestLowSpeedBytes(mg_connection* conn, mg_http_message* msg, TimerArg* arg);
+ static void OnRequestBasicCookies(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestEmptyCookies(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestCookiesReflect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestRedirectionWithChangingCookies(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestBasicAuth(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestBearerAuth(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestBasicJson(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestTempRedirect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPermRedirect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestResolvePermRedirect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestTwoRedirects(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestUrlPost(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPostReflect(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestBodyGet(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestJsonPost(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestFormPost(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestDelete(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPut(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPutNotAllowed(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPatch(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestPatchNotAllowed(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestDownloadGzip(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestLocalPort(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestCheckAcceptEncoding(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestCheckExpect100Continue(mg_connection* conn, mg_http_message* msg);
+
+ protected:
+ mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override;
+ void acceptConnection(mg_connection* conn) override;
+};
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/test/httpsServer.cpp b/Src/external_dependencies/cpr/test/httpsServer.cpp
new file mode 100644
index 00000000..401a4703
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/httpsServer.cpp
@@ -0,0 +1,65 @@
+#include "httpsServer.hpp"
+#include <system_error>
+
+namespace cpr {
+HttpsServer::HttpsServer(fs::path&& baseDirPath, fs::path&& sslCertFileName, fs::path&& sslKeyFileName) : baseDirPath(baseDirPath.make_preferred().string()), sslCertFileName(sslCertFileName.make_preferred().string()), sslKeyFileName(sslKeyFileName.make_preferred().string()) {
+ // See https://mongoose.ws/tutorials/tls/
+ memset(static_cast<void*>(&tlsOpts), 0, sizeof(tlsOpts));
+ tlsOpts.cert = this->sslCertFileName.c_str();
+ tlsOpts.certkey = this->sslKeyFileName.c_str();
+}
+
+std::string HttpsServer::GetBaseUrl() {
+ return "https://127.0.0.1:" + std::to_string(GetPort());
+}
+
+uint16_t HttpsServer::GetPort() {
+ // Unassigned port in the ephemeral range
+ return 61937;
+}
+
+mg_connection* HttpsServer::initServer(mg_mgr* mgr, mg_event_handler_t event_handler) {
+ mg_mgr_init(mgr);
+
+ std::string port = std::to_string(GetPort());
+ mg_connection* c = mg_http_listen(mgr, GetBaseUrl().c_str(), event_handler, this);
+ return c;
+}
+
+void HttpsServer::acceptConnection(mg_connection* conn) {
+ // See https://mongoose.ws/tutorials/tls/
+ mg_tls_init(conn, &tlsOpts);
+}
+
+void HttpsServer::OnRequest(mg_connection* conn, mg_http_message* msg) {
+ std::string uri = std::string(msg->uri.ptr, msg->uri.len);
+ if (uri == "/hello.html") {
+ OnRequestHello(conn, msg);
+ } else {
+ OnRequestNotFound(conn, msg);
+ }
+}
+
+void HttpsServer::OnRequestNotFound(mg_connection* conn, mg_http_message* /*msg*/) {
+ mg_http_reply(conn, 404, nullptr, "Not Found");
+}
+
+void HttpsServer::OnRequestHello(mg_connection* conn, mg_http_message* /*msg*/) {
+ std::string response{"Hello world!"};
+ std::string headers{"Content-Type: text/html\r\n"};
+ mg_http_reply(conn, 200, headers.c_str(), response.c_str());
+}
+
+const std::string& HttpsServer::getBaseDirPath() const {
+ return baseDirPath;
+}
+
+const std::string& HttpsServer::getSslCertFileName() const {
+ return sslCertFileName;
+}
+
+const std::string& HttpsServer::getSslKeyFileName() const {
+ return sslKeyFileName;
+}
+
+} // namespace cpr
diff --git a/Src/external_dependencies/cpr/test/httpsServer.hpp b/Src/external_dependencies/cpr/test/httpsServer.hpp
new file mode 100644
index 00000000..cea4d343
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/httpsServer.hpp
@@ -0,0 +1,42 @@
+#ifndef CPR_TEST_HTTPS_SERVER_H
+#define CPR_TEST_HTTPS_SERVER_H
+
+#include <memory>
+#include <string>
+
+#include "abstractServer.hpp"
+#include "cpr/cpr.h"
+#include "mongoose.h"
+#include <cpr/filesystem.h>
+
+namespace cpr {
+class HttpsServer : public AbstractServer {
+ private:
+ // We don't use fs::path here, as this leads to problems using windows
+ const std::string baseDirPath;
+ const std::string sslCertFileName;
+ const std::string sslKeyFileName;
+ struct mg_tls_opts tlsOpts;
+
+ public:
+ explicit HttpsServer(fs::path&& baseDirPath, fs::path&& sslCertFileName, fs::path&& sslKeyFileName);
+ ~HttpsServer() override = default;
+
+ std::string GetBaseUrl() override;
+ uint16_t GetPort() override;
+
+ void OnRequest(mg_connection* conn, mg_http_message* msg) override;
+ static void OnRequestHello(mg_connection* conn, mg_http_message* msg);
+ static void OnRequestNotFound(mg_connection* conn, mg_http_message* msg);
+
+ const std::string& getBaseDirPath() const;
+ const std::string& getSslCertFileName() const;
+ const std::string& getSslKeyFileName() const;
+
+ protected:
+ mg_connection* initServer(mg_mgr* mgr, mg_event_handler_t event_handler) override;
+ void acceptConnection(mg_connection* conn) override;
+};
+} // namespace cpr
+
+#endif
diff --git a/Src/external_dependencies/cpr/test/interceptor_tests.cpp b/Src/external_dependencies/cpr/test/interceptor_tests.cpp
new file mode 100644
index 00000000..2aaf2d62
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/interceptor_tests.cpp
@@ -0,0 +1,369 @@
+#include <gtest/gtest.h>
+#include <iostream>
+
+#include "cpr/cpr.h"
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+class HiddenHelloWorldRedirectInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ // Save original url
+ Url old_url = session.GetFullRequestUrl();
+
+ // Rewrite the url
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ session.SetUrl(url);
+
+ // Procceed the chain
+ Response response = proceed(session);
+
+ // Restore the url again
+ response.url = old_url;
+ return response;
+ }
+};
+
+class ChangeStatusCodeInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ // Procceed the chain
+ Response response = proceed(session);
+
+ // Change the status code
+ response.status_code = 12345;
+ return response;
+ }
+};
+
+class SetBasicAuthInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ // Set authentication
+ session.SetAuth(Authentication{"user", "password", AuthMode::BASIC});
+
+ // Procceed the chain
+ return proceed(session);
+ }
+};
+
+class SetUnsupportedProtocolErrorInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ // Procceed the chain
+ Response response = proceed(session);
+
+ // Set unsupported protocol error
+ response.error = Error{CURLE_UNSUPPORTED_PROTOCOL, "SetErrorInterceptor"};
+
+ // Return response
+ return response;
+ }
+};
+
+class ChangeRequestMethodToGetInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ return proceed(session, Interceptor::ProceedHttpMethod::GET_REQUEST);
+ }
+};
+
+class ChangeRequestMethodToPostInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ session.SetOption(Payload{{"x", "5"}});
+ return proceed(session, Interceptor::ProceedHttpMethod::POST_REQUEST);
+ }
+};
+
+class ChangeRequestMethodToPutInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ session.SetOption(Payload{{"x", "5"}});
+ return proceed(session, Interceptor::ProceedHttpMethod::PUT_REQUEST);
+ }
+};
+
+class ChangeRequestMethodToDeleteInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ return proceed(session, Interceptor::ProceedHttpMethod::DELETE_REQUEST);
+ }
+};
+
+bool write_data(std::string /*data*/, intptr_t /*userdata*/) {
+ return true;
+}
+
+class ChangeRequestMethodToDownloadCallbackInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ return proceed(session, Interceptor::ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST, WriteCallback{write_data, 0});
+ }
+};
+
+class ChangeRequestMethodToHeadInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ return proceed(session, Interceptor::ProceedHttpMethod::HEAD_REQUEST);
+ }
+};
+
+class ChangeRequestMethodToOptionsInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ return proceed(session, Interceptor::ProceedHttpMethod::OPTIONS_REQUEST);
+ }
+};
+
+class ChangeRequestMethodToPatchInterceptor : public Interceptor {
+ public:
+ Response intercept(Session& session) override {
+ session.SetOption(Payload{{"x", "5"}});
+ return proceed(session, Interceptor::ProceedHttpMethod::PATCH_REQUEST);
+ }
+};
+
+TEST(InterceptorTest, HiddenUrlRewriteInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeStatusCodeInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());
+ Response response = session.Get();
+
+ long expected_status_code{12345};
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(expected_status_code, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, DownloadChangeStatusCodeInterceptorTest) {
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ cpr::Session session;
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());
+ Response response = session.Download(cpr::WriteCallback{write_data, 0});
+
+ long expected_status_code{12345};
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(expected_status_code, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, SetBasicAuthInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<SetBasicAuthInterceptor>());
+
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, SetUnsupportedProtocolErrorInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptor>());
+
+ Response response = session.Get();
+ std::string expected_error_message{"SetErrorInterceptor"};
+ ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(expected_error_message, response.error.message);
+ EXPECT_EQ(expected_error_code, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToGetInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToGetInterceptor>());
+ Response response = session.Head();
+
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToPostInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToPostInterceptor>());
+ Response response = session.Head();
+
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToPutInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToPutInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToPatchInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToPatchInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToOptionsInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToOptionsInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToHeadInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToHeadInterceptor>());
+ Response response = session.Get();
+
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToDownloadCallbackInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ session.SetTimeout(Timeout{2000});
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToDownloadCallbackInterceptor>());
+ Response response = session.Put();
+
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ChangeRequestMethodToDeleteInterceptorTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<ChangeRequestMethodToDeleteInterceptor>());
+ Response response = session.Get();
+
+
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, TwoInterceptorsTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());
+ session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{"Hello world!"};
+ long expected_status_code{12345};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(expected_status_code, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(InterceptorTest, ThreeInterceptorsTest) {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ Session session;
+ session.SetUrl(url);
+ session.AddInterceptor(std::make_shared<HiddenHelloWorldRedirectInterceptor>());
+ session.AddInterceptor(std::make_shared<ChangeStatusCodeInterceptor>());
+ session.AddInterceptor(std::make_shared<SetUnsupportedProtocolErrorInterceptor>());
+ Response response = session.Get();
+
+ std::string expected_text{"Hello world!"};
+ long expected_status_code{12345};
+ std::string expected_error_message{"SetErrorInterceptor"};
+ ErrorCode expected_error_code{ErrorCode::UNSUPPORTED_PROTOCOL};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(expected_status_code, response.status_code);
+ EXPECT_EQ(expected_error_message, response.error.message);
+ EXPECT_EQ(expected_error_code, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+} \ No newline at end of file
diff --git a/Src/external_dependencies/cpr/test/multiperform_tests.cpp b/Src/external_dependencies/cpr/test/multiperform_tests.cpp
new file mode 100644
index 00000000..6d6dcdf0
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/multiperform_tests.cpp
@@ -0,0 +1,673 @@
+#include <cstdint>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+
+#include <cpr/api.h>
+#include <cpr/cpr.h>
+#include <curl/curl.h>
+
+#include "httpServer.hpp"
+#include "httpsServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+bool write_data(std::string /*data*/, intptr_t /*userdata*/) {
+ return true;
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddSingleSessionTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+
+ EXPECT_EQ(2, session.use_count());
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddMultipleSessionsTest) {
+ MultiPerform multiperform;
+ for (int i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ multiperform.AddSession(session);
+ EXPECT_EQ(2, session.use_count());
+ }
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddMultipleNonDownloadSessionsTest) {
+ MultiPerform multiperform;
+ for (int i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST);
+ EXPECT_EQ(2, session.use_count());
+ }
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddMultipleDownloadSessionsTest) {
+ MultiPerform multiperform;
+ for (int i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);
+ EXPECT_EQ(2, session.use_count());
+ }
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsTest) {
+ std::shared_ptr<Session> session_1 = std::make_shared<Session>();
+ std::shared_ptr<Session> session_2 = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session_1, MultiPerform::HttpMethod::GET_REQUEST);
+ EXPECT_EQ(2, session_1.use_count());
+ EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::DOWNLOAD_REQUEST), std::invalid_argument);
+}
+
+TEST(MultiperformAddSessionTests, MultiperformAddTwoMixedTypeSessionsReversTest) {
+ std::shared_ptr<Session> session_1 = std::make_shared<Session>();
+ std::shared_ptr<Session> session_2 = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session_1, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);
+ EXPECT_EQ(2, session_1.use_count());
+ EXPECT_THROW(multiperform.AddSession(session_2, MultiPerform::HttpMethod::GET_REQUEST), std::invalid_argument);
+}
+
+TEST(MultiperformRemoveSessionTests, MultiperformRemoveSingleSessionTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ EXPECT_EQ(2, session.use_count());
+ multiperform.RemoveSession(session);
+ EXPECT_EQ(1, session.use_count());
+}
+
+TEST(MultiperformRemoveSessionTests, MultiperformRemoveMultipleSessionsTest) {
+ MultiPerform multiperform;
+ for (int i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ multiperform.AddSession(session);
+ EXPECT_EQ(2, session.use_count());
+ multiperform.RemoveSession(session);
+ EXPECT_EQ(1, session.use_count());
+ }
+}
+
+TEST(MultiperformRemoveSessionTests, MultiperformRemoveNonExistingSessionTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ MultiPerform multiperform;
+ EXPECT_THROW(multiperform.RemoveSession(session), std::invalid_argument);
+}
+
+TEST(MultiperformGetTests, MultiperformSingleSessionGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Get();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformGetTests, MultiperformTwoSessionsGetTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/hello.html"});
+ urls.push_back({server->GetBaseUrl() + "/error.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ multiperform.AddSession(sessions.at(i));
+ }
+
+ std::vector<Response> responses = multiperform.Get();
+
+ EXPECT_EQ(responses.size(), sessions.size());
+ std::vector<std::string> expected_texts;
+ expected_texts.emplace_back("Hello world!");
+ expected_texts.emplace_back("Not Found");
+
+ std::vector<std::string> expected_content_types;
+ expected_content_types.emplace_back("text/html");
+ expected_content_types.emplace_back("text/plain");
+
+ std::vector<uint16_t> expected_status_codes;
+ expected_status_codes.push_back(200);
+ expected_status_codes.push_back(404);
+
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(expected_texts.at(i), responses.at(i).text);
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]);
+ EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformGetTests, MultiperformRemoveSessionGetTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/hello.html"});
+ urls.push_back({server->GetBaseUrl() + "/hello.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ multiperform.AddSession(sessions.at(i));
+ }
+
+ multiperform.RemoveSession(sessions.at(0));
+
+ std::vector<Response> responses = multiperform.Get();
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(urls.at(0), responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+#ifndef __APPLE__
+/**
+ * This test case is currently disabled for macOS/Apple systems since it fails in an nondeterministic manner.
+ * It is probably caused by a bug inside curl_multi_perform on macOS.
+ * Needs further investigation.
+ * Issue: https://github.com/libcpr/cpr/issues/841
+ **/
+TEST(MultiperformGetTests, MultiperformTenSessionsGetTest) {
+ const size_t sessionCount = 10;
+
+ MultiPerform multiperform;
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ for (size_t i = 0; i < sessionCount; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ multiperform.AddSession(session);
+ }
+
+ std::vector<Response> responses = multiperform.Get();
+
+ EXPECT_EQ(responses.size(), sessionCount);
+ for (Response& response : responses) {
+ EXPECT_EQ(std::string{"Hello world!"}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+#endif
+
+TEST(MultiperformDeleteTests, MultiperformSingleSessionDeleteTest) {
+ Url url{server->GetBaseUrl() + "/delete.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Delete();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadTest) {
+ Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0});
+
+ EXPECT_EQ(responses.size(), 1);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformDownloadTests, MultiperformSingleSessionDownloadNonMatchingArgumentsNumberTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ EXPECT_THROW(std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument);
+}
+
+TEST(MultiperformDownloadTests, MultiperformTwoSessionsDownloadTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/download_gzip.html"});
+ urls.push_back({server->GetBaseUrl() + "/download_gzip.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ sessions.at(i)->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+
+ multiperform.AddSession(sessions.at(i));
+ }
+
+ std::vector<Response> responses = multiperform.Download(WriteCallback{write_data, 0}, WriteCallback{write_data, 0});
+
+ EXPECT_EQ(responses.size(), sessions.size());
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(200, responses.at(i).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformPutTests, MultiperformSingleSessionPutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload(Payload{{"x", "5"}});
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Put();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformHeadTests, MultiperformSingleSessionHeadTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Head();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text;
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformOptionsTests, MultiperformSingleSessionOptionsTest) {
+ Url url{server->GetBaseUrl() + "/"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Options();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text;
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, responses.at(0).header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformPatchTests, MultiperformSingleSessionPatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload(Payload{{"x", "5"}});
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Patch();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformPostTests, MultiperformSingleSessionPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload(Payload{{"x", "5"}});
+ MultiPerform multiperform;
+ multiperform.AddSession(session);
+ std::vector<Response> responses = multiperform.Post();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(201, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformPerformTests, MultiperformSingleGetPerformTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ MultiPerform multiperform;
+ multiperform.AddSession(session, MultiPerform::HttpMethod::GET_REQUEST);
+ std::vector<Response> responses = multiperform.Perform();
+
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformPerformTests, MultiperformTwoGetPerformTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/hello.html"});
+ urls.push_back({server->GetBaseUrl() + "/error.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::GET_REQUEST);
+ }
+
+ std::vector<Response> responses = multiperform.Perform();
+
+ EXPECT_EQ(responses.size(), sessions.size());
+ std::vector<std::string> expected_texts;
+ expected_texts.emplace_back("Hello world!");
+ expected_texts.emplace_back("Not Found");
+
+ std::vector<std::string> expected_content_types;
+ expected_content_types.emplace_back("text/html");
+ expected_content_types.emplace_back("text/plain");
+
+ std::vector<uint16_t> expected_status_codes;
+ expected_status_codes.push_back(200);
+ expected_status_codes.push_back(404);
+
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(expected_texts.at(i), responses.at(i).text);
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]);
+ EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformPerformTests, MultiperformMixedMethodsPerformTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/hello.html"});
+ urls.push_back({server->GetBaseUrl() + "/delete.html"});
+ urls.push_back({server->GetBaseUrl() + "/error.html"});
+ urls.push_back({server->GetBaseUrl() + "/url_post.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+ std::vector<MultiPerform::HttpMethod> methods;
+ methods.push_back(MultiPerform::HttpMethod::GET_REQUEST);
+ methods.push_back(MultiPerform::HttpMethod::DELETE_REQUEST);
+ methods.push_back(MultiPerform::HttpMethod::GET_REQUEST);
+ methods.push_back(MultiPerform::HttpMethod::POST_REQUEST);
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ if (methods.at(i) == MultiPerform::HttpMethod::POST_REQUEST) {
+ sessions.at(i)->SetPayload(Payload{{"x", "5"}});
+ }
+ multiperform.AddSession(sessions.at(i), methods.at(i));
+ }
+
+ std::vector<Response> responses = multiperform.Perform();
+
+ EXPECT_EQ(responses.size(), sessions.size());
+
+ std::vector<std::string> expected_texts;
+ expected_texts.emplace_back("Hello world!");
+ expected_texts.emplace_back("Delete success");
+ expected_texts.emplace_back("Not Found");
+ expected_texts.emplace_back(
+ "{\n"
+ " \"x\": 5\n"
+ "}");
+
+ std::vector<std::string> expected_content_types;
+ expected_content_types.emplace_back("text/html");
+ expected_content_types.emplace_back("text/html");
+ expected_content_types.emplace_back("text/plain");
+ expected_content_types.emplace_back("application/json");
+
+ std::vector<uint16_t> expected_status_codes;
+ expected_status_codes.push_back(200);
+ expected_status_codes.push_back(200);
+ expected_status_codes.push_back(404);
+ expected_status_codes.push_back(201);
+
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(expected_texts.at(i), responses.at(i).text);
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]);
+ EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformPerformDownloadTests, MultiperformSinglePerformDownloadTest) {
+ Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ MultiPerform multiperform;
+ multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);
+ std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0});
+
+ EXPECT_EQ(responses.size(), 1);
+ EXPECT_EQ(url, responses.at(0).url);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformDownloadTests, MultiperformSinglePerformDownloadNonMatchingArgumentsNumberTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ MultiPerform multiperform;
+ multiperform.AddSession(session, MultiPerform::HttpMethod::DOWNLOAD_REQUEST);
+ EXPECT_THROW(std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0}), std::invalid_argument);
+}
+
+TEST(MultiperformPerformDownloadTests, MultiperformTwoPerformDownloadTest) {
+ MultiPerform multiperform;
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/download_gzip.html"});
+ urls.push_back({server->GetBaseUrl() + "/download_gzip.html"});
+
+ std::vector<std::shared_ptr<Session>> sessions;
+ sessions.push_back(std::make_shared<Session>());
+ sessions.push_back(std::make_shared<Session>());
+
+
+ for (size_t i = 0; i < sessions.size(); ++i) {
+ sessions.at(i)->SetUrl(urls.at(i));
+ sessions.at(i)->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+
+ multiperform.AddSession(sessions.at(i), MultiPerform::HttpMethod::DOWNLOAD_REQUEST);
+ }
+
+ std::vector<Response> responses = multiperform.PerformDownload(WriteCallback{write_data, 0}, WriteCallback{write_data, 0});
+
+ EXPECT_EQ(responses.size(), sessions.size());
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(200, responses.at(i).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformAPITests, MultiperformApiSingleGetTest) {
+ std::vector<Response> responses = MultiGet(std::tuple<Url>{Url{server->GetBaseUrl() + "/hello.html"}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiTwoGetsTest) {
+ std::vector<Response> responses = MultiGet(std::tuple<Url, Timeout>{Url{server->GetBaseUrl() + "/long_timeout.html"}, Timeout{1000}}, std::tuple<Url>{Url{server->GetBaseUrl() + "/error.html"}});
+
+ EXPECT_EQ(responses.size(), 2);
+ std::vector<Url> urls;
+ urls.push_back({server->GetBaseUrl() + "/long_timeout.html"});
+ urls.push_back({server->GetBaseUrl() + "/error.html"});
+
+ std::vector<std::string> expected_texts;
+ expected_texts.emplace_back("");
+ expected_texts.emplace_back("Not Found");
+
+ std::vector<std::string> expected_content_types;
+ expected_content_types.emplace_back("");
+ expected_content_types.emplace_back("text/plain");
+
+ std::vector<uint16_t> expected_status_codes;
+ expected_status_codes.push_back(0);
+ expected_status_codes.push_back(404);
+
+ std::vector<ErrorCode> expected_error_codes;
+ expected_error_codes.push_back(ErrorCode::OPERATION_TIMEDOUT);
+ expected_error_codes.push_back(ErrorCode::OK);
+
+ for (size_t i = 0; i < responses.size(); ++i) {
+ EXPECT_EQ(expected_texts.at(i), responses.at(i).text);
+ EXPECT_EQ(urls.at(i), responses.at(i).url);
+ EXPECT_EQ(expected_content_types.at(i), responses.at(i).header["content-type"]);
+ EXPECT_EQ(expected_status_codes.at(i), responses.at(i).status_code);
+ EXPECT_EQ(expected_error_codes.at(i), responses.at(i).error.code);
+ }
+}
+
+TEST(MultiperformAPITests, MultiperformApiSingleDeleteTest) {
+ std::vector<Response> responses = MultiDelete(std::tuple<Url>{Url{server->GetBaseUrl() + "/delete.html"}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{"Delete success"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/delete.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiSinglePutTest) {
+ std::vector<Response> responses = MultiPut(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + "/put.html"}, Payload{{"x", "5"}}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/put.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiSingleHeadTest) {
+ std::vector<Response> responses = MultiHead(std::tuple<Url>{Url{server->GetBaseUrl() + "/hello.html"}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text;
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"text/html"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiSingleOptionsTest) {
+ std::vector<Response> responses = MultiOptions(std::tuple<Url>{Url{server->GetBaseUrl() + "/"}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text;
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, responses.at(0).header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiSinglePatchTest) {
+ std::vector<Response> responses = MultiPatch(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + "/patch.html"}, Payload{{"x", "5"}}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/patch.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(200, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+TEST(MultiperformAPITests, MultiperformApiSinglePostTest) {
+ std::vector<Response> responses = MultiPost(std::tuple<Url, Payload>{Url{server->GetBaseUrl() + "/url_post.html"}, Payload{{"x", "5"}}});
+ EXPECT_EQ(responses.size(), 1);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, responses.at(0).text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/url_post.html"}, responses.at(0).url);
+ EXPECT_EQ(std::string{"application/json"}, responses.at(0).header["content-type"]);
+ EXPECT_EQ(201, responses.at(0).status_code);
+ EXPECT_EQ(ErrorCode::OK, responses.at(0).error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/options_tests.cpp b/Src/external_dependencies/cpr/test/options_tests.cpp
new file mode 100644
index 00000000..ee3176f7
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/options_tests.cpp
@@ -0,0 +1,73 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(OptionsTests, BaseUrlTest) {
+ Url url{server->GetBaseUrl() + "/"};
+ Response response = cpr::Options(url);
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(OptionsTests, SpecificUrlTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Options(url);
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(OptionsTests, AsyncBaseUrlTest) {
+ Url url{server->GetBaseUrl() + "/"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::OptionsAsync(url));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(OptionsTests, AsyncSpecificUrlTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::OptionsAsync(url));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/patch_tests.cpp b/Src/external_dependencies/cpr/test/patch_tests.cpp
new file mode 100644
index 00000000..22d0b4f4
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/patch_tests.cpp
@@ -0,0 +1,276 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(PatchTests, PatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Patch(url, payload);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, PatchUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Patch(url, payload);
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchUnallowedAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchUnallowedAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, SessionPatchUnallowedAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Patch();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, AsyncPatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload);
+ cpr::Response response = future_response.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, AsyncPatchUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ cpr::AsyncResponse future_response = cpr::PatchAsync(url, payload);
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PatchTests, AsyncMultiplePatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::PatchAsync(url, payload));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(PatchTests, AsyncMultiplePatchUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/patch_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::PatchAsync(url, payload));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/post_tests.cpp b/Src/external_dependencies/cpr/test/post_tests.cpp
new file mode 100644
index 00000000..a4d08e5a
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/post_tests.cpp
@@ -0,0 +1,756 @@
+#include <gtest/gtest.h>
+
+#include <array>
+#include <cstdio>
+#include <fstream>
+#include <string>
+
+#include <cpr/cpr.h>
+#include <cpr/multipart.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(UrlEncodedPostTests, UrlPostSingleTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostAddPayloadPair) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "1"}};
+ payload.Add({"y", "2"});
+ Response response = cpr::Post(url, payload);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 1,\n"
+ " \"y\": 2,\n"
+ " \"sum\": 3\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostPayloadIteratorTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::vector<Pair> payloadData;
+ payloadData.emplace_back("x", "1");
+ payloadData.emplace_back("y", "2");
+ Response response = cpr::Post(url, Payload(payloadData.begin(), payloadData.end()));
+ std::string expected_text{
+ "{\n"
+ " \"x\": 1,\n"
+ " \"y\": 2,\n"
+ " \"sum\": 3\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostEncodeTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Payload{{"x", "hello world!!~"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": hello world!!~\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostEncodeNoCopyTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "hello world!!~"}};
+ // payload lives through the lifetime of Post, so it doesn't need to be copied
+ Response response = cpr::Post(url, payload);
+ std::string expected_text{
+ "{\n"
+ " \"x\": hello world!!~\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostManyTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}, {"y", "13"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5,\n"
+ " \"y\": 13,\n"
+ " \"sum\": 18\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostBadHostTest) {
+ Url url{"http://bad_host/"};
+ Response response = cpr::Post(url, Payload{{"hello", "world"}});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostSingleTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", 5}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileTest) {
+ std::string filename{"test_file"};
+ std::string content{"hello world"};
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << content;
+ test_file.close();
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", File{filename}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ content +
+ "\"\n"
+ "}"};
+ std::remove(filename.c_str());
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostMultipleFilesTestLvalue) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ std::string filename1{"file1"};
+ std::string content1{"apple"};
+ std::ofstream file1;
+ file1.open(filename1);
+ file1 << content1;
+ file1.close();
+ std::string filename2{"file2"};
+ std::string content2{"banana"};
+ std::ofstream file2;
+ file2.open(filename2);
+ file2 << content2;
+ file2.close();
+ File singleFile{"file1"};
+ File singleFileWithOverridedFilename{"file1", "applefile"};
+ Files multipleFiles{"file1", "file2"};
+ Files multipleFilesWithOverridedFilename{
+ File{"file1", "applefile"},
+ File{"file2", "bananafile"},
+ };
+ {
+ Response response = cpr::Post(url, Multipart{{"files", singleFile}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"file1=" +
+ content1 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", singleFileWithOverridedFilename}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"applefile=" +
+ content1 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", multipleFiles}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"file1=" +
+ content1 +
+ "\",\n"
+ " \"files\": \"file2=" +
+ content2 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", multipleFilesWithOverridedFilename}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"applefile=" +
+ content1 +
+ "\",\n"
+ " \"files\": \"bananafile=" +
+ content2 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ std::remove(filename1.c_str());
+ std::remove(filename2.c_str());
+}
+
+TEST(UrlEncodedPostTests, FormPostMultipleFilesTestRvalue) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ std::string filename1{"file1"};
+ std::string content1{"apple"};
+ std::ofstream file1;
+ file1.open(filename1);
+ file1 << content1;
+ file1.close();
+ std::string filename2{"file2"};
+ std::string content2{"banana"};
+ std::ofstream file2;
+ file2.open(filename2);
+ file2 << content2;
+ file2.close();
+ {
+ Response response = cpr::Post(url, Multipart{{"files", File{"file1"}}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"file1=" +
+ content1 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", File{"file1", "applefile"}}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"applefile=" +
+ content1 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", Files{"file1", "file2"}}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"file1=" +
+ content1 +
+ "\",\n"
+ " \"files\": \"file2=" +
+ content2 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = cpr::Post(url, Multipart{{"files", Files{
+ File{"file1", "applefile"},
+ File{"file2", "bananafile"},
+ }}});
+ std::string expected_text{
+ "{\n"
+ " \"files\": \"applefile=" +
+ content1 +
+ "\",\n"
+ " \"files\": \"bananafile=" +
+ content2 + "\"\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ std::remove(filename1.c_str());
+ std::remove(filename2.c_str());
+}
+
+TEST(UrlEncodedPostTests, FormPostFileTestWithOverridedFilename) {
+ std::string filename{"test_file"};
+ std::string overided_filename{"overided_filename"};
+ std::string content{"hello world"};
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << content;
+ test_file.close();
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+
+ Response response = cpr::Post(url, Multipart{{"x", File{filename, overided_filename}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"" +
+ overided_filename + "=" + content +
+ "\"\n"
+ "}"};
+ std::remove(filename.c_str());
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileNoCopyTest) {
+ std::string filename{"./test_file"};
+ std::string content{"hello world"};
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << content;
+ test_file.close();
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Multipart multipart{{"x", File{filename}}};
+ Response response = cpr::Post(url, multipart);
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ content +
+ "\"\n"
+ "}"};
+ std::remove(filename.c_str());
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileNoCopyTestWithOverridedFilename) {
+ std::string filename{"test_file"};
+ std::string overrided_filename{"overided_filename"};
+ std::string content{"hello world"};
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << content;
+ test_file.close();
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Multipart multipart{{"x", File{filename, overrided_filename}}};
+ Response response = cpr::Post(url, multipart);
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"" +
+ overrided_filename + "=" + content +
+ "\"\n"
+ "}"};
+ std::remove(filename.c_str());
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, TimeoutPostTest) {
+ Url url{server->GetBaseUrl() + "/json_post.html"};
+ std::string body{R"({"RegisterObject": {"DeviceID": "65010000005030000001"}})"};
+ cpr::Response response = cpr::Post(url, cpr::Header{{"Content-Type", "application/json"}}, cpr::Body{body}, cpr::ConnectTimeout{3000}, cpr::Timeout{3000});
+ std::string expected_text{R"({"RegisterObject": {"DeviceID": "65010000005030000001"}})"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferTest) {
+ std::string content{"hello world"};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ content +
+ "\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferNoCopyTest) {
+ std::string content{"hello world"};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Multipart multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}};
+ Response response = cpr::Post(url, multipart);
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ content +
+ "\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferPointerTest) {
+ const char* content = "hello world";
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{content, 11 + content, "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ std::string(content) +
+ "\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferArrayTest) {
+ const char content[] = "hello world";
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ // We subtract 1 from std::end() because we don't want to include the terminating null
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{std::begin(content), std::end(content) - 1, "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=" +
+ std::string(content) +
+ "\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferVectorTest) {
+ std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=hello world\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostFileBufferStdArrayTest) {
+ std::array<unsigned char, 11> content{{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=hello world\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostBufferRvalueTest) {
+ std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", Buffer{content.begin(), content.end(), "test_file"}}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=hello world\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, ReflectPostBufferLvalueTest) {
+ std::vector<unsigned char> content{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Buffer buff{content.begin(), content.end(), "test_file"};
+ Response response = cpr::Post(url, Multipart{{"x", buff}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"test_file=hello world\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostManyTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", 5}, {"y", 13}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\",\n"
+ " \"y\": \"13\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostManyNoCopyTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Multipart multipart{{"x", 5}, {"y", 13}};
+ Response response = cpr::Post(url, multipart);
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\",\n"
+ " \"y\": \"13\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostContentTypeTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url, Multipart{{"x", 5, "application/number"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, FormPostContentTypeLValueTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Multipart multipart{{"x", 5, "application/number"}};
+ Response response = cpr::Post(url, multipart);
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, UrlPostAsyncSingleTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::PostAsync(url, payload));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(UrlEncodedPostTests, UrlReflectTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}});
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UrlEncodedPostTests, PostWithNoBodyTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Response response = cpr::Post(url);
+ std::string expected_text{"{\n}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+static std::string getTimestamp() {
+ char buf[1000];
+ time_t now = time(0);
+ struct tm* tm = gmtime(&now);
+ strftime(buf, sizeof buf, "%a, %d %b %Y %H:%M:%S GMT", tm);
+ return buf;
+}
+
+TEST(UrlEncodedPostTests, PostReflectTest) {
+ std::string uri = server->GetBaseUrl() + "/post_reflect.html";
+ std::string body = R"({"property1": "value1"})";
+ std::string contentType = "application/json";
+ std::string signature = "x-ms-date: something";
+ std::string logType = "LoggingTest";
+ std::string date = getTimestamp();
+ Response response = cpr::Post(cpr::Url(uri), cpr::Header{{"content-type", contentType}, {"Authorization", signature}, {"log-type", logType}, {"x-ms-date", date}, {"content-length", std::to_string(body.length())}}, cpr::Body(body));
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(body, response.text);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(signature, response.header["Authorization"]);
+ EXPECT_EQ(logType, response.header["log-type"]);
+ EXPECT_EQ(date, response.header["x-ms-date"]);
+ EXPECT_EQ(std::to_string(body.length()), response.header["content-length"]);
+}
+
+TEST(UrlEncodedPostTests, PostReflectPayloadTest) {
+ std::string uri = server->GetBaseUrl() + "/header_reflect.html";
+ cpr::Payload payload = cpr::Payload{{"email", ""}, {"password", ""}, {"devicetoken", ""}};
+ cpr::Response response = cpr::Post(cpr::Url(uri), cpr::Timeout{10000}, payload);
+
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(200, response.status_code);
+}
+
+TEST(UrlEncodedPostTests, InjectMultipleHeadersTest) {
+ std::string uri = server->GetBaseUrl() + "/post_reflect.html";
+ std::string key_1 = "key_1";
+ std::string val_1 = "value_1";
+ std::string key_2 = "key_2";
+ std::string val_2 = "value_2";
+ cpr::Response response = cpr::Post(cpr::Url{uri}, cpr::Header{{key_1, val_1}}, cpr::Header{{key_2, val_2}});
+
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(val_1, response.header[key_1]);
+ EXPECT_EQ(val_2, response.header[key_2]);
+}
+
+TEST(UrlEncodedPostTests, PostBodyWithFile) {
+ std::string filename{"test_file"};
+ std::string expected_text(R"({"property1": "value1"})");
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << expected_text;
+ test_file.close();
+ Url url{server->GetBaseUrl() + "/post_reflect.html"};
+ cpr::Response response = Post(url, cpr::Header({{"Content-Type", "application/octet-stream"}}), cpr::Body(File("test_file")));
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(std::string{"application/octet-stream"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+}
+
+TEST(UrlEncodedPostTests, PostBodyWithBuffer) {
+ Url url{server->GetBaseUrl() + "/post_reflect.html"};
+ std::string expected_text(R"({"property1": "value1"})");
+ cpr::Response response = Post(url, cpr::Header({{"Content-Type", "application/octet-stream"}}), cpr::Body(Buffer{expected_text.begin(), expected_text.end(), "test_file"}));
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/octet-stream"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PostRedirectTests, TempRedirectTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html");
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PostRedirectTests, TempRedirectNoneTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}, Redirect(PostRedirectFlags::NONE));
+ EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html");
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PostRedirectTests, PermRedirectTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html");
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PostRedirectTests, PermRedirectNoneTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Response response = cpr::Post(url, Payload{{"x", "5"}}, Header{{"RedirectLocation", "url_post.html"}}, Redirect(PostRedirectFlags::NONE));
+ EXPECT_EQ(response.url, server->GetBaseUrl() + "/url_post.html");
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/prepare_tests.cpp b/Src/external_dependencies/cpr/test/prepare_tests.cpp
new file mode 100644
index 00000000..d7332c9b
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/prepare_tests.cpp
@@ -0,0 +1,138 @@
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(PrepareTests, GetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.PrepareGet();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PrepareTests, OptionsTests) {
+ Url url{server->GetBaseUrl() + "/"};
+ Session session;
+ session.SetUrl(url);
+ session.PrepareOptions();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"GET, POST, PUT, DELETE, PATCH, OPTIONS"}, response.header["Access-Control-Allow-Methods"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PrepareTests, PatchTest) {
+ Url url{server->GetBaseUrl() + "/patch.html"};
+ Payload payload{{"x", "5"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ session.PreparePatch();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Url urlPost{server->GetBaseUrl() + "/post_reflect.html"};
+ Url urlPut{server->GetBaseUrl() + "/put.html"};
+ Session session;
+ for (size_t i = 0; i < 3; ++i) {
+ {
+ session.SetUrl(url);
+ session.PrepareDelete();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{"Header reflect DELETE"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(urlPost);
+ std::string expectedBody = "a1b2c3Post";
+ session.SetBody(expectedBody);
+ session.PreparePost();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ EXPECT_EQ(expectedBody, response.text);
+ EXPECT_EQ(urlPost, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(url);
+ session.PrepareGet();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(urlPut);
+ session.SetPayload({{"x", "5"}});
+ session.PreparePut();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(urlPut, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(url);
+ session.PrepareHead();
+ CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
+ Response response = session.Complete(curl_result);
+ std::string expected_text{"Header reflect HEAD"};
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ }
+}
+
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/proxy_auth_tests.cpp b/Src/external_dependencies/cpr/test/proxy_auth_tests.cpp
new file mode 100644
index 00000000..f618ba2f
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/proxy_auth_tests.cpp
@@ -0,0 +1,94 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "cpr/cpr.h"
+#include "httpServer.hpp"
+
+// TODO: This requires a local proxy server (squid). This should be replaced with a source
+// code implementation.
+
+#define HTTP_PROXY "127.0.0.1:3128"
+#define HTTPS_PROXY "127.0.0.1:3128"
+#define PROXY_USER "u$er"
+#define PROXY_PASS "p@ss"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+// TODO: These should be fixed after a source code implementation of a proxy
+#if defined(false)
+TEST(ProxyAuthTests, SingleProxyTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}}, ProxyAuthentication{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyAuthTests, MultipleProxyHttpTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Response response = cpr::Get(url, Proxies{{"https", HTTPS_PROXY}, {"http", HTTP_PROXY}}, ProxyAuthentication{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}, {"https", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyAuthTests, CopyProxyTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Proxies proxies{{"http", HTTP_PROXY}};
+ ProxyAuthentication proxy_auth{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}};
+ Response response = cpr::Get(url, proxies, proxy_auth);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyAuthTests, ProxySessionTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetProxies(Proxies{{"http", HTTP_PROXY}});
+ session.SetProxyAuth(ProxyAuthentication{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyAuthTests, ReferenceProxySessionTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Proxies proxies{{"http", HTTP_PROXY}};
+ ProxyAuthentication proxy_auth{{"http", EncodedAuthentication{PROXY_USER, PROXY_PASS}}};
+ Session session;
+ session.SetUrl(url);
+ session.SetProxies(proxies);
+ session.SetProxyAuth(proxy_auth);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+#endif
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/proxy_tests.cpp b/Src/external_dependencies/cpr/test/proxy_tests.cpp
new file mode 100644
index 00000000..f43c4c23
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/proxy_tests.cpp
@@ -0,0 +1,92 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+// TODO: This uses public servers for proxies and endpoints. This should be replaced with a source
+// code implementation inside server.cpp
+
+#define HTTP_PROXY "51.159.4.98:80"
+#define HTTPS_PROXY "51.104.53.182:8000"
+
+using namespace cpr;
+
+TEST(ProxyTests, SingleProxyTest) {
+ Url url{"http://www.httpbin.org/get"};
+ Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyTests, MultipleProxyHttpTest) {
+ Url url{"http://www.httpbin.org/get"};
+ Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}, {"https", HTTPS_PROXY}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+// TODO: These should be fixed after a source code implementation of an HTTPS proxy
+#if defined(false)
+TEST(ProxyTests, ProxyHttpsTest) {
+ Url url{"https://www.httpbin.org/get"};
+ Response response = cpr::Get(url, Proxies{{"https", HTTPS_PROXY}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyTests, MultipleProxyHttpsTest) {
+ Url url{"https://www.httpbin.org/get"};
+ Response response = cpr::Get(url, Proxies{{"http", HTTP_PROXY}, {"https", HTTPS_PROXY}});
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+#endif
+
+TEST(ProxyTests, CopyProxyTest) {
+ Url url{"http://www.httpbin.org/get"};
+ Proxies proxies{{"http", HTTP_PROXY}};
+ Response response = cpr::Get(url, proxies);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyTests, ProxySessionTest) {
+ Url url{"http://www.httpbin.org/get"};
+ Session session;
+ session.SetUrl(url);
+ session.SetProxies(Proxies{{"http", HTTP_PROXY}});
+ Response response = session.Get();
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ProxyTests, ReferenceProxySessionTest) {
+ Url url{"http://www.httpbin.org/get"};
+ Proxies proxies{{"http", HTTP_PROXY}};
+ Session session;
+ session.SetUrl(url);
+ session.SetProxies(proxies);
+ Response response = session.Get();
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/put_tests.cpp b/Src/external_dependencies/cpr/test/put_tests.cpp
new file mode 100644
index 00000000..34e083ff
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/put_tests.cpp
@@ -0,0 +1,276 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cpr.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(PutTests, PutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, PutUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ Response response = cpr::Put(url, payload);
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutUnallowedAfterGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ }
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutUnallowedAfterHeadTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/get.html"};
+ session.SetUrl(url);
+ Response response = session.Head();
+ }
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, SessionPutUnallowedAfterPostTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ Response response = session.Post();
+ }
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ session.SetUrl(url);
+ session.SetPayload(payload);
+ Response response = session.Put();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, AsyncPutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ cpr::AsyncResponse future_response = cpr::PutAsync(url, payload);
+ cpr::Response response = future_response.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, AsyncPutUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ cpr::AsyncResponse future_response = cpr::PutAsync(url, payload);
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PutTests, AsyncMultiplePutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ Payload payload{{"x", "5"}};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::PutAsync(url, payload));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(PutTests, AsyncMultiplePutUnallowedTest) {
+ Url url{server->GetBaseUrl() + "/put_unallowed.html"};
+ Payload payload{{"x", "5"}};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ responses.emplace_back(cpr::PutAsync(url, payload));
+ }
+ for (cpr::AsyncResponse& future_response : responses) {
+ cpr::Response response = future_response.get();
+ std::string expected_text{"Method Not Allowed"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(405, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/raw_body_tests.cpp b/Src/external_dependencies/cpr/test/raw_body_tests.cpp
new file mode 100644
index 00000000..2416e4eb
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/raw_body_tests.cpp
@@ -0,0 +1,134 @@
+#include <gtest/gtest.h>
+
+#include <cstdio>
+#include <fstream>
+#include <string>
+
+#include <cpr/cpr.h>
+#include <cpr/multipart.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(BodyPostTests, DefaultUrlEncodedPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Body{"x=5"});
+ std::string expected_text = "{\n \"x\": 5\n}";
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, TextUrlEncodedPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Body{"x=hello world!!~"});
+ std::string expected_text{
+ "{\n"
+ " \"x\": hello world!!~\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, TextUrlEncodedNoCopyPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Body body{"x=hello world!!~"};
+ // body lives through the lifetime of Post, so it doesn't need to be copied
+ Response response = cpr::Post(url, body);
+ std::string expected_text{
+ "{\n"
+ " \"x\": hello world!!~\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, UrlEncodedManyPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Body{"x=5&y=13"});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5,\n"
+ " \"y\": 13,\n"
+ " \"sum\": 18\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, CustomHeaderNumberPostTest) {
+ Url url{server->GetBaseUrl() + "/json_post.html"};
+ Response response = cpr::Post(url, Body{"{\"x\":5}"}, Header{{"Content-Type", "application/json"}});
+ std::string expected_text{"{\"x\":5}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, CustomHeaderTextPostTest) {
+ Url url{server->GetBaseUrl() + "/json_post.html"};
+ Response response = cpr::Post(url, Body{"{\"x\":\"hello world!!~\"}"}, Header{{"Content-Type", "application/json"}});
+ std::string expected_text{"{\"x\":\"hello world!!~\"}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, CustomWrongHeaderPostTest) {
+ Url url{server->GetBaseUrl() + "/json_post.html"};
+ Response response = cpr::Post(url, Body{"{\"x\":5}"}, Header{{"Content-Type", "text/plain"}});
+ std::string expected_text{"Unsupported Media Type"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(415, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyPostTests, UrlPostBadHostTest) {
+ Url url{"http://bad_host/"};
+ Response response = cpr::Post(url, Body{"hello=world"});
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(0, response.status_code);
+ EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
+}
+
+TEST(BodyPostTests, StringMoveBodyTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Response response = cpr::Post(url, Body{std::string{"x=5"}});
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/resolve_tests.cpp b/Src/external_dependencies/cpr/test/resolve_tests.cpp
new file mode 100644
index 00000000..bf675414
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/resolve_tests.cpp
@@ -0,0 +1,44 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "cpr/cpr.h"
+#include "httpServer.hpp"
+
+using namespace cpr;
+
+static HttpServer* server = new HttpServer();
+
+TEST(ResolveTests, HelloWorldTest) {
+ Url url{"http://www.example.com:" + std::to_string(server->GetPort()) + "/hello.html"};
+ Resolve resolve{"www.example.com", "127.0.0.1", {server->GetPort()}};
+ Response response = cpr::Get(url, resolve);
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ResolveTests, RedirectMultiple) {
+ Url url1{"http://www.example0.com:" + std::to_string(server->GetPort()) + "/resolve_permanent_redirect.html"};
+ Url url2{"http://www.example1.com:" + std::to_string(server->GetPort()) + "/hello.html"};
+ Resolve resolve1{"www.example0.com", "127.0.0.1", {server->GetPort()}};
+ Resolve resolve2{"www.example1.com", "127.0.0.1", {server->GetPort()}};
+
+ Response response = cpr::Get(url1, std::vector<Resolve>{resolve1, resolve2}, Header{{"RedirectLocation", url2.str()}});
+
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url2, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/session_tests.cpp b/Src/external_dependencies/cpr/test/session_tests.cpp
new file mode 100644
index 00000000..81c86130
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/session_tests.cpp
@@ -0,0 +1,1462 @@
+#include <cstdint>
+#include <cstdlib>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+
+#include <cpr/cpr.h>
+#include <curl/curl.h>
+
+#include "httpServer.hpp"
+
+using namespace cpr;
+using namespace std::chrono_literals;
+
+static HttpServer* server = new HttpServer();
+std::chrono::milliseconds sleep_time{50};
+std::chrono::seconds zero{0};
+
+bool write_data(std::string /*data*/, intptr_t /*userdata*/) {
+ return true;
+}
+
+TEST(RedirectTests, TemporaryDefaultRedirectTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(RedirectTests, NoTemporaryRedirectTest) {
+ Url url{server->GetBaseUrl() + "/temporary_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(false));
+ Response response = session.Get();
+ std::string expected_text{"Moved Temporarily"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(302, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(RedirectTests, PermanentDefaultRedirectTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(RedirectTests, NoPermanentRedirectTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(false));
+ Response response = session.Get();
+ std::string expected_text{"Moved Permanently"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(301, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MaxRedirectsTests, ZeroMaxRedirectsSuccessTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(0L));
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MaxRedirectsTests, ZeroMaxRedirectsFailureTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(0L));
+ Response response = session.Get();
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(301, response.status_code);
+ EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);
+}
+
+TEST(MaxRedirectsTests, OneMaxRedirectsSuccessTest) {
+ Url url{server->GetBaseUrl() + "/permanent_redirect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(1L));
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MaxRedirectsTests, OneMaxRedirectsFailureTest) {
+ Url url{server->GetBaseUrl() + "/two_redirects.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(1L));
+ Response response = session.Get();
+ EXPECT_EQ(std::string{}, response.text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/permanent_redirect.html"}, response.url);
+ EXPECT_EQ(std::string{}, response.header["content-type"]);
+ EXPECT_EQ(301, response.status_code);
+ EXPECT_EQ(ErrorCode::TOO_MANY_REDIRECTS, response.error.code);
+}
+
+TEST(MaxRedirectsTests, TwoMaxRedirectsSuccessTest) {
+ Url url{server->GetBaseUrl() + "/two_redirects.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(2L));
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{server->GetBaseUrl() + "/hello.html"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MultipleGetTests, BasicMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ for (size_t i = 0; i < 100; ++i) {
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, UrlChangeMultipleGetTest) {
+ Session session;
+ {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Url url{server->GetBaseUrl() + "/basic.json"};
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{
+ "[\n"
+ " {\n"
+ " \"first_key\": \"first_value\",\n"
+ " \"second_key\": \"second_value\"\n"
+ " }\n"
+ "]"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, HeaderMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetHeader(Header{{"hello", "world"}});
+ for (size_t i = 0; i < 100; ++i) {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, HeaderChangeMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetHeader(Header{{"hello", "world"}});
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"world"}, response.header["hello"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ session.SetHeader(Header{{"key", "value"}});
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(std::string{"value"}, response.header["key"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, ParameterMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetParameters({{"hello", "world"}});
+ for (size_t i = 0; i < 100; ++i) {
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, ParameterChangeMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetParameters({{"hello", "world"}});
+ {
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ session.SetUrl(url);
+ session.SetParameters({{"key", "value"}});
+ {
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?key=value"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, BasicAuthenticationMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetAuth(Authentication{"user", "password", AuthMode::BASIC});
+ for (size_t i = 0; i < 100; ++i) {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(MultipleGetTests, BasicAuthenticationChangeMultipleGetTest) {
+ Url url{server->GetBaseUrl() + "/basic_auth.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetAuth(Authentication{"user", "password", AuthMode::BASIC});
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ session.SetAuth(Authentication{"user", "bad_password", AuthMode::BASIC});
+ {
+ Response response = session.Get();
+ EXPECT_EQ(std::string{"Unauthorized"}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ session.SetAuth(Authentication{"bad_user", "password", AuthMode::BASIC});
+ {
+ Response response = session.Get();
+ EXPECT_EQ(std::string{"Unauthorized"}, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/plain"}, response.header["content-type"]);
+ EXPECT_EQ(401, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(ParameterTests, ParameterSingleTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ Parameters parameters{{"hello", "world"}};
+ session.SetParameters(parameters);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ParameterTests, ParameterMultipleTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ Parameters parameters{{"hello", "world"}, {"key", "value"}};
+ session.SetParameters(parameters);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(Url{url + "?hello=world&key=value"}, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(FullRequestUrlTest, GetFullRequestUrlNoParametersTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ std::string expected_text{server->GetBaseUrl() + "/hello.html"};
+ EXPECT_EQ(expected_text, session.GetFullRequestUrl());
+}
+
+TEST(FullRequestUrlTest, GetFullRequestUrlOneParameterTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ Parameters parameters{{"hello", "world"}};
+ session.SetParameters(parameters);
+ std::string expected_text{server->GetBaseUrl() + "/hello.html" + "?hello=world"};
+ EXPECT_EQ(expected_text, session.GetFullRequestUrl());
+}
+
+TEST(FullRequestUrlTest, GetFullRequestUrlMultipleParametersTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ Parameters parameters{{"hello", "world"}, {"key", "value"}};
+ session.SetParameters(parameters);
+ std::string expected_text{server->GetBaseUrl() + "/hello.html" + "?hello=world&key=value"};
+ EXPECT_EQ(expected_text, session.GetFullRequestUrl());
+}
+
+TEST(TimeoutTests, SetTimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(0L);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(TimeoutTests, SetTimeoutLongTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(10000L);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(TimeoutTests, SetTimeoutLowSpeed) {
+ Url url{server->GetBaseUrl() + "/low_speed_timeout.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(1000);
+ Response response = session.Get();
+ EXPECT_EQ(url, response.url);
+ // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(TimeoutTests, SetChronoTimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(std::chrono::milliseconds{0});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(TimeoutTests, SetChronoTimeoutLongTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(std::chrono::milliseconds{10000});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(TimeoutTests, SetChronoLiteralTimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(2s);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(TimeoutTests, SetChronoLiteralTimeoutLowSpeed) {
+ Url url{server->GetBaseUrl() + "/low_speed_timeout.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetTimeout(1000ms);
+ Response response = session.Get();
+ EXPECT_EQ(url, response.url);
+ // Do not check for the HTTP status code, since libcurl always provides the status code of the header if it was received
+ EXPECT_EQ(ErrorCode::OPERATION_TIMEDOUT, response.error.code);
+}
+
+TEST(ConnectTimeoutTests, SetConnectTimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetConnectTimeout(0L);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ConnectTimeoutTests, SetConnectTimeoutLongTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetConnectTimeout(10000L);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ConnectTimeoutTests, SetChronoConnectTimeoutTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetConnectTimeout(std::chrono::milliseconds{0});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(ConnectTimeoutTests, SetChronoConnectTimeoutLongTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetConnectTimeout(std::chrono::milliseconds{10000});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(LowSpeedTests, SetLowSpeedTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetLowSpeed({1, 1});
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PayloadTests, SetPayloadTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetPayload({{"x", "5"}});
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(PayloadTests, SetPayloadLValueTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Session session;
+ session.SetUrl(url);
+ Payload payload{{"x", "5"}};
+ session.SetPayload(payload);
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MultipartTests, SetMultipartTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetMultipart({{"x", "5"}});
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(MultipartTests, SetMultipartValueTest) {
+ Url url{server->GetBaseUrl() + "/form_post.html"};
+ Session session;
+ session.SetUrl(url);
+ Multipart multipart{{"x", "5"}};
+ session.SetMultipart(multipart);
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": \"5\"\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyTests, SetBodyTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetBody(Body{"x=5"});
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BodyTests, SetBodyValueTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ Session session;
+ session.SetUrl(url);
+ Body body{"x=5"};
+ session.SetBody(body);
+ Response response = session.Post();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(DigestTests, SetDigestTest) {
+ Url url{server->GetBaseUrl() + "/digest_auth.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetAuth({"user", "password", AuthMode::DIGEST});
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UserAgentTests, SetUserAgentTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ UserAgent userAgent{"Test User Agent"};
+ Session session;
+ session.SetUrl(url);
+ session.SetUserAgent(userAgent);
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(userAgent, response.header["User-Agent"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(UserAgentTests, SetUserAgentStringViewTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ UserAgent userAgent{std::string_view{"Test User Agent"}};
+ Session session;
+ session.SetUrl(url);
+ session.SetUserAgent(userAgent);
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(userAgent, response.header["User-Agent"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CookiesTests, BasicCookiesTest) {
+ Url url{server->GetBaseUrl() + "/basic_cookies.html"};
+ Session session{};
+ session.SetUrl(url);
+ Response response = session.Get();
+ Cookies res_cookies{response.cookies};
+ std::string expected_text{"Basic Cookies"};
+ cpr::Cookies expectedCookies{
+ {"SID", "31d4d96e407aad42", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ {"lang", "en-US", "127.0.0.1", false, "/", true, std::chrono::system_clock::from_time_t(3905119080)},
+ };
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ for (auto cookie = res_cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != res_cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {
+ EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());
+ EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());
+ EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());
+ EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());
+ EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());
+ EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());
+ EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());
+ }
+}
+
+TEST(CookiesTests, ClientSetCookiesTest) {
+ Url url{server->GetBaseUrl() + "/cookies_reflect.html"};
+ {
+ Session session{};
+ session.SetUrl(url);
+ session.SetCookies(Cookies{
+ {"SID", "31d4d96e407aad42"},
+ {"lang", "en-US"},
+ });
+ Response response = session.Get();
+ std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"};
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ }
+ {
+ Session session{};
+ session.SetUrl(url);
+ Cookies cookie{
+ {"SID", "31d4d96e407aad42"},
+ {"lang", "en-US"},
+ };
+ session.SetCookies(cookie);
+ Response response = session.Get();
+ std::string expected_text{"SID=31d4d96e407aad42; lang=en-US;"};
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ }
+}
+
+TEST(CookiesTests, RedirectionWithChangingCookiesTest) {
+ Url url{server->GetBaseUrl() + "/redirection_with_changing_cookies.html"};
+ {
+ Session session{};
+ session.SetUrl(url);
+ session.SetCookies(Cookies{
+ {"SID", "31d4d96e407aad42"},
+ {"lang", "en-US"},
+ });
+ session.SetRedirect(Redirect(0L));
+ Response response = session.Get();
+ std::string expected_text{"Received cookies are: SID=31d4d96e407aad42; lang=en-US;"};
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ }
+ {
+ Session session{};
+ session.SetUrl(url);
+ session.SetRedirect(Redirect(1L));
+ Response response = session.Get();
+ std::string expected_text{"Received cookies are: lang=en-US; SID=31d4d96e407aad42"};
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ }
+ {
+ Session session{};
+ session.SetUrl(url);
+ session.SetCookies(Cookies{
+ {"SID", "empty_sid"},
+ });
+ session.SetRedirect(Redirect(1L));
+ Response response = session.Get();
+ std::string expected_text{"Received cookies are: lang=en-US; SID=31d4d96e407aad42; SID=empty_sid;"};
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ EXPECT_EQ(expected_text, response.text);
+ }
+}
+
+TEST(DifferentMethodTests, GetPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(DifferentMethodTests, PostGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(DifferentMethodTests, GetPostGetTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(DifferentMethodTests, PostGetPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(DifferentMethodTests, MultipleGetPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ for (size_t i = 0; i < 100; ++i) {
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ }
+}
+
+TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Url urlPost{server->GetBaseUrl() + "/post_reflect.html"};
+ Url urlPut{server->GetBaseUrl() + "/put.html"};
+ Session session;
+ for (size_t i = 0; i < 10; ++i) {
+ {
+ session.SetUrl(url);
+ Response response = session.Delete();
+ std::string expected_text{"Header reflect DELETE"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(urlPost);
+ std::string expectedBody = "a1b2c3Post";
+ session.SetBody(expectedBody);
+ Response response = session.Post();
+ EXPECT_EQ(expectedBody, response.text);
+ EXPECT_EQ(urlPost, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(url);
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(urlPut);
+ session.SetPayload({{"x", "5"}});
+ Response response = session.Put();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(urlPut, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ session.SetUrl(url);
+ Response response = session.Head();
+ std::string expected_text{"Header reflect HEAD"};
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ }
+}
+
+TEST(CurlHolderManipulateTests, CustomOptionTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ Session session;
+ session.SetUrl(url);
+ curl_easy_setopt(session.GetCurlHolder()->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE);
+ {
+ Response response = session.Get();
+ std::string expected_text{"Header reflect GET"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+ {
+ Response response = session.Post();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ }
+}
+
+TEST(LocalPortTests, SetLocalPortTest) {
+ Url url{server->GetBaseUrl() + "/local_port.html"};
+ Session session;
+ session.SetUrl(url);
+ std::uint16_t const local_port = 60252; // beware of HttpServer::GetPort when changing
+ std::uint16_t const local_port_range = 5000;
+ session.SetLocalPort(local_port);
+ session.SetLocalPortRange(local_port_range);
+ // expected response: body contains port number in specified range
+ // NOTE: even when trying up to 5000 ports there is the chance that all of them are occupied.
+ // It would be possible to also check here for ErrorCode::INTERNAL_ERROR but that somehow seems
+ // wrong as then this test would pass in case SetLocalPort does not work at all
+ // or in other words: we have to assume that at least one port in the specified range is free.
+ Response response = session.Get();
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ std::uint16_t port_from_response = std::strtoul(response.text.c_str(), nullptr, 10);
+ EXPECT_EQ(errno, 0);
+ EXPECT_GE(port_from_response, local_port);
+ EXPECT_LE(port_from_response, local_port + local_port_range);
+}
+
+TEST(LocalPortTests, SetOptionTest) {
+ Url url{server->GetBaseUrl() + "/local_port.html"};
+ Session session;
+ session.SetUrl(url);
+ std::uint16_t const local_port = 60551; // beware of HttpServer::GetPort when changing
+ std::uint16_t const local_port_range = 5000;
+ session.SetOption(LocalPort(local_port));
+ session.SetOption(LocalPortRange(local_port_range));
+ // expected response: body contains port number in specified range
+ // NOTE: even when trying up to 5000 ports there is the chance that all of them are occupied.
+ // It would be possible to also check here for ErrorCode::INTERNAL_ERROR but that somehow seems
+ // wrong as then this test would pass in case SetOption(LocalPort) does not work at all
+ // or in other words: we have to assume that at least one port in the specified range is free.
+ Response response = session.Get();
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+ unsigned long port_from_response = std::strtoul(response.text.c_str(), nullptr, 10);
+ EXPECT_EQ(errno, 0);
+ EXPECT_GE(port_from_response, local_port);
+ EXPECT_LE(port_from_response, local_port + local_port_range);
+}
+
+// The tests using the port of the server as a source port for curl fail for windows.
+// The reason probably is that Windows allows two sockets to bind to the same port if the full hostname is different.
+// In these tests, mongoose binds to http://127.0.0.1:61936, while libcurl binds to a different hostname, but still port 61936.
+// This seems to be okay for Windows, however, these tests expect an error like on Linux and MacOS
+// We therefore, simply skip the tests if Windows is used
+#ifndef _WIN32
+TEST(LocalPortTests, SetLocalPortTestOccupied) {
+ Url url{server->GetBaseUrl() + "/local_port.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetLocalPort(server->GetPort());
+ // expected response: request cannot be made as port is already occupied
+ Response response = session.Get();
+ EXPECT_EQ(ErrorCode::INTERNAL_ERROR, response.error.code);
+}
+
+TEST(LocalPortTests, SetOptionTestOccupied) {
+ Url url{server->GetBaseUrl() + "/local_port.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetOption(LocalPort(server->GetPort()));
+ // expected response: request cannot be made as port is already occupied
+ Response response = session.Get();
+ EXPECT_EQ(ErrorCode::INTERNAL_ERROR, response.error.code);
+}
+#endif // _WIN32
+
+TEST(BasicTests, ReserveResponseString) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetReserveSize(4096);
+ Response response = session.Get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_GE(response.text.capacity(), 4096);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, AcceptEncodingTestWithMethodsStringMap) {
+ Url url{server->GetBaseUrl() + "/check_accept_encoding.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetAcceptEncoding({{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}});
+ Response response = session.Get();
+ std::string expected_text{"deflate, gzip, zlib"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, AcceptEncodingTestWithMethodsStringMapLValue) {
+ Url url{server->GetBaseUrl() + "/check_accept_encoding.html"};
+ Session session;
+ session.SetUrl(url);
+ AcceptEncoding accept_encoding{{AcceptEncodingMethods::deflate, AcceptEncodingMethods::gzip, AcceptEncodingMethods::zlib}};
+ session.SetAcceptEncoding(accept_encoding);
+ Response response = session.Get();
+ std::string expected_text{"deflate, gzip, zlib"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, AcceptEncodingTestWithCostomizedString) {
+ Url url{server->GetBaseUrl() + "/check_accept_encoding.html"};
+ Session session;
+ session.SetUrl(url);
+ session.SetAcceptEncoding({{"deflate", "gzip", "zlib"}});
+ Response response = session.Get();
+ std::string expected_text{"deflate, gzip, zlib"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, AcceptEncodingTestWithCostomizedStringLValue) {
+ Url url{server->GetBaseUrl() + "/check_accept_encoding.html"};
+ Session session;
+ session.SetUrl(url);
+ AcceptEncoding accept_encoding{{"deflate", "gzip", "zlib"}};
+ session.SetAcceptEncoding(accept_encoding);
+ Response response = session.Get();
+ std::string expected_text{"deflate, gzip, zlib"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(BasicTests, DisableHeaderExpect100ContinueTest) {
+ Url url{server->GetBaseUrl() + "/check_expect_100_continue.html"};
+ std::string filename{"test_file"};
+ std::string content{std::string(1024 * 1024, 'a')};
+ std::ofstream test_file;
+ test_file.open(filename);
+ test_file << content;
+ test_file.close();
+ Session session{};
+ session.SetUrl(url);
+ session.SetMultipart({{"file", File{"test_file"}}});
+ Response response = session.Post();
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncGetTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ cpr::AsyncResponse future = session->GetAsync();
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+}
+
+TEST(AsyncRequestsTests, AsyncGetMultipleTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+
+ std::vector<AsyncResponse> responses;
+ std::vector<std::shared_ptr<Session>> sessions;
+ for (size_t i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ sessions.emplace_back(session);
+ responses.emplace_back(session->GetAsync());
+ }
+
+ for (cpr::AsyncResponse& future : responses) {
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ }
+}
+
+TEST(AsyncRequestsTests, AsyncGetMultipleTemporarySessionTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 10; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ responses.emplace_back(session->GetAsync());
+ }
+
+ for (cpr::AsyncResponse& future : responses) {
+ std::string expected_text{"Hello world!"};
+ cpr::Response response = future.get();
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ }
+}
+
+TEST(AsyncRequestsTests, AsyncGetMultipleReflectTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::vector<AsyncResponse> responses;
+ for (size_t i = 0; i < 100; ++i) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetParameters({{"key", std::to_string(i)}});
+ responses.emplace_back(session->GetAsync());
+ }
+ int i = 0;
+ for (cpr::AsyncResponse& future : responses) {
+ cpr::Response response = future.get();
+ std::string expected_text{"Hello world!"};
+ Url expected_url{url + "?key=" + std::to_string(i)};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(expected_url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ ++i;
+ }
+}
+
+TEST(AsyncRequestsTests, AsyncWritebackDownloadTest) {
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ cpr::Url url{server->GetBaseUrl() + "/download_gzip.html"};
+ session->SetUrl(url);
+ session->SetHeader(cpr::Header{{"Accept-Encoding", "gzip"}});
+ cpr::AsyncResponse future = session->DownloadAsync(cpr::WriteCallback{write_data, 0});
+ cpr::Response response = future.get();
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(cpr::ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncPostTest) {
+ Url url{server->GetBaseUrl() + "/url_post.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload({{"x", "5"}});
+ cpr::AsyncResponse future = session->PostAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(201, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncPutTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload({{"x", "5"}});
+ cpr::AsyncResponse future = session->PutAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncHeadTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ cpr::AsyncResponse future = session->HeadAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncDeleteTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ cpr::AsyncResponse future = session->DeleteAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect DELETE"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncOptionsTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ cpr::AsyncResponse future = session->OptionsAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect OPTIONS"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(AsyncRequestsTests, AsyncPatchTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ cpr::AsyncResponse future = session->PatchAsync();
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect PATCH"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, GetCallbackTest) {
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->GetCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{"Hello world!"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, PostCallbackTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->PostCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect POST"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, PutCallbackTest) {
+ Url url{server->GetBaseUrl() + "/put.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ session->SetPayload({{"x", "5"}});
+ auto future = session->PutCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{
+ "{\n"
+ " \"x\": 5\n"
+ "}"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, HeadCallbackTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->HeadCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{""};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, DeleteCallbackTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->DeleteCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect DELETE"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, OptionsCallbackTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->OptionsCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect OPTIONS"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+TEST(CallbackTests, PatchCallbackTest) {
+ Url url{server->GetBaseUrl() + "/header_reflect.html"};
+ std::shared_ptr<Session> session = std::make_shared<Session>();
+ session->SetUrl(url);
+ auto future = session->PatchCallback([](Response r) { return r; });
+ std::this_thread::sleep_for(sleep_time);
+ cpr::Response response = future.get();
+ std::string expected_text{"Header reflect PATCH"};
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(server);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/ssl_tests.cpp b/Src/external_dependencies/cpr/test/ssl_tests.cpp
new file mode 100644
index 00000000..41c73162
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/ssl_tests.cpp
@@ -0,0 +1,166 @@
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <cpr/cprtypes.h>
+#include <cpr/filesystem.h>
+#include <cpr/ssl_options.h>
+
+#include "httpsServer.hpp"
+
+
+using namespace cpr;
+
+static HttpsServer* server;
+
+static std::string caCertPath;
+static std::string serverPubKeyPath;
+static std::string clientKeyPath;
+static std::string clientCertPath;
+
+std::string loadCertificateFromFile(const std::string certPath) {
+ std::ifstream certFile(certPath);
+ std::stringstream buffer;
+ buffer << certFile.rdbuf();
+ return buffer.str();
+}
+
+TEST(SslTests, HelloWorldTestSimpel) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string baseDirPath{server->getBaseDirPath()};
+ std::string crtPath{baseDirPath + "certificates/"};
+ std::string keyPath{baseDirPath + "keys/"};
+
+ SslOptions sslOpts = Ssl(ssl::CaPath{crtPath + "root-ca.crt"}, ssl::CertFile{crtPath + "client.crt"}, ssl::KeyFile{keyPath + "client.key"}, ssl::VerifyPeer{false}, ssl::PinnedPublicKey{keyPath + "server.pub"}, ssl::VerifyHost{false}, ssl::VerifyStatus{false});
+ Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});
+ std::string expected_text = "Hello world!";
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;
+}
+
+TEST(SslTests, HelloWorldTestFull) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string baseDirPath{server->getBaseDirPath()};
+ std::string crtPath{baseDirPath + "certificates/"};
+ std::string keyPath{baseDirPath + "keys/"};
+
+ SslOptions sslOpts = Ssl(ssl::TLSv1{}, ssl::ALPN{false}, ssl::NPN{false}, ssl::CaPath{crtPath + "root-ca.crt"}, ssl::CertFile{crtPath + "client.crt"}, ssl::KeyFile{keyPath + "client.key"}, ssl::PinnedPublicKey{keyPath + "server.pub"}, ssl::VerifyPeer{false}, ssl::VerifyHost{false}, ssl::VerifyStatus{false});
+ Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});
+ std::string expected_text = "Hello world!";
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;
+}
+
+TEST(SslTests, GetCertInfos) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ Url url{server->GetBaseUrl() + "/hello.html"};
+ std::string baseDirPath{server->getBaseDirPath()};
+ std::string crtPath{baseDirPath + "certificates/"};
+ std::string keyPath{baseDirPath + "keys/"};
+
+ SslOptions sslOpts = Ssl(ssl::CaPath{crtPath + "root-ca.crt"}, ssl::CertFile{crtPath + "client.crt"}, ssl::KeyFile{keyPath + "client.key"}, ssl::VerifyPeer{false}, ssl::VerifyHost{false}, ssl::VerifyStatus{false});
+
+ Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});
+ std::vector<CertInfo> certInfos = response.GetCertInfos();
+
+ std::string expected_text = "Hello world!";
+ std::vector<CertInfo> expectedCertInfos{
+ CertInfo{
+ "Subject:CN = test-server",
+ "Issuer:C = GB, O = Example, CN = Root CA",
+ "Version:2",
+ "Serial Number:28c252871ec62a626a98006b0bf2888f",
+ "Signature Algorithm:ED25519",
+ "Public Key Algorithm:ED25519",
+ "X509v3 Subject Alternative Name:DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1",
+ "X509v3 Subject Key Identifier:39:C1:81:38:01:DC:55:38:E5:2F:4E:7A:D0:4C:84:7B:B7:27:D3:AF",
+ "X509v3 Authority Key Identifier:E4:F2:F3:85:0E:B7:85:75:84:76:E3:43:D1:B6:9D:14:B8:E2:A4:B7",
+ "Start date:Jun 29 11:33:07 2022 GMT",
+ "Expire date:Jun 28 11:33:07 2027 GMT",
+ "Signature:2e:0d:a1:0d:f5:90:77:e9:eb:84:7d:80:63:63:4d:8a:eb:d9:23:57:1f:21:2a:ed:81:b4:a8:58:b9:00:1b:cb:5c:90:1b:33:6b:f6:ec:42:20:63:54:d6:60:ee:37:14:1b:1c:95:0b:33:ea:67:29:d4:cc:d9:7e:34:fd:47:04:",
+ R"(Cert:-----BEGIN CERTIFICATE-----
+MIIBdTCCASegAwIBAgIQKMJShx7GKmJqmABrC/KIjzAFBgMrZXAwMTELMAkGA1UE
+BhMCR0IxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMjIw
+NjI5MTEzMzA3WhcNMjcwNjI4MTEzMzA3WjAWMRQwEgYDVQQDDAt0ZXN0LXNlcnZl
+cjAqMAUGAytlcAMhAI64JU5RjfdEG1KQMxS5DQWkiGlKIQO7ye4mNFq9QleTo3Aw
+bjAsBgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEw
+HQYDVR0OBBYEFDnBgTgB3FU45S9OetBMhHu3J9OvMB8GA1UdIwQYMBaAFOTy84UO
+t4V1hHbjQ9G2nRS44qS3MAUGAytlcANBAC4NoQ31kHfp64R9gGNjTYrr2SNXHyEq
+7YG0qFi5ABvLXJAbM2v27EIgY1TWYO43FBsclQsz6mcp1MzZfjT9RwQ=
+-----END CERTIFICATE-----
+)",
+ },
+ };
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;
+ EXPECT_EQ(1, certInfos.size());
+ for (auto certInfo_it = certInfos.begin(), expectedCertInfo_it = expectedCertInfos.begin(); certInfo_it != certInfos.end() && expectedCertInfo_it != expectedCertInfos.end(); certInfo_it++, expectedCertInfo_it++) {
+ for (auto entry_it = (*certInfo_it).begin(), expectedEntry_it = (*expectedCertInfo_it).begin(); entry_it != (*certInfo_it).end() && expectedEntry_it != (*expectedCertInfo_it).end(); entry_it++, expectedEntry_it++) {
+ std::string search_string = "Identifier:keyid:";
+ std::size_t search_index = (*entry_it).find(search_string);
+ if (search_index != std::string::npos) {
+ (*entry_it).replace(search_index, search_string.length(), "Identifier:");
+ search_string = "\n";
+ search_index = (*entry_it).find(search_string);
+ if (search_index != std::string::npos) {
+ (*entry_it).replace(search_index, search_string.length(), "");
+ }
+ }
+ EXPECT_EQ(*expectedEntry_it, *entry_it);
+ }
+ std::cout << std::endl;
+ }
+}
+
+#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
+TEST(SslTests, LoadCertFromBufferTestSimpel) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ Url url{server->GetBaseUrl() + "/hello.html"};
+
+ std::string baseDirPath{server->getBaseDirPath()};
+ std::string crtPath{baseDirPath + "certificates/"};
+ std::string keyPath{baseDirPath + "keys/"};
+ std::string certBuffer = loadCertificateFromFile(crtPath + "root-ca.crt");
+ SslOptions sslOpts = Ssl(ssl::CaBuffer{std::move(certBuffer)}, ssl::CertFile{crtPath + "client.crt"}, ssl::KeyFile{keyPath + "client.key"}, ssl::VerifyPeer{false}, ssl::VerifyHost{false}, ssl::VerifyStatus{false});
+ Response response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{});
+ std::string expected_text = "Hello world!";
+ EXPECT_EQ(expected_text, response.text);
+ EXPECT_EQ(url, response.url);
+ EXPECT_EQ(std::string{"text/html"}, response.header["content-type"]);
+ EXPECT_EQ(200, response.status_code);
+ EXPECT_EQ(ErrorCode::OK, response.error.code) << response.error.message;
+}
+#endif
+
+fs::path getBasePath(const std::string& execPath) {
+ return fs::path(fs::path{execPath}.parent_path().string() + "/").make_preferred();
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ fs::path baseDirPath = fs::path{getBasePath(argv[0]).string() + "data/"};
+ fs::path serverCertPath = fs::path{baseDirPath}.append("certificates/server.crt");
+ fs::path serverKeyPath = fs::path{baseDirPath}.append("keys/server.key");
+ server = new HttpsServer(std::move(baseDirPath), std::move(serverCertPath), std::move(serverKeyPath));
+ ::testing::AddGlobalTestEnvironment(server);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/structures_tests.cpp b/Src/external_dependencies/cpr/test/structures_tests.cpp
new file mode 100644
index 00000000..026362e4
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/structures_tests.cpp
@@ -0,0 +1,62 @@
+#include "cpr/cprtypes.h"
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/parameters.h>
+#include <cpr/payload.h>
+
+using namespace cpr;
+
+TEST(PayloadTests, UseStringVariableTest) {
+ std::string value1 = "hello";
+ std::string key2 = "key2";
+ Payload payload{{"key1", value1}, {key2, "world"}};
+
+ std::string expected = "key1=hello&key2=world";
+ EXPECT_EQ(payload.GetContent(CurlHolder()), expected);
+}
+
+TEST(PayloadTests, DisableEncodingTest) {
+ std::string key1 = "key1";
+ std::string key2 = "key2§$%&/";
+ std::string value1 = "hello.,.,";
+ std::string value2 = "hello";
+ Payload payload{{key1, value1}, {key2, value2}};
+ payload.encode = false;
+
+ std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2;
+ EXPECT_EQ(payload.GetContent(CurlHolder()), expected);
+}
+
+TEST(ParametersTests, UseStringVariableTest) {
+ std::string value1 = "hello";
+ std::string key2 = "key2";
+ Parameters parameters{{"key1", value1}, {key2, "world"}};
+
+ std::string expected = "key1=hello&key2=world";
+ EXPECT_EQ(parameters.GetContent(CurlHolder()), expected);
+}
+
+TEST(ParametersTests, DisableEncodingTest) {
+ std::string key1 = "key1";
+ std::string key2 = "key2§$%&/";
+ std::string value1 = "hello.,.,";
+ std::string value2 = "hello";
+ Parameters parameters{{key1, value1}, {key2, value2}};
+ parameters.encode = false;
+
+ std::string expected = key1 + '=' + value1 + '&' + key2 + '=' + value2;
+ EXPECT_EQ(parameters.GetContent(CurlHolder()), expected);
+}
+
+TEST(UrlToAndFromString, UrlTests) {
+ std::string s{"https://github.com/whoshuu/cpr"};
+ cpr::Url url = s;
+ EXPECT_EQ(s, url.str());
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/util_tests.cpp b/Src/external_dependencies/cpr/test/util_tests.cpp
new file mode 100644
index 00000000..33977530
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/util_tests.cpp
@@ -0,0 +1,237 @@
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <cpr/cprtypes.h>
+#include <cpr/util.h>
+
+using namespace cpr;
+
+TEST(UtilParseCookiesTests, BasicParseTest) {
+ Cookies expectedCookies{{Cookie("status", "on", "127.0.0.1", false, "/", false, std::chrono::system_clock::from_time_t(1656908640)), Cookie("name", "debug", "127.0.0.1", false, "/", false, std::chrono::system_clock::from_time_t(0))}};
+ curl_slist* raw_cookies = new curl_slist{
+ (char*) "127.0.0.1\tFALSE\t/\tFALSE\t1656908640\tstatus\ton",
+ new curl_slist{
+ (char*) "127.0.0.1\tFALSE\t/\tFALSE\t0\tname\tdebug",
+ nullptr,
+ },
+ };
+ Cookies cookies = util::parseCookies(raw_cookies);
+ for (auto cookie = cookies.begin(), expectedCookie = expectedCookies.begin(); cookie != cookies.end() && expectedCookie != expectedCookies.end(); cookie++, expectedCookie++) {
+ EXPECT_EQ(expectedCookie->GetName(), cookie->GetName());
+ EXPECT_EQ(expectedCookie->GetValue(), cookie->GetValue());
+ EXPECT_EQ(expectedCookie->GetDomain(), cookie->GetDomain());
+ EXPECT_EQ(expectedCookie->IsIncludingSubdomains(), cookie->IsIncludingSubdomains());
+ EXPECT_EQ(expectedCookie->GetPath(), cookie->GetPath());
+ EXPECT_EQ(expectedCookie->IsHttpsOnly(), cookie->IsHttpsOnly());
+ EXPECT_EQ(expectedCookie->GetExpires(), cookie->GetExpires());
+ }
+ delete raw_cookies->next;
+ delete raw_cookies;
+}
+
+TEST(UtilParseHeaderTests, BasicParseTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\r\n"
+ "Server: nginx\r\n"
+ "Date: Sun, 05 Mar 2017 00:34:54 GMT\r\n"
+ "Content-Type: application/json\r\n"
+ "Content-Length: 351\r\n"
+ "Connection: keep-alive\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "\r\n"};
+ Header header = util::parseHeader(header_string);
+ EXPECT_EQ(std::string{"nginx"}, header["Server"]);
+ EXPECT_EQ(std::string{"Sun, 05 Mar 2017 00:34:54 GMT"}, header["Date"]);
+ EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]);
+ EXPECT_EQ(std::string{"351"}, header["Content-Length"]);
+ EXPECT_EQ(std::string{"keep-alive"}, header["Connection"]);
+ EXPECT_EQ(std::string{"*"}, header["Access-Control-Allow-Origin"]);
+ EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]);
+}
+
+TEST(UtilParseHeaderTests, NewlineTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\r\n"
+ "Auth:\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "\r\n"};
+ Header header = util::parseHeader(header_string);
+ EXPECT_EQ(std::string{""}, header["Server"]);
+ EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]);
+}
+
+TEST(UtilParseHeaderTests, SpaceNewlineTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\r\n"
+ "Auth: \n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "\r\n"};
+ Header header = util::parseHeader(header_string);
+ EXPECT_EQ(std::string{""}, header["Server"]);
+ EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]);
+}
+
+TEST(UtilParseHeaderTests, CarriageReturnNewlineTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\n"
+ "Auth:\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "\r\n"};
+ Header header = util::parseHeader(header_string);
+ EXPECT_EQ(std::string{""}, header["Server"]);
+ EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]);
+}
+
+TEST(UtilParseHeaderTests, SpaceCarriageReturnNewlineTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\n"
+ "Auth: \r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "\r\n"};
+ Header header = util::parseHeader(header_string);
+ EXPECT_EQ(std::string{""}, header["Server"]);
+ EXPECT_EQ(std::string{"true"}, header["Access-Control-Allow-Credentials"]);
+}
+
+TEST(UtilParseHeaderTests, BasicStatusLineTest) {
+ std::string header_string{
+ "HTTP/1.1 200 OK\r\n"
+ "Server: nginx\r\n"
+ "Content-Type: application/json\r\n"
+ "\r\n"};
+ std::string status_line;
+ std::string reason;
+ Header header = util::parseHeader(header_string, &status_line, &reason);
+ EXPECT_EQ(std::string{"HTTP/1.1 200 OK"}, status_line);
+ EXPECT_EQ(std::string{"OK"}, reason);
+ EXPECT_EQ(std::string{"nginx"}, header["Server"]);
+ EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]);
+}
+
+TEST(UtilParseHeaderTests, NewlineStatusLineTest) {
+ std::string header_string{
+ "HTTP/1.1 407 Proxy Authentication Required\n"
+ "Server: nginx\r\n"
+ "Content-Type: application/json\r\n"
+ "\r\n"};
+ std::string status_line;
+ std::string reason;
+ Header header = util::parseHeader(header_string, &status_line, &reason);
+ EXPECT_EQ(std::string{"HTTP/1.1 407 Proxy Authentication Required"}, status_line);
+ EXPECT_EQ(std::string{"Proxy Authentication Required"}, reason);
+ EXPECT_EQ(std::string{"nginx"}, header["Server"]);
+ EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]);
+}
+
+TEST(UtilParseHeaderTests, NoReasonSpaceTest) {
+ std::string header_string{
+ "HTTP/1.1 200 \n"
+ "Server: nginx\r\n"
+ "Content-Type: application/json\r\n"
+ "\r\n"};
+ std::string status_line;
+ std::string reason;
+ Header header = util::parseHeader(header_string, &status_line, &reason);
+ EXPECT_EQ(std::string{"HTTP/1.1 200"}, status_line);
+ EXPECT_EQ(std::string{""}, reason);
+ EXPECT_EQ(std::string{"nginx"}, header["Server"]);
+ EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]);
+}
+
+TEST(UtilParseHeaderTests, NoReasonTest) {
+ std::string header_string{
+ "HTTP/1.1 200\n"
+ "Server: nginx\r\n"
+ "Content-Type: application/json\r\n"
+ "\r\n"};
+ std::string status_line;
+ std::string reason;
+ Header header = util::parseHeader(header_string, &status_line, &reason);
+ EXPECT_EQ(std::string{"HTTP/1.1 200"}, status_line);
+ EXPECT_EQ(std::string{""}, reason);
+ EXPECT_EQ(std::string{"nginx"}, header["Server"]);
+ EXPECT_EQ(std::string{"application/json"}, header["Content-Type"]);
+}
+
+TEST(UtilUrlEncodeTests, UnicodeEncoderTest) {
+ std::string input = "一二三";
+ std::string result = util::urlEncode(input);
+ std::string expected = "%E4%B8%80%E4%BA%8C%E4%B8%89";
+ EXPECT_EQ(result, expected);
+}
+
+TEST(UtilUrlEncodeTests, AsciiEncoderTest) {
+ std::string input = "Hello World!";
+ std::string result = util::urlEncode(input);
+ std::string expected = "Hello%20World%21";
+ EXPECT_EQ(result, expected);
+}
+
+TEST(UtilUrlDecodeTests, UnicodeDecoderTest) {
+ std::string input = "%E4%B8%80%E4%BA%8C%E4%B8%89";
+ std::string result = util::urlDecode(input);
+ std::string expected = "一二三";
+ EXPECT_EQ(result, expected);
+}
+
+TEST(UtilUrlDecodeTests, AsciiDecoderTest) {
+ std::string input = "Hello%20World%21";
+ std::string result = util::urlDecode(input);
+ std::string expected = "Hello World!";
+ EXPECT_EQ(result, expected);
+}
+
+TEST(UtilSecureStringClearTests, EmptyStringTest) {
+ std::string input;
+ util::secureStringClear(input);
+ EXPECT_TRUE(input.empty());
+}
+
+TEST(UtilSecureStringClearTests, NotEmptyStringTest) {
+ std::string input = "Hello World!";
+ util::secureStringClear(input);
+ EXPECT_TRUE(input.empty());
+}
+
+TEST(UtilIsTrueTests, TrueTest) {
+ {
+ std::string input = "TRUE";
+ bool output = util::isTrue(input);
+ EXPECT_TRUE(output);
+ }
+ {
+ std::string input = "True";
+ bool output = util::isTrue(input);
+ EXPECT_TRUE(output);
+ }
+ {
+ std::string input = "true";
+ bool output = util::isTrue(input);
+ EXPECT_TRUE(output);
+ }
+}
+
+TEST(UtilIsTrueTests, FalseTest) {
+ {
+ std::string input = "FALSE";
+ bool output = util::isTrue(input);
+ EXPECT_FALSE(output);
+ }
+ {
+ std::string input = "False";
+ bool output = util::isTrue(input);
+ EXPECT_FALSE(output);
+ }
+ {
+ std::string input = "false";
+ bool output = util::isTrue(input);
+ EXPECT_FALSE(output);
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/Src/external_dependencies/cpr/test/version_tests.cpp b/Src/external_dependencies/cpr/test/version_tests.cpp
new file mode 100644
index 00000000..4c589d29
--- /dev/null
+++ b/Src/external_dependencies/cpr/test/version_tests.cpp
@@ -0,0 +1,65 @@
+#include <cctype>
+#include <cpr/cpr.h>
+#include <cstddef>
+#include <gtest/gtest.h>
+#include <string>
+
+
+TEST(VersionTests, StringVersionExists) {
+#ifndef CPR_VERSION
+ EXPECT_TRUE(false);
+#endif // CPR_VERSION
+}
+
+TEST(VersionTests, StringVersionValid) {
+ EXPECT_TRUE(CPR_VERSION != nullptr);
+ std::string version = CPR_VERSION;
+
+ // Check if the version string is: '\d+\.\d+\.\d+'
+ bool digit = true;
+ size_t dotCount = 0;
+ for (size_t i = 0; i < version.size(); i++) {
+ if (i == 0) {
+ EXPECT_TRUE(std::isdigit(version[i]));
+ } else if (digit) {
+ if (version[i] == '.') {
+ digit = false;
+ dotCount++;
+ continue;
+ }
+ }
+ EXPECT_TRUE(std::isdigit(version[i]));
+ digit = true;
+ }
+ EXPECT_EQ(dotCount, 2);
+}
+
+TEST(VersionTests, VersionMajorExists) {
+#ifndef CPR_VERSION_MAJOR
+ EXPECT_TRUE(false);
+#endif // CPR_VERSION_MAJOR
+}
+
+TEST(VersionTests, VersionMinorExists) {
+#ifndef CPR_VERSION_MINOR
+ EXPECT_TRUE(false);
+#endif // CPR_VERSION_MINOR
+}
+
+TEST(VersionTests, VersionPatchExists) {
+#ifndef CPR_VERSION_PATCH
+ EXPECT_TRUE(false);
+#endif // CPR_VERSION_PATCH
+}
+
+TEST(VersionTests, VersionNumExists) {
+#ifndef CPR_VERSION_NUM
+ EXPECT_TRUE(false);
+#endif // CPR_VERSION_NUM
+}
+
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}