diff --git a/cpr/CMakeLists.txt b/cpr/CMakeLists.txt index c4f9b5b4f..845654673 100644 --- a/cpr/CMakeLists.txt +++ b/cpr/CMakeLists.txt @@ -16,7 +16,6 @@ add_library(${CPR_LIBRARIES} timeout.cpp util.cpp unix_socket.cpp - ssl_options.cpp # Header files (useful in IDEs) "${CPR_INCLUDE_DIRS}/cpr/api.h" diff --git a/cpr/session.cpp b/cpr/session.cpp index 5ddbaf261..983b9b25e 100644 --- a/cpr/session.cpp +++ b/cpr/session.cpp @@ -13,6 +13,9 @@ namespace cpr { +const long ON = 1L; +const long OFF = 0L; + class Session::Impl { public: Impl(); @@ -38,10 +41,11 @@ class Session::Impl { void SetBody(Body&& body); void SetBody(const Body& body); void SetLowSpeed(const LowSpeed& low_speed); - void SetVerbose(const Verbose& verbose); void SetVerifySsl(const VerifySsl& verify); void SetLimitRate(const LimitRate& limit_rate); void SetUnixSocket(const UnixSocket& unix_socket); + void SetVerbose(const Verbose& verbose); + void SetSslOptions(const SslOptions& options); Response Delete(); Response Download(std::ofstream& file); @@ -165,7 +169,7 @@ void Session::Impl::SetConnectTimeout(const ConnectTimeout& timeout) { void Session::Impl::SetVerbose(const Verbose& verbose) { auto curl = curl_->handle; if (curl) { - curl_easy_setopt(curl, CURLOPT_VERBOSE, verbose.verbose); + curl_easy_setopt(curl, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF); } } @@ -338,7 +342,7 @@ void Session::Impl::SetLowSpeed(const LowSpeed& low_speed) { void Session::Impl::SetVerifySsl(const VerifySsl& verify) { auto curl = curl_->handle; if (curl) { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, verify ? 1L : 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L); } } @@ -350,6 +354,58 @@ void Session::Impl::SetUnixSocket(const UnixSocket& unix_socket) { } } +void Session::Impl::SetSslOptions(const SslOptions& opts) { + auto curl = curl_->handle; + if (curl) { + curl_easy_setopt(curl, CURLOPT_SSLCERT, opts.cert_file.c_str()); + if (!opts.cert_type.empty()) { + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, opts.cert_type.c_str()); + } + curl_easy_setopt(curl, CURLOPT_SSLKEY, opts.key_file.c_str()); + if (!opts.key_type.empty()) { + curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, opts.key_type.c_str()); + } + if (!opts.key_pass.empty()) { + curl_easy_setopt(curl, CURLOPT_KEYPASSWD, opts.key_pass.c_str()); + } +#if SUPPORT_ALPN + curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, opts.enable_alpn ? ON : OFF); +#endif +#if SUPPORT_NPN + curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_NPN, opts.enable_npn ? ON : OFF); +#endif + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, opts.verify_peer ? ON : OFF); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, opts.verify_host ? 2L : 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, opts.verify_status ? ON : OFF); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, + opts.ssl_version +#if SUPPORT_MAX_TLS_VERSION + | opts.max_version +#endif + ); + if (!opts.ca_info.empty()) { + curl_easy_setopt(curl, CURLOPT_CAINFO, opts.ca_info.c_str()); + } + if (!opts.ca_path.empty()) { + curl_easy_setopt(curl, CURLOPT_CAPATH, opts.ca_path.c_str()); + } + if (!opts.crl_file.empty()) { + curl_easy_setopt(curl, CURLOPT_CRLFILE, opts.crl_file.c_str()); + } + if (!opts.ciphers.empty()) { + curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, opts.ciphers.c_str()); + } +#if SUPPORT_TLSv13_CIPHERS + if (!opts.tls13_ciphers.empty()) { + curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS, opts.ciphers.c_str()); + } +#endif +#if SUPPORT_SESSIONID_CACHE + curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, opts.session_id_cache ? ON : OFF); +#endif + } +} + Response Session::Impl::Delete() { auto curl = curl_->handle; if (curl) { @@ -478,7 +534,12 @@ Response Session::Impl::makeDownloadRequest(CURL* curl, std::ofstream& file) { auto header = cpr::util::parseHeader(header_string); return Response{static_cast(response_code), - std::string{}, header, raw_url, elapsed, cookies, error}; + std::string{}, + header, + raw_url, + elapsed, + cookies, + error}; } Response Session::Impl::makeRequest(CURL* curl) { @@ -498,8 +559,8 @@ Response Session::Impl::makeRequest(CURL* curl) { #if LIBCURL_VERSION_MAJOR >= 7 #if LIBCURL_VERSION_MINOR >= 21 - /* enable all supported built-in compressions */ - curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + /* enable all supported built-in compressions */ + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); #endif #endif @@ -596,6 +657,7 @@ void Session::SetOption(const LowSpeed& low_speed) { pimpl_->SetLowSpeed(low_spe void Session::SetOption(const VerifySsl& verify) { pimpl_->SetVerifySsl(verify); } void Session::SetOption(const Verbose& verbose) { pimpl_->SetVerbose(verbose); } void Session::SetOption(const UnixSocket& unix_socket) { pimpl_->SetUnixSocket(unix_socket); } +void Session::SetOption(const SslOptions& options) { pimpl_->SetSslOptions(options); } Response Session::Delete() { return pimpl_->Delete(); } Response Session::Download(std::ofstream& file) { return pimpl_->Download(file); } diff --git a/cpr/ssl_options.cpp b/cpr/ssl_options.cpp deleted file mode 100644 index 79d77f75b..000000000 --- a/cpr/ssl_options.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "cpr/ssl_options.h" - -namespace cpr { - -VerifySsl::VerifySsl(bool verify) : verify_{verify} {} - -VerifySsl::operator bool() const { - return verify_; -} - -} // namespace cpr diff --git a/gen-test-key.sh b/gen-test-key.sh new file mode 100755 index 000000000..bafb98da3 --- /dev/null +++ b/gen-test-key.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +mkdir -p test/data +cd test/data + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -key ca.key -out ca.crt + +openssl genrsa -out key.pem 2048 +openssl req -new -key key.pem -out cert.csr +openssl x509 -req -in cert.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out cert.pem + +c_rehash . \ No newline at end of file diff --git a/include/cpr/session.h b/include/cpr/session.h index cee87a088..e2a70078c 100644 --- a/include/cpr/session.h +++ b/include/cpr/session.h @@ -7,9 +7,11 @@ #include "cpr/auth.h" #include "cpr/body.h" +#include "cpr/connect_timeout.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/digest.h" +#include "cpr/limit_rate.h" #include "cpr/low_speed.h" #include "cpr/max_redirects.h" #include "cpr/multipart.h" @@ -17,14 +19,11 @@ #include "cpr/payload.h" #include "cpr/proxies.h" #include "cpr/response.h" -#include "cpr/timeout.h" -#include "cpr/connect_timeout.h" #include "cpr/ssl_options.h" #include "cpr/timeout.h" +#include "cpr/unix_socket.h" #include "cpr/user_agent.h" #include "cpr/verbose.h" -#include "cpr/limit_rate.h" -#include "cpr/unix_socket.h" namespace cpr { @@ -56,6 +55,8 @@ class Session { void SetLowSpeed(const LowSpeed& low_speed); void SetVerifySsl(const VerifySsl& verify); void SetUnixSocket(const UnixSocket& unix_socket); + void SetSslOptions(const SslOptions& options); + void SetVerbose(const Verbose& verbose); // Used in templated functions void SetOption(const Url& url); @@ -83,6 +84,7 @@ class Session { void SetOption(const VerifySsl& verify); void SetOption(const Verbose& verbose); void SetOption(const UnixSocket& unix_socket); + void SetOption(const SslOptions& options); Response Delete(); Response Download(std::ofstream& file); diff --git a/include/cpr/ssl_options.h b/include/cpr/ssl_options.h index ebee0dff5..094bfd8cc 100644 --- a/include/cpr/ssl_options.h +++ b/include/cpr/ssl_options.h @@ -1,19 +1,501 @@ #ifndef CPR_SSLOPTIONS_H #define CPR_SSLOPTIONS_H +#include + +#include + +#include "defines.h" + +#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 + namespace cpr { class VerifySsl { public: VerifySsl() {} - VerifySsl(bool verify); + VerifySsl(bool verify) : verify(verify) {} + + operator bool() const { + return verify; + } + + bool verify = true; +}; + +namespace ssl { + +// set SSL client certificate +class CertFile { + public: + template + CertFile(FileType&& p_filename) : filename(CPR_FWD(p_filename)) {} + + std::string filename; + + virtual const char* GetCertType(void) const { + return "PEM"; + } +}; + +typedef CertFile PemCert; + +class DerCert : public CertFile { + public: + template + DerCert(FileType&& p_filename) : CertFile(CPR_FWD(p_filename)) {} + + virtual const char* GetCertType(void) const { + return "DER"; + } +}; - operator bool() const; +// specify private keyfile for TLS and SSL client cert +class KeyFile { + public: + template + KeyFile(FileType&& p_filename) : filename(CPR_FWD(p_filename)) {} + + template + KeyFile(FileType&& p_filename, PassType p_password) + : filename(CPR_FWD(p_filename)), password(CPR_FWD(p_password)) {} + + std::string filename; + std::string password; + + virtual const char* GetKeyType(void) const { + return "PEM"; + } +}; + +typedef KeyFile PemKey; + +class DerKey : public KeyFile { + public: + template + DerKey(FileType&& p_filename) : KeyFile(CPR_FWD(p_filename)) {} + + template + DerKey(FileType&& p_filename, PassType p_password) + : KeyFile(CPR_FWD(p_filename), CPR_FWD(p_password)) {} - private: - bool verify_ = true; + virtual const char* GetKeyType(void) const { + return "DER"; + } }; +#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() {} + ALPN(bool enabled) : enabled(enabled) {} + + 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() {} + NPN(bool enabled) : enabled(enabled) {} + + 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() {} + VerifyHost(bool enabled) : enabled(enabled) {} + + 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() {} + VerifyPeer(bool enabled) : enabled(enabled) {} + + 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: + VerifyStatus(bool enabled) : enabled(enabled) {} + + 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: + template + CaInfo(FileType&& p_filename) : filename(CPR_FWD(p_filename)) {} + + std::string filename; +}; + +// specify directory holding CA certificates +class CaPath { + public: + template + CaPath(FileType&& p_filename) : filename(CPR_FWD(p_filename)) {} + + std::string filename; +}; + +// specify a Certificate Revocation List file +class Crl { + public: + template + Crl(FileType&& p_filename) : filename(CPR_FWD(p_filename)) {} + + std::string filename; +}; + +// specify ciphers to use for TLS +class Ciphers { + public: + template + Ciphers(T&& p_ciphers) : ciphers(CPR_FWD(p_ciphers)) {} + + std::string ciphers; +}; + +#if SUPPORT_TLSv13_CIPHERS +// specify ciphers suites to use for TLS 1.3 +class TLS13_Ciphers { + public: + template + TLS13_Ciphers(T&& p_ciphers) : ciphers(CPR_FWD(p_ciphers)) {} + + std::string ciphers; +}; +#endif + +#if SUPPORT_SESSIONID_CACHE +// enable/disable use of the SSL session-ID cache +class SessionIdCache { + public: + SessionIdCache() {} + SessionIdCache(bool enabled) : enabled(enabled) {} + + operator bool() const { + return enabled; + } + + bool enabled = true; +}; +#endif + +#if SUPPORT_SSL_FALSESTART +class SslFastStart { + public: + SslFastStart() {} + SslFastStart(bool enabled) : enabled(enabled) {} + + operator bool() const { + return enabled; + } + + bool enabled = false; +}; +#endif + +} // namespace ssl + +struct SslOptions { + std::string cert_file; + std::string cert_type; + std::string key_file; + std::string key_type; + std::string key_pass; +#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_MAX_TLS_VERSION + int max_version = CURL_SSLVERSION_MAX_DEFAULT; +#endif + std::string ca_info; + std::string ca_path; + 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 + + void SetOption(const ssl::CertFile& opt) { + cert_file = opt.filename; + cert_type = opt.GetCertType(); + } + void SetOption(const ssl::KeyFile& opt) { + key_file = opt.filename; + key_type = opt.GetKeyType(); + key_pass = opt.password; + } +#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_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; + } + void SetOption(const ssl::CaPath& opt) { + ca_path = opt.filename; + } + void SetOption(const ssl::Crl& opt) { + crl_file = opt.filename; + } + void SetOption(const ssl::Ciphers& opt) { + ciphers = opt.ciphers; + } +#if SUPPORT_TLSv13_CIPHERS + void SetOption(const ssl::TLS13_Ciphers& opt) { + tls13_ciphers = opt.ciphers; + } +#endif +#if SUPPORT_SESSIONID_CACHE + void SetOption(const ssl::SessionIdCache& opt) { + session_id_cache = opt.enabled; + } +#endif +}; + +namespace priv { + +template +void set_ssl_option(SslOptions& opts, T&& t) { + opts.SetOption(CPR_FWD(t)); +} + +template +void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) { + set_ssl_option(opts, CPR_FWD(t)); + set_ssl_option(opts, CPR_FWD(ts)...); +} + +} // namespace priv + +template +SslOptions Ssl(Ts&&... ts) { + SslOptions opts; + priv::set_ssl_option(opts, CPR_FWD(ts)...); + return opts; +} + } // namespace cpr #endif diff --git a/include/cpr/verbose.h b/include/cpr/verbose.h index 064879ba2..863c228c2 100644 --- a/include/cpr/verbose.h +++ b/include/cpr/verbose.h @@ -1,4 +1,3 @@ - #ifndef CPR_VERBOSE_H_ #define CPR_VERBOSE_H_ @@ -8,13 +7,13 @@ namespace cpr { class Verbose { public: + Verbose() {} Verbose(const bool verbose) : verbose{verbose} {} - bool verbose; + bool verbose = true; }; } // namespace cpr - -#endif /* CPR_VERBOSE_H_ */ +#endif /* CPR_VERBOSE_H_ */ \ No newline at end of file diff --git a/opt/CMakeLists.txt b/opt/CMakeLists.txt index 90b43cd27..5a3dc517a 100644 --- a/opt/CMakeLists.txt +++ b/opt/CMakeLists.txt @@ -51,12 +51,12 @@ if(NOT USE_SYSTEM_CURL OR NOT CURL_FOUND) set(CURL_INCLUDE_DIRS ${CURL_SOURCE_DIR}/include ${CURL_BINARY_DIR}/include/curl) - + # 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() @@ -65,6 +65,16 @@ set_cache_variable(CURL_LIBRARIES "Location of libcurl") set_cache_variable(CURL_INCLUDE_DIRS "Location of curl include files") +# SSL configuration +if(CMAKE_USE_OPENSSL) + find_package(OpenSSL REQUIRED) + + message(STATUS "using system OpenSSL library.") + + include_directories(${OPENSSL_INCLUDE_DIR}) + set(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) +endif() + # GTest configuration if(BUILD_CPR_TESTS) @@ -85,7 +95,7 @@ if(BUILD_CPR_TESTS) set(GTEST_MAIN_LIBRARIES gtest_main) set(GTEST_BOTH_LIBRARIES gtest gtest_main) set(GTEST_INCLUDE_DIRS ${gtest_SOURCE_DIR}/include) - + # 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") @@ -103,15 +113,24 @@ endif() if(BUILD_CPR_TESTS) message(STATUS "Building mongoose project for test support.") - add_subdirectory(mongoose) + + project(mongoose C) + add_library(mongoose STATIC mongoose/mongoose.c) + if(CMAKE_USE_OPENSSL) + target_compile_definitions(mongoose PUBLIC NS_ENABLE_SSL) + target_link_libraries(mongoose ${OPENSSL_LIBRARIES}) + endif() + set(MONGOOSE_FOUND TRUE) set(MONGOOSE_LIBRARIES mongoose) - set(MONGOOSE_INCLUDE_DIRS ${mongoose_SOURCE_DIR}) + set(MONGOOSE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/mongoose) + + include_directories(${MONGOOSE_INCLUDE_DIRS}) set_cache_variable(MONGOOSE_FOUND "Set if libmongoose was found or built") set_cache_variable(MONGOOSE_LIBRARIES "Location of libmongoose") set_cache_variable(MONGOOSE_INCLUDE_DIRS "Location of mongoose include files") - + # Group under the "external" project folder in IDEs such as Visual Studio. set_property(TARGET mongoose PROPERTY FOLDER "external") endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c5c001f35..dffe8ee61 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,6 +2,8 @@ find_package(Threads) set(TEST_SERVER_LIBRARY test_server) add_library(${TEST_SERVER_LIBRARY} server.cpp) +target_include_directories(${TEST_SERVER_LIBRARY} BEFORE PRIVATE + ${MONGOOSE_INCLUDE_DIRS}) target_link_libraries(${TEST_SERVER_LIBRARY} ${MONGOOSE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) @@ -13,7 +15,7 @@ macro(add_cpr_test _TEST_NAME) ${TEST_SERVER_LIBRARY} ${GTEST_LIBRARIES} ${CPR_LIBRARIES}) - add_test(NAME cpr_${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests) + add_test(NAME cpr_${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests ${CMAKE_CURRENT_SOURCE_DIR}/data/) # Group under the "tests" project folder in IDEs such as Visual Studio. set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") if(WIN32) @@ -49,3 +51,4 @@ add_cpr_test(patch) add_cpr_test(error) add_cpr_test(alternating) add_cpr_test(util) +add_cpr_test(ssl) diff --git a/test/data/10c14023.0 b/test/data/10c14023.0 new file mode 120000 index 000000000..a74ccf5b0 --- /dev/null +++ b/test/data/10c14023.0 @@ -0,0 +1 @@ +ca.crt \ No newline at end of file diff --git a/test/data/10c14023.1 b/test/data/10c14023.1 new file mode 120000 index 000000000..f9bcc09fc --- /dev/null +++ b/test/data/10c14023.1 @@ -0,0 +1 @@ +cert.pem \ No newline at end of file diff --git a/test/data/88d0bdcb.0 b/test/data/88d0bdcb.0 new file mode 120000 index 000000000..8f18c71de --- /dev/null +++ b/test/data/88d0bdcb.0 @@ -0,0 +1 @@ +server.pem \ No newline at end of file diff --git a/test/data/ca.crt b/test/data/ca.crt new file mode 100644 index 000000000..a573b10d2 --- /dev/null +++ b/test/data/ca.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7DCCAdQCCQC3SbRgZOwEJzANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEXMBUGA1UEAwwOY3ByLmdpdGh1Yi5jb20wHhcN +MTgwNjE0MDkxNjQ5WhcNMTgwNzE0MDkxNjQ5WjA4MQswCQYDVQQGEwJDTjEQMA4G +A1UECAwHQmVpamluZzEXMBUGA1UEAwwOY3ByLmdpdGh1Yi5jb20wggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCot34Vl4D6oMarBXvjqEpR1lrZHT1rGrwd +rnrt4g9D6iPPpecSWf9eqO2Rt0EIJb0eSGFkhjc96hhxghlcwFhwSX3F73EwtTcU +hGW8j/ftjsnM3Xha0pH4mRmjj8og3vLSMfJhNxJcAw6AzLb24n/Nh+IMf6AIn63I ++o7De2W1XHVySBWZBMjNNGACH10cxdQawHqBYUSDVlbU47Hb6yuCUv1scQGxt+8H +/8dw+I06QQkb2ftAZAUIAELfqJhzVuuw0a82BQelTPWkvzThA9fPrNm+NSDT4RqE +sphvlQg/VQUKIPC35tG0A1S6nE6SMtuC+AuepqpYzpsPjQg6RpGxAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBACGnlHapDJp2EvTrE86paP4DTx1CBCqgC1H++sdXjbkw +ABfhP0huR3SHaQkV6eIUKVLCrkzPukcV6MTJScm60Yao2mEMh+FyM7+Kz+1gTUKK +ssFR4jECeVwEReBN5srz+AlUObM5ZoJJHWramdBvoHmZv9BjIzm0ToWeq8gcKdrd +FalOVt0G6e8cZ8e7waW3thJBL+FzGGjGboKyHGrxyix/h1m+ikcUj1VKgg4cuocT +HfzgANtluzvMddsZ9joeNHzzvm4JJSqu2hCmZJHY5UL/SWB/r69dyfU3AwEWvxyW +lMLUSQ52qzlXaRmkjCbAlrhXspurlqEcBXQ17VlNmZE= +-----END CERTIFICATE----- diff --git a/test/data/ca.key b/test/data/ca.key new file mode 100644 index 000000000..6d1427e89 --- /dev/null +++ b/test/data/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAqLd+FZeA+qDGqwV746hKUdZa2R09axq8Ha567eIPQ+ojz6Xn +Eln/XqjtkbdBCCW9HkhhZIY3PeoYcYIZXMBYcEl9xe9xMLU3FIRlvI/37Y7JzN14 +WtKR+JkZo4/KIN7y0jHyYTcSXAMOgMy29uJ/zYfiDH+gCJ+tyPqOw3tltVx1ckgV +mQTIzTRgAh9dHMXUGsB6gWFEg1ZW1OOx2+srglL9bHEBsbfvB//HcPiNOkEJG9n7 +QGQFCABC36iYc1brsNGvNgUHpUz1pL804QPXz6zZvjUg0+EahLKYb5UIP1UFCiDw +t+bRtANUupxOkjLbgvgLnqaqWM6bD40IOkaRsQIDAQABAoIBAQCOWkQH88zfqsf1 +hKsDavtKEZKVnhY0SFpwsMcH77TxmJyel1xgH49YTzAjwnscZLss0eVa6+OwWdRF +8X90wtXb6CY229ClSEExDoTIlLCbv96E/FiQSRrgpkz7tFbXZUV8FKhtXgofmHSk +OBYoOPOcdwpDgWuF2zTGXqIWTgeC/SB8Ti1hFzxBBWBW/0LkxxXaPibHDWq6Y0vH +Nsi1Z0oapzYp4yGfaHx2FJwvuqchzPkRdJwoCZUYW1jgG6y5D81S0fzXjzduKsrZ +MBj5xybYn9BeD6IyFBL/gS8LMYZ8+2UJjjze2jmL9o2s1/loUczk6XLhGtbg7S0R +tN/mfQABAoGBAN9klo1KSoP8mo8G+8XWu6K48KVYk7CgqHkDWMnHtKQcBZ+z3EAt +Sg5n9btxkaF4twaN8KeKavmtoi0S4d4YZHmkUY/1z98oy5aA1Jw2jp7xqID+RhWx +R32HfQFIHNP9fJ66PEAjw5fwMW317yy08utMeNak+HbMwxDZSZNuRgTJAoGBAMFX +2QsNOYpVCalxNwbPeaAK54gZJ2vZbdaCt0b8SX6AcbuRWlUPpERV4tnnS9okjqBW +4pjpWooCvGAw+jle0KNeudPMmKTxYkr5RetvWKBsgTGmAgFZdbzmAToPUZqPd1pv +88VoLceDmOTTnPSHkXhBgSu9w947uuHrvGlpM6GpAoGBANFcB+yqr2M7He7lcJhd +QsSh5oZPmTpzlvCyvkd9LkKPMIYXnldXGoydyaK+MOnPpFg0NlIOW2tb2x27EIuA ++Mk3wmjUytgt9385hghQ6SD1AiLCKxSxbHqsu+6zkrDqZByuFUFXQzvmE60mS9zu +SKtUo/sl6OYNyNLFkTrmoiBhAoGAf0KUcQrOYfghOO91Ob1cn/Qte86avQlLm9NE +UawDsAlZUw2UeMKn6/bgL29n5PkFDBLEacPHPWoZlfMG8LYw/CeAhyF3I7JicVjs +JdCPdGCXFRfg4ASDU8fM7OtiWiquc8+Wk8xDvbwn6g5DlEybep0bKbBpeSC1nLju +ti9xtakCgYAlB3TX34hoKy7GhJ4dMTQsT8hqPhyaVCbpZTjGwXsWzBdLssBLrNlc +B+x3gySTBafGVNXbksjxTHVQCU/rilyZ0UwtnQyBaW7PDs9+qKE1AO3FkORWksgM +axpjFj2fYcdVpTLR/D+poYTPmhkLviOXTJxohQv+p8DTDWBQZz3Hcw== +-----END RSA PRIVATE KEY----- diff --git a/test/data/ca.srl b/test/data/ca.srl new file mode 100644 index 000000000..67615d3b4 --- /dev/null +++ b/test/data/ca.srl @@ -0,0 +1 @@ +FB8B04C98A853780 diff --git a/test/data/cert.csr b/test/data/cert.csr new file mode 100644 index 000000000..9465353a0 --- /dev/null +++ b/test/data/cert.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICfTCCAWUCAQAwODELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxFzAV +BgNVBAMMDmNwci5naXRodWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvoOtmrs3JzeG7v3HXmHXWD+hutixuSEyZfP5d0nbDMjqqWJFFjkd9NYv +BAJm5k5rmVYU+XJZmH1hLT+9pdXqVtqRg5ZmQPMgAjfReMam3HLufh/pOy876D3b +QpPn4UhPgexB55RykZbZ0KMPj8GZdXLGUXockzbZQis3tRp/e066x0n7XmDzqWTA +KrDMAH1O3k/NjIxeuzC0CxyuBs+aNtQjk2sjAiYFwGy/pRByuxqb1mP/oIJvTjX9 +hYcWzWoeaNk7R6m8s+50q9ZqIS3YdXnL7YBoOtaomijOfJ3wSkEMxOsww568eEFG +gbKbTRF3pODFsx20CXITzC/CYVJKSQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB +AKYejT6lFp5LASuQLOH/6VBFdckl8cnUnS8V23UNa3g1Rk+BjRQmAPpQ6m5FIMSG +oKXXJdfu6CG0BQqD+aYZKSnKxJJmjlLgA6BBb4iKBAqWrw1MWZC3EZs6zhQ1a5nP +AOX4sh+BsIVwncSXaLA1dAaab3zLlt94eKlrIUBYLSlO1qvtKiTZFKWaiCHu/gXp +11Yqm2JueQ7mDJyL7eOd/aYxyVDm2h6w1Sj4m+ooM0Fx5Q4edFy1WXK58E5ThP+0 +Qa2Nm7ZC4zxeNT+vPWE0achBzUIQGB6JrZs5+3Su4R5NcZHmpvQ/uBZpZRnTjRU5 +WRb9+NDP08IGN3RC0hXXjoo= +-----END CERTIFICATE REQUEST----- diff --git a/test/data/cert.pem b/test/data/cert.pem new file mode 100644 index 000000000..ecbd9e5fa --- /dev/null +++ b/test/data/cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7DCCAdQCCQD7iwTJioU3gDANBgkqhkiG9w0BAQUFADA4MQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEXMBUGA1UEAwwOY3ByLmdpdGh1Yi5jb20wHhcN +MTgwNjE0MDkxNzQxWhcNMTgwNzE0MDkxNzQxWjA4MQswCQYDVQQGEwJDTjEQMA4G +A1UECAwHQmVpamluZzEXMBUGA1UEAwwOY3ByLmdpdGh1Yi5jb20wggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+g62auzcnN4bu/cdeYddYP6G62LG5ITJl +8/l3SdsMyOqpYkUWOR301i8EAmbmTmuZVhT5clmYfWEtP72l1epW2pGDlmZA8yAC +N9F4xqbccu5+H+k7LzvoPdtCk+fhSE+B7EHnlHKRltnQow+PwZl1csZRehyTNtlC +Kze1Gn97TrrHSfteYPOpZMAqsMwAfU7eT82MjF67MLQLHK4Gz5o21COTayMCJgXA +bL+lEHK7GpvWY/+ggm9ONf2FhxbNah5o2TtHqbyz7nSr1mohLdh1ecvtgGg61qia +KM58nfBKQQzE6zDDnrx4QUaBsptNEXek4MWzHbQJchPML8JhUkpJAgMBAAEwDQYJ +KoZIhvcNAQEFBQADggEBAFZnB4DGtR/99yChyqkVi/KAyiTWx246/ftgm7xDt+kM +CD+20llTzcubBdS467Bi1Z+uuwZ9P8Mpr4bWJ7Bw9gUHd1TTKEKdcSB1zzutORDY +v4UAVFvxFeUUbwfbBWkiIzSYhvRJAXbZEk7oOjFAWbgrAzU1djvmL3bgXOzSFEpR +nAjqe6/+VSP7CIu9h1PdtnXCED6a4zMj0LBxjSL3d8lLuMXfHqZFAq9CwQ1GAQiA +TYF5bgej2HuLjlIjFxtHWvszo3BEZSB8sjGkUvcHx9pFRvzPULIV8+CmMKRMcgkS +Htg2tF5rqsTEexd3i/KeQSNzf87XDzVomQX7hv//y4E= +-----END CERTIFICATE----- diff --git a/test/data/key.pem b/test/data/key.pem new file mode 100644 index 000000000..885ccd4cf --- /dev/null +++ b/test/data/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAvoOtmrs3JzeG7v3HXmHXWD+hutixuSEyZfP5d0nbDMjqqWJF +Fjkd9NYvBAJm5k5rmVYU+XJZmH1hLT+9pdXqVtqRg5ZmQPMgAjfReMam3HLufh/p +Oy876D3bQpPn4UhPgexB55RykZbZ0KMPj8GZdXLGUXockzbZQis3tRp/e066x0n7 +XmDzqWTAKrDMAH1O3k/NjIxeuzC0CxyuBs+aNtQjk2sjAiYFwGy/pRByuxqb1mP/ +oIJvTjX9hYcWzWoeaNk7R6m8s+50q9ZqIS3YdXnL7YBoOtaomijOfJ3wSkEMxOsw +w568eEFGgbKbTRF3pODFsx20CXITzC/CYVJKSQIDAQABAoIBAQC7Au9LUDNXn74o +o50tHMYSroPHsibjH58lTVS4sBsCdcdD9l7y7aZmTElimikAMkKVBlcYdhNVCN53 +WHaFb3LVR0BH9z7wyrfE9Z39/KKXC4ro69I6cqdP1kiBFkcscZr6YjxfYu0D1RyD +HU2NQ/GrxLrACCo8qD6kIWBRum0hT/lvSi94+jI6NfSv30SOUHoovQ5W3qyy8juf +48/ykwMDKQJ5y5gdf6xii0gNCEqW/nVuFJVz0/O5byfgrMjMm7AGO5AURjaiAkkL +fN97zRiSKgdxB5aJQ5rr2FHnqAgnymxl8fdtlhQSE0dqr3qDj4sabkuVl7tHoUqw +q6uqAUzBAoGBAONhHwttRGTPL4lUj/yQaNOEjj2kMF7EB1jK+ZFd3LyVhHJt8yFc +xjzoSr4Y+7TPtLKD3KjiHFQaGYlwJE9zLVkQLDq5a4VV6wGK1opeaRtT7BhNRFYU +3zeOG6oNTlt2HLPMUvGVU/QTffNwJPZRWdQhgl5lwvGNzjha7QkyQzUNAoGBANZ+ +pnYhDFdg5oHEtvUr9xisc0SaQ7IXSYPtUi4xA9fkJgN98g29J9fcixUj6n+fHhsw +hWIC+0EdBPhTgkoTqFad+17RvV55uNk3WxnXQH7NJiLws3wz5tbHDWPZlXWpvl0j +1Tym2EATfb5UC3eK90w+90xezYFFlQt1HoKIWhMtAoGAdOWwjTJe0CkIOmVEJUek +7OGgMyafS4kicc7gYZDM9BM2ZZILRiKpNBUEoBi+ByYlswVBw32DrOVCLbt+n24K +aVh8NLCxC3qxeDiFtdt10/vgTFolANB4NdqUdGek2jat6O3p0pO/9qKyxR3pks0e +DoYvvRI6F++cjrKIXIr/rF0CgYAc48QR5xOddTy2v8MQtJI8bYW8+LYjd4V8Umue +MxXcbys7jywZ8585MMg+2a6M7+aDLP7aG6Kxu3He7IiA57Llr7Oaqs9BuU1PXGhV +y6sxKPp4/EiAcxElXnwMNdMunOU58EiZObOotbda+kDtIfd6+oQeciplvfR8LZMl +a64OJQKBgFhqCyNF2yCjxfPsRnQYvwBORYiQhnKM9kHtN461zT/UviLIswmJmMWl +4FRb9B1lxt+a7/ClIs/FeHS7QkIEsYF0WeRXf9Y4UJiOXstSCSvaRLQtBUD0Vpdr +e2A0H5l2mGOlC/Y1SkpMvo8VE7Ygj68mrmPx3bZFROZhDWy1NpRc +-----END RSA PRIVATE KEY----- diff --git a/test/data/server.pem b/test/data/server.pem new file mode 100644 index 000000000..f191e3c10 --- /dev/null +++ b/test/data/server.pem @@ -0,0 +1,46 @@ +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgIJAPhB8jbL+G82MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMMCTEyNy4wLjAuMTAeFw0xNTAzMDYxMjQzMzNaFw0yNTAzMDMxMjQzMzNaMBQx +EjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALi3b3daMgzUEROKob1Caf68i+//cTRkPdBJv2cOBak21CdQzY0Nvx73GLzf +5TKB347BCHNbYRKGJXDbYdmFp20/WeBHkY7RS3Ad2Q5lzyx66u9PxNx7hJIiqBgF +58VU+E3o/I+o8QNIoOT+wtCiq3Nwkp+zGBJmS32rzMEV9bcKxSzMrkfRhF+XAREd +DwM9vfPg6WRb/b+vv06uvVwcw390RprLautGfBdaRddVYkIAKJGRRTqZAvTRFW1J +FcIVOxlN+iA7qP7xjr3tUP78qMmlu0MXsHrUR2cgfveZK2sdUW5G804yHsU5sC8l +FbtLKMEOyLsk2bEIScOXgum7g2sCAwEAAaNQME4wHQYDVR0OBBYEFHtLzUqAsXkH +Il8S5sMhJuVhRJLdMB8GA1UdIwQYMBaAFHtLzUqAsXkHIl8S5sMhJuVhRJLdMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEzHc0AOr+qs0OFvWMfcSMi7 +O/aYlLS6f7Sos+lli69+61EcmCTJVarVeAVUsAoqmzBKDbeOpAK1hGX6/GGcXjR2 +BmuU0hUKyX9l1lwdMKU45BayH/riElwnvAyj2GxKoPpdIjlHns4SAITOCUx9NfpM +agd7kjolton0ZQ5DI/2a43PkqHv1lY4Dp60wJlxit9U68bsGOycCJ/BsAyrPROb2 +D1MkpMBIdfHc8uxRywM3/l9buFX8yrrMUGOYKgfjDwdzbj0iwIixoGpHL7IfeBtu +dvGO/g2rEhbtAP+xIgOR3GvzqjZh30er3no7zjDMn65tTME18Aq3tBQY7vPDKms= +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4t293WjIM1BET +iqG9Qmn+vIvv/3E0ZD3QSb9nDgWpNtQnUM2NDb8e9xi83+Uygd+OwQhzW2EShiVw +22HZhadtP1ngR5GO0UtwHdkOZc8seurvT8Tce4SSIqgYBefFVPhN6PyPqPEDSKDk +/sLQoqtzcJKfsxgSZkt9q8zBFfW3CsUszK5H0YRflwERHQ8DPb3z4OlkW/2/r79O +rr1cHMN/dEaay2rrRnwXWkXXVWJCACiRkUU6mQL00RVtSRXCFTsZTfogO6j+8Y69 +7VD+/KjJpbtDF7B61EdnIH73mStrHVFuRvNOMh7FObAvJRW7SyjBDsi7JNmxCEnD +l4Lpu4NrAgMBAAECggEAaFuqbAHXOQwuwZ2XFzgIblTTsrncmT7w9VZU/sIbTKif +X771AnX7vmDX5w2PjeN2DE7emV3NEAwd5w7qz1wFZWFfQ6jrgYaZWjRixxGZ5IVl +aeLlU7OtCGrwEPJ1KTWCO3IgDoHh+Hr1+6o7Imhk+QlmrTcfqHWGvO9s9MGVWt2S +RLAnSTFiOe5brdJnmlqq1sKZmnLmpydBaPUOYpZGAgRasrjdMZB+lZOazd1x23/5 +GAcm0rDREMnO9b2Jt+TNEZHT6d5KpVoExztZEZj8QCLXoic/SpFIqHGtpNlQXa+d +BVqgQbIYjO8ldldxZ8YIyJDVF+9e/uBBwu6jBIIsEQKBgQDspEHCyyuh4LG+7BbZ +eXlsfCxPTM6K9w31ZwHAwRtAuGqrOrE+pFJG9CEsFZbAI1aOGmZZdjexuSMcOlXl +TIVJTQHoFtoGEsanYEXO4O1t02Ab/DCYSpXusXUraRBRPpsTC77Sh5mxLUNd23d9 +NhnDBuwChAmC+IYexjkXeqPYFwKBgQDH08PEd+2PVo4MD8UVKUlEcgoyCr6ESiyp +HfYyhhfd5x3DbZLoKCkunDfBs/hakQk8DA2nn4tl4ZjfmzXmX0EBx+E5YTdYshW7 +ZcjN5x64B5PEOAR/NZA6agNlp3XGXXXgX+gnN6pgE49eVU22nZ4G+QBKD6NcCviB +LBPUxMbvzQKBgHgZYRqonGtaqzsXfP1AjmSFnMNeWtDiU95BOf2Gw/sT3WcrsXr2 +UJ+cFR3XkxvOk4YpVdp/igKT0ILqBGAMdvTdtWMB/gLpEpMt5B/7veRoS7XIRy1z +ZSawP6QZfWOOX4vKAT29/j2SmEcRNFKC245EfBFGy8EBuqfxuFX3MyJfAoGBAJ0y +tjsErVmpma1baosvI3g4zlR3p1CimWehLmCopHXorr1iocMIdP0535L+ZU258y3N +vaA0HpFTW9PsYgaMwLMJ7uAY3lVkIzx84e849i2HqHMgLkl0dbW+WFXL2xblxylv +yU2wuNNED/EB4lTawcpycAvTKYvrBXt4lVE4S9exAoGAGl6vZV3zyw4jpIw4uDfk +LTPYUrghFDDGKExyeOnC/W9pqR2veqzfBz02C3jqwhewoqgAcnNc2sg0rJmM+6Oz +Z2mmGZTHO9xR++7+W7e8AkQBbS6TB8a+7yNcM4USLP+b9sX5N+8gFhFs9tG7j/no +G44qLsJ/yve7/QsOA37uEMs= +-----END PRIVATE KEY----- diff --git a/test/error_tests.cpp b/test/error_tests.cpp index 2d5cd12f6..e418f8335 100644 --- a/test/error_tests.cpp +++ b/test/error_tests.cpp @@ -11,8 +11,8 @@ using namespace cpr; static Server* server = new Server(); -auto base = server->GetBaseUrl(); -auto baseSSL = server->GetBaseUrlSSL(); +auto base = server -> GetBaseUrl(); +auto baseSSL = server -> GetBaseUrlSSL(); TEST(ErrorTests, BasicSSLFailure) { auto url = Url{baseSSL + "/hello.html"}; @@ -21,11 +21,10 @@ TEST(ErrorTests, BasicSSLFailure) { EXPECT_EQ(0, response.status_code); auto curl_version = curl_version_info(CURLVERSION_NOW); auto expected = ErrorCode::UNSUPPORTED_PROTOCOL; - if(curl_version->features & CURL_VERSION_SSL) { + if (curl_version->features & CURL_VERSION_SSL) { expected = ErrorCode::SSL_CONNECT_ERROR; } - EXPECT_EQ(expected, response.error.code); - + EXPECT_EQ(expected, response.error.code) << response.error.message; } TEST(ErrorTests, UnsupportedProtocolFailure) { diff --git a/test/server.cpp b/test/server.cpp index 7800273e3..a0a39a6a3 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -17,14 +17,15 @@ std::mutex shutdown_mutex; std::mutex server_mutex; std::condition_variable server_cv; -static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; static inline bool is_base64(unsigned char c); std::string base64_decode(std::string const& encoded_string); -static int lowercase(const char *s); -static int mg_strncasecmp(const char *s1, const char *s2, size_t len); +static int lowercase(const char* s); +static int mg_strncasecmp(const char* s1, const char* s2, size_t len); static int options(struct mg_connection* conn) { if (std::string{conn->request_method} == std::string{"OPTIONS"}) { @@ -33,7 +34,8 @@ static int options(struct mg_connection* conn) { mg_send_header(conn, "content-type", "text/html"); mg_send_header(conn, "Access-Control-Allow-Origin", "*"); mg_send_header(conn, "Access-Control-Allow-Credentials", "true"); - mg_send_header(conn, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + mg_send_header(conn, "Access-Control-Allow-Methods", + "GET, POST, PUT, DELETE, PATCH, OPTIONS"); mg_send_header(conn, "Access-Control-Max-Age", "3600"); mg_send_data(conn, response.data(), response.length()); } else { @@ -97,7 +99,7 @@ static int basicCookies(struct mg_connection* conn) { auto response = std::string{"Hello world!"}; mg_send_status(conn, 200); mg_send_header(conn, "content-type", "text/html"); - time_t t = time(NULL) + 5; // Valid for 1 hour + time_t t = time(NULL) + 5; // Valid for 1 hour char expire[100], expire_epoch[100]; snprintf(expire_epoch, sizeof(expire_epoch), "%lu", static_cast(t)); strftime(expire, sizeof(expire), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t)); @@ -184,7 +186,7 @@ static int basicAuth(struct mg_connection* conn) { static int digestAuth(struct mg_connection* conn) { int result = MG_FALSE; { - FILE *fp; + FILE* fp; if ((fp = fopen("digest.txt", "w")) != NULL) { fprintf(fp, "user:mydomain.com:0cf722ef3dd136b48da83758c5d855f8\n"); fclose(fp); @@ -192,7 +194,7 @@ static int digestAuth(struct mg_connection* conn) { } { - FILE *fp; + FILE* fp; if ((fp = fopen("digest.txt", "r")) != NULL) { result = mg_authorize_digest(conn, fp); fclose(fp); @@ -203,12 +205,13 @@ static int digestAuth(struct mg_connection* conn) { } static int basicJson(struct mg_connection* conn) { - auto response = std::string{"[\n" - " {\n" - " \"first_key\": \"first_value\",\n" - " \"second_key\": \"second_value\"\n" - " }\n" - "]"}; + auto response = std::string{ + "[\n" + " {\n" + " \"first_key\": \"first_value\",\n" + " \"second_key\": \"second_value\"\n" + " }\n" + "]"}; mg_send_status(conn, 200); auto raw_header = mg_get_header(conn, "Content-type"); std::string header; @@ -225,8 +228,7 @@ static int basicJson(struct mg_connection* conn) { } static int headerReflect(struct mg_connection* conn) { - auto response = std::string{"Header reflect "} + - std::string{conn->request_method}; + auto response = std::string{"Header reflect "} + std::string{conn->request_method}; mg_send_status(conn, 200); mg_send_header(conn, "content-type", "text/html"); auto num_headers = conn->num_headers; @@ -285,18 +287,28 @@ static int urlPost(struct mg_connection* conn) { auto x_string = std::string{x}; auto y_string = std::string{y}; if (y_string.empty()) { - auto response = std::string{"{\n" - " \"x\": " + x_string + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } else { std::ostringstream s; s << (atoi(x) + atoi(y)); - auto response = std::string{"{\n" - " \"x\": " + x_string + ",\n" - " \"y\": " + y_string + ",\n" - " \"sum\": " + s.str() + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + ",\n" + " \"y\": " + + y_string + + ",\n" + " \"sum\": " + + s.str() + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } return MG_TRUE; @@ -308,7 +320,7 @@ static int jsonPost(struct mg_connection* conn) { auto has_json_header = false; for (int i = 0; i < num_headers; ++i) { if (std::string{"Content-Type"} == headers[i].name && - std::string{"application/json"} == headers[i].value) { + std::string{"application/json"} == headers[i].value) { has_json_header = true; } } @@ -337,10 +349,9 @@ static int formPost(struct mg_connection* conn) { int data_len; char name[100]; char filename[100]; - auto read_len = mg_parse_multipart(content, content_len, - name, sizeof(name), - filename, sizeof(filename), - const_cast(&data), &data_len); + auto read_len = + mg_parse_multipart(content, content_len, name, sizeof(name), filename, + sizeof(filename), const_cast(&data), &data_len); if (read_len == 0) { delete[] data; break; @@ -360,18 +371,28 @@ static int formPost(struct mg_connection* conn) { mg_send_status(conn, 201); mg_send_header(conn, "content-type", "application/json"); if (forms.find("y") == forms.end()) { - auto response = std::string{"{\n" - " \"x\": " + forms["x"] + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + forms["x"] + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } else { std::ostringstream s; s << (atoi(forms["x"].data()) + atoi(forms["y"].data())); - auto response = std::string{"{\n" - " \"x\": " + forms["x"] + ",\n" - " \"y\": " + forms["y"] + ",\n" - " \"sum\": " + s.str() + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + forms["x"] + + ",\n" + " \"y\": " + + forms["y"] + + ",\n" + " \"sum\": " + + s.str() + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } return MG_TRUE; @@ -383,22 +404,22 @@ static int deleteRequest(struct mg_connection* conn) { auto has_json_header = false; for (int i = 0; i < num_headers; ++i) { if (std::string{"Content-Type"} == headers[i].name && - std::string{"application/json"} == headers[i].value) { + std::string{"application/json"} == headers[i].value) { has_json_header = true; } } if (std::string{conn->request_method} == std::string{"DELETE"}) { - if (!has_json_header) { - auto response = std::string{"Delete success"}; - mg_send_status(conn, 200); - mg_send_header(conn, "content-type", "text/html"); - mg_send_data(conn, response.data(), response.length()); - } else { - auto response = std::string{conn->content, conn->content_len}; - mg_send_status(conn, 200); - mg_send_header(conn, "content-type", "application/json"); - mg_send_data(conn, response.data(), response.length()); - } + if (!has_json_header) { + auto response = std::string{"Delete success"}; + mg_send_status(conn, 200); + mg_send_header(conn, "content-type", "text/html"); + mg_send_data(conn, response.data(), response.length()); + } else { + auto response = std::string{conn->content, conn->content_len}; + mg_send_status(conn, 200); + mg_send_header(conn, "content-type", "application/json"); + mg_send_data(conn, response.data(), response.length()); + } } else { auto response = std::string{"Method unallowed"}; mg_send_status(conn, 405); @@ -434,18 +455,28 @@ static int patch(struct mg_connection* conn) { auto x_string = std::string{x}; auto y_string = std::string{y}; if (y_string.empty()) { - auto response = std::string{"{\n" - " \"x\": " + x_string + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } else { std::ostringstream s; s << (atoi(x) + atoi(y)); - auto response = std::string{"{\n" - " \"x\": " + x_string + ",\n" - " \"y\": " + y_string + ",\n" - " \"sum\": " + s.str() + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + ",\n" + " \"y\": " + + y_string + + ",\n" + " \"sum\": " + + s.str() + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } } else { @@ -483,18 +514,28 @@ static int put(struct mg_connection* conn) { auto x_string = std::string{x}; auto y_string = std::string{y}; if (y_string.empty()) { - auto response = std::string{"{\n" - " \"x\": " + x_string + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } else { std::ostringstream s; s << (atoi(x) + atoi(y)); - auto response = std::string{"{\n" - " \"x\": " + x_string + ",\n" - " \"y\": " + y_string + ",\n" - " \"sum\": " + s.str() + "\n" - "}"}; + auto response = std::string{ + "{\n" + " \"x\": " + + x_string + + ",\n" + " \"y\": " + + y_string + + ",\n" + " \"sum\": " + + s.str() + + "\n" + "}"}; mg_send_data(conn, response.data(), response.length()); } } else { @@ -590,10 +631,10 @@ static int evHandler(struct mg_connection* conn, enum mg_event ev) { } } -void runServer(struct mg_server* server) { +void runServer(struct mg_server* server, const std::string listening_port) { { std::lock_guard server_lock(server_mutex); - mg_set_option(server, "listening_port", SERVER_PORT); + mg_set_option(server, "listening_port", listening_port.c_str()); server_cv.notify_one(); } @@ -611,8 +652,15 @@ void Server::SetUp() { shutdown_mutex.lock(); struct mg_server* server; server = mg_create_server(NULL, evHandler); + std::ostringstream listening_port; + if (cert_file.empty()) { + listening_port << SERVER_PORT; + } else { + listening_port << "ssl://0.0.0.0:" << SERVER_PORT << ":" << cert_file; + } + std::unique_lock server_lock(server_mutex); - std::thread(runServer, server).detach(); + std::thread(runServer, server, listening_port.str()).detach(); server_cv.wait(server_lock); } @@ -642,10 +690,11 @@ std::string base64_decode(std::string const& encoded_string) { unsigned char char_array_4[4], char_array_3[3]; std::string ret; - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) { + while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) { + for (i = 0; i < 4; i++) { char_array_4[i] = base64_chars.find(char_array_4[i]); } @@ -662,11 +711,11 @@ std::string base64_decode(std::string const& encoded_string) { } if (i) { - for (j = i; j <4; j++) { + for (j = i; j < 4; j++) { char_array_4[j] = 0; } - for (j = 0; j <4; j++) { + for (j = 0; j < 4; j++) { char_array_4[j] = base64_chars.find(char_array_4[j]); } @@ -682,11 +731,11 @@ std::string base64_decode(std::string const& encoded_string) { return ret; } -static int lowercase(const char *s) { - return tolower(* reinterpret_cast(s)); +static int lowercase(const char* s) { + return tolower(*reinterpret_cast(s)); } -static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { +static int mg_strncasecmp(const char* s1, const char* s2, size_t len) { int diff = 0; if (len > 0) { @@ -697,4 +746,3 @@ static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { return diff; } - diff --git a/test/server.h b/test/server.h index 16e0bbd79..9ae3afdc8 100644 --- a/test/server.h +++ b/test/server.h @@ -9,13 +9,18 @@ using namespace cpr; -class Server: public ::testing::Environment { +class Server : public ::testing::Environment { public: + Server() {} + Server(const std::string cert_file) : cert_file(cert_file) {} + virtual void SetUp(); virtual void TearDown(); Url GetBaseUrl(); Url GetBaseUrlSSL(); + + std::string cert_file; }; #endif diff --git a/test/ssl_tests.cpp b/test/ssl_tests.cpp new file mode 100644 index 000000000..51913542f --- /dev/null +++ b/test/ssl_tests.cpp @@ -0,0 +1,45 @@ +#include + +#include + +#include +#include + +#include "server.h" + +static Server* server; +auto base_url = server -> GetBaseUrlSSL(); + +using namespace cpr; + +std::string base_dir; + +TEST(SslTests, HelloWorldTest) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + auto url = Url{base_url + "/hello.html"}; + auto sslOpts = Ssl(ssl::TLSv1{}, ssl::ALPN{false}, ssl::NPN{false}, ssl::CaPath{base_dir}, + ssl::CertFile{base_dir + "/cert.pem"}, ssl::KeyFile{base_dir + "/key.pem"}, + ssl::VerifyPeer{false}, ssl::VerifyHost{false}, ssl::VerifyStatus{false}); + auto response = cpr::Get(url, sslOpts, Timeout{5000}, Verbose{}); + auto expected_text = std::string{"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; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (argc > 1) { + base_dir = argv[1]; + } + + server = new Server(base_dir + "/server.pem"); + + ::testing::AddGlobalTestEnvironment(server); + + return RUN_ALL_TESTS(); +}