diff --git a/cpr/CMakeLists.txt b/cpr/CMakeLists.txt index 66fc9ebdb..3b1c4dbca 100644 --- a/cpr/CMakeLists.txt +++ b/cpr/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.15) add_library(cpr + accept_encoding.cpp auth.cpp bearer.cpp cookies.cpp diff --git a/cpr/accept_encoding.cpp b/cpr/accept_encoding.cpp new file mode 100644 index 000000000..be90d463f --- /dev/null +++ b/cpr/accept_encoding.cpp @@ -0,0 +1,23 @@ +#include "cpr/accept_encoding.h" + +namespace cpr { + +AcceptEncoding::AcceptEncoding(const std::initializer_list& methods) : methods_{methods} {} + +bool AcceptEncoding::isEmpty() const { + return methods_.empty(); +} + +const std::string& AcceptEncoding::getString() const { + std::string* accept_encoding = new std::string(); + for (AcceptEncodingMethods method : methods_) { + *accept_encoding += AcceptEncoding::MethodsString.at(method) + ", "; + } + if (!accept_encoding->empty()) { + accept_encoding->pop_back(); + accept_encoding->pop_back(); + } + return *accept_encoding; +} + +} // namespace cpr diff --git a/cpr/session.cpp b/cpr/session.cpp index 876fd8609..b044e6700 100644 --- a/cpr/session.cpp +++ b/cpr/session.cpp @@ -68,6 +68,7 @@ class Session::Impl { void SetRange(const Range& range); void SetMultiRange(const MultiRange& multi_range); void SetReserveSize(const ReserveSize& reserve_size); + void SetAcceptEncoding(const AcceptEncoding& accept_encoding); cpr_off_t GetDownloadFileLength(); void ResponseStringReserve(size_t size); @@ -103,6 +104,7 @@ class Session::Impl { 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_. @@ -570,6 +572,10 @@ void Session::Impl::SetReserveSize(const ReserveSize& reserve_size) { ResponseStringReserve(reserve_size.size); } +void Session::Impl::SetAcceptEncoding(const AcceptEncoding& accept_encoding) { + acceptEncoding_ = accept_encoding; +} + void Session::Impl::PrepareDelete() { curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L); curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); @@ -792,8 +798,11 @@ void Session::Impl::prepareCommon() { #if LIBCURL_VERSION_MAJOR >= 7 #if LIBCURL_VERSION_MINOR >= 21 - /* enable all supported built-in compressions */ - curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, ""); + if (acceptEncoding_.isEmpty()) + /* 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 @@ -883,6 +892,7 @@ void Session::SetHttpVersion(const HttpVersion& version) { pimpl_->SetHttpVersio void Session::SetRange(const Range& range) { pimpl_->SetRange(range); } void Session::SetMultiRange(const MultiRange& multi_range) { pimpl_->SetMultiRange(multi_range); } void Session::SetReserveSize(const ReserveSize& reserve_size) { pimpl_->SetReserveSize(reserve_size); } +void Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) { pimpl_->SetAcceptEncoding(accept_encoding); } void Session::SetOption(const ReadCallback& read) { pimpl_->SetReadCallback(read); } void Session::SetOption(const HeaderCallback& header) { pimpl_->SetHeaderCallback(header); } void Session::SetOption(const WriteCallback& write) { pimpl_->SetWriteCallback(write); } @@ -924,6 +934,7 @@ void Session::SetOption(const HttpVersion& version) { pimpl_->SetHttpVersion(ver void Session::SetOption(const Range& range) { pimpl_->SetRange(range); } void Session::SetOption(const MultiRange& multi_range) { pimpl_->SetMultiRange(multi_range); } void Session::SetOption(const ReserveSize& reserve_size) { pimpl_->SetReserveSize(reserve_size.size); } +void Session::SetOption(const AcceptEncoding& accept_encoding) { pimpl_->SetAcceptEncoding(accept_encoding); } cpr_off_t Session::GetDownloadFileLength() { return pimpl_->GetDownloadFileLength(); } void Session::ResponseStringReserve(size_t size) { pimpl_->ResponseStringReserve(size); } diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 6b4d62d33..1abb6ba63 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -7,6 +7,7 @@ target_include_directories(cpr PUBLIC target_sources(cpr PRIVATE # Header files (useful in IDEs) + cpr/accept_encoding.h cpr/api.h cpr/auth.h cpr/bearer.h diff --git a/include/cpr/accept_encoding.h b/include/cpr/accept_encoding.h new file mode 100644 index 000000000..9570d149a --- /dev/null +++ b/include/cpr/accept_encoding.h @@ -0,0 +1,40 @@ +#ifndef CPR_ACCEPT_ENCODING_H +#define CPR_ACCEPT_ENCODING_H + +#include +#include +#include +#include +#include + +namespace cpr { + +enum class AcceptEncodingMethods { + identity, + deflate, + zlib, + gzip, +}; + +class AcceptEncoding { + public: + std::map MethodsString { + {AcceptEncodingMethods::identity, "identity"}, + {AcceptEncodingMethods::deflate, "deflate"}, + {AcceptEncodingMethods::zlib, "zlib"}, + {AcceptEncodingMethods::gzip, "gzip"} + }; + + AcceptEncoding() = default; + AcceptEncoding(const std::initializer_list& methods); + + bool isEmpty() const; + const std::string& getString() const; + + private: + std::vector methods_; +}; + +} // namespace cpr + +#endif diff --git a/include/cpr/session.h b/include/cpr/session.h index c48bc8b17..f2aa0767e 100644 --- a/include/cpr/session.h +++ b/include/cpr/session.h @@ -13,6 +13,7 @@ #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" +#include "cpr/accept_encoding.h" #include "cpr/http_version.h" #include "cpr/interface.h" #include "cpr/limit_rate.h" @@ -81,6 +82,7 @@ class Session { void SetRange(const Range& range); void SetMultiRange(const MultiRange& multi_range); void SetReserveSize(const ReserveSize& reserve_size); + void SetAcceptEncoding(const AcceptEncoding& accept_encoding); // Used in templated functions void SetOption(const Url& url); @@ -124,6 +126,7 @@ class Session { void SetOption(const Range& range); void SetOption(const MultiRange& multi_range); void SetOption(const ReserveSize& reserve_size); + void SetOption(const AcceptEncoding& accept_encoding); cpr_off_t GetDownloadFileLength(); /** diff --git a/test/httpServer.cpp b/test/httpServer.cpp index f7c7138a5..b6894d828 100644 --- a/test/httpServer.cpp +++ b/test/httpServer.cpp @@ -756,6 +756,22 @@ void HttpServer::OnRequestDownloadGzip(mg_connection* conn, http_message* msg) { } } +void HttpServer::OnRequestCheckAcceptEncoding(mg_connection* conn, http_message* msg) { + std::string response; + for (size_t i = 0; i < sizeof(msg->header_names) / sizeof(mg_str); i++) { + if (!msg->header_names[i].p) { + continue; + } + std::string name = std::string(msg->header_names[i].p, msg->header_names[i].len); + if (std::string{"Accept-Encoding"} == name) { + response = std::string(msg->header_values[i].p, msg->header_values[i].len); + } + } + std::string headers = "Content-Type: text/html"; + mg_send_head(conn, 200, response.length(), headers.c_str()); + mg_send(conn, response.c_str(), response.length()); +} + void HttpServer::OnRequest(mg_connection* conn, http_message* msg) { std::string uri = std::string(msg->uri.p, msg->uri.len); if (uri == "/") { @@ -820,6 +836,8 @@ void HttpServer::OnRequest(mg_connection* conn, http_message* msg) { OnRequestPatchNotAllowed(conn, msg); } else if (uri == "/download_gzip.html") { OnRequestDownloadGzip(conn, msg); + } else if (uri == "/check_accept_encoding.html") { + OnRequestCheckAcceptEncoding(conn, msg); } else { OnRequestNotFound(conn, msg); } diff --git a/test/httpServer.hpp b/test/httpServer.hpp index 17792442b..2fed5bf22 100644 --- a/test/httpServer.hpp +++ b/test/httpServer.hpp @@ -51,6 +51,7 @@ class HttpServer : public AbstractServer { static void OnRequestPatch(mg_connection* conn, http_message* msg); static void OnRequestPatchNotAllowed(mg_connection* conn, http_message* msg); static void OnRequestDownloadGzip(mg_connection* conn, http_message* msg); + static void OnRequestCheckAcceptEncoding(mg_connection* conn, http_message* msg); protected: mg_connection* initServer(mg_mgr* mgr, MG_CB(mg_event_handler_t event_handler, void* user_data)) override; diff --git a/test/session_tests.cpp b/test/session_tests.cpp index e42eeea71..ac089b0b4 100644 --- a/test/session_tests.cpp +++ b/test/session_tests.cpp @@ -940,6 +940,20 @@ TEST(BasicTests, ReserveResponseString) { EXPECT_EQ(ErrorCode::OK, response.error.code); } +TEST(BasicTests, AcceptEncodingTest) { + 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); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(server);