Skip to content

Commit

Permalink
Add method SetAcceptEncoding for customized Accept-Encoding header (#683
Browse files Browse the repository at this point in the history
)
  • Loading branch information
leviliangtw committed May 28, 2022
1 parent e17dec0 commit 49432fc
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 3 deletions.
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
41 changes: 41 additions & 0 deletions cpr/accept_encoding.cpp
@@ -0,0 +1,41 @@
#include "cpr/accept_encoding.h"

namespace cpr {

void mapMethodsToString(const std::initializer_list<cpr::AcceptEncodingMethods>& from, std::vector<std::string>& to) {
const auto* first = from.begin();
const auto* last = from.end();
auto output = std::back_inserter(to);
while (first != last) {
*output++ = AcceptEncodingMethodsStringMap.at(*first);
first = std::next(first);
}
}

std::string concatenateMethodsWithComma(const std::vector<std::string>& methods) {
auto first = std::next(methods.begin());
auto last = methods.end();
std::string init = methods[0];

for (; first != last; ++first) {
init = std::move(init) + ", " + *first;
}
return init;
}

AcceptEncoding::AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods) {
methods_.clear();
mapMethodsToString(methods, methods_);
}

AcceptEncoding::AcceptEncoding(const std::initializer_list<std::string>& string_methods) : methods_{string_methods} {}

bool AcceptEncoding::empty() const noexcept {
return methods_.empty();
}

const std::string AcceptEncoding::getString() const {
return concatenateMethodsWithComma(methods_);
}

} // namespace cpr
24 changes: 22 additions & 2 deletions cpr/session.cpp
Expand Up @@ -72,6 +72,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 @@ -111,6 +113,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 @@ -588,6 +591,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 @@ -818,8 +829,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 @@ -922,6 +938,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 @@ -965,6 +983,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
2 changes: 1 addition & 1 deletion test/abstractServer.cpp
Expand Up @@ -105,7 +105,7 @@ std::string AbstractServer::Base64Decode(const std::string& in) {
break;
}
// NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers)
val = (val << 6) + T[c];
val = (static_cast<uint>(val) << static_cast<uint>(6)) + T[c];
// NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers)
valb += 6;
if (valb >= 0) {
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

0 comments on commit 49432fc

Please sign in to comment.