Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method SetAcceptEncoding for customized Accept-Encoding header (#683) #746

Merged
merged 1 commit into from Jun 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions cpr/CMakeLists.txt
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.15)

add_library(cpr
accept_encoding.cpp
async.cpp
auth.cpp
bearer.cpp
Expand Down
25 changes: 25 additions & 0 deletions 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
24 changes: 22 additions & 2 deletions cpr/session.cpp
Expand Up @@ -76,6 +76,8 @@ class Session::Impl {
void SetRange(const Range& range);
void SetMultiRange(const MultiRange& multi_range);
void SetReserveSize(const ReserveSize& reserve_size);
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);
void SetAcceptEncoding(const AcceptEncoding& accept_encoding);

cpr_off_t GetDownloadFileLength();
void ResponseStringReserve(size_t size);
Expand Down Expand Up @@ -115,6 +117,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_.
Expand Down Expand Up @@ -600,6 +603,14 @@ 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::SetAcceptEncoding(AcceptEncoding&& accept_encoding) {
acceptEncoding_ = std::move(accept_encoding);
}

void Session::Impl::PrepareDelete() {
curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L);
curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L);
Expand Down Expand Up @@ -830,8 +841,13 @@ 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_.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

Expand Down Expand Up @@ -934,6 +950,8 @@ 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::SetAcceptEncoding(AcceptEncoding&& accept_encoding) { pimpl_->SetAcceptEncoding(std::move(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); }
Expand Down Expand Up @@ -977,6 +995,8 @@ 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); }
void Session::SetOption(AcceptEncoding&& accept_encoding) { pimpl_->SetAcceptEncoding(std::move(accept_encoding)); }

cpr_off_t Session::GetDownloadFileLength() { return pimpl_->GetDownloadFileLength(); }
void Session::ResponseStringReserve(size_t size) { pimpl_->ResponseStringReserve(size); }
Expand Down
1 change: 1 addition & 0 deletions include/CMakeLists.txt
Expand Up @@ -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/async.h
cpr/auth.h
Expand Down
36 changes: 36 additions & 0 deletions 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
5 changes: 5 additions & 0 deletions include/cpr/session.h
Expand Up @@ -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"
Expand Down Expand Up @@ -87,6 +88,8 @@ 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);
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);

// Used in templated functions
void SetOption(const Url& url);
Expand Down Expand Up @@ -132,6 +135,8 @@ 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);
void SetOption(AcceptEncoding&& accept_encoding);

cpr_off_t GetDownloadFileLength();
/**
Expand Down
18 changes: 18 additions & 0 deletions test/httpServer.cpp
Expand Up @@ -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 == "/") {
Expand Down Expand Up @@ -822,6 +838,8 @@ void HttpServer::OnRequest(mg_connection* conn, http_message* msg) {
OnRequestDownloadGzip(conn, msg);
} else if (uri == "/local_port.html") {
OnRequestLocalPort(conn, msg);
} else if (uri == "/check_accept_encoding.html") {
OnRequestCheckAcceptEncoding(conn, msg);
} else {
OnRequestNotFound(conn, msg);
}
Expand Down
1 change: 1 addition & 0 deletions test/httpServer.hpp
Expand Up @@ -52,6 +52,7 @@ class HttpServer : public AbstractServer {
static void OnRequestPatchNotAllowed(mg_connection* conn, http_message* msg);
static void OnRequestDownloadGzip(mg_connection* conn, http_message* msg);
static void OnRequestLocalPort(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;
Expand Down
58 changes: 58 additions & 0 deletions test/session_tests.cpp
Expand Up @@ -1004,6 +1004,64 @@ TEST(BasicTests, ReserveResponseString) {
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);
}

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(server);
Expand Down