Skip to content

Commit 0292b5a

Browse files
authored
Merge pull request #1214 from T-Maxxx/1207-body-view
Implemented cpr::BodyView.
2 parents 21e6599 + ae53922 commit 0292b5a

File tree

10 files changed

+123
-17
lines changed

10 files changed

+123
-17
lines changed

cmake/clang-tidy.cmake

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
# Include this file if and only if you want to use clang-tidy linter.
2+
13
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
2-
find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy)
4+
if (NOT ${CPR_LINTER_PATH} STREQUAL "")
5+
get_filename_component(CLANG_TIDY_HINT_FILENAME ${CPR_LINTER_PATH} NAME)
6+
get_filename_component(CLANG_TIDY_HINT_PATH ${CPR_LINTER_PATH} DIRECTORY)
7+
endif ()
8+
9+
find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy ${CLANG_TIDY_HINT_FILENAME} HINTS ${CLANG_TIDY_HINT_PATH} REQUIRED)
310
mark_as_advanced(CLANG_TIDY_EXECUTABLE)
411

5-
if (${CLANG_TIDY_EXECUTABLE})
6-
message(FATAL_ERROR "Clang-tidy not found")
7-
else()
8-
message(STATUS "Enabling clang-tidy")
9-
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*")
10-
endif()
11-
else()
12+
message(STATUS "Enabling clang-tidy: ${CLANG_TIDY_EXECUTABLE}")
13+
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*")
14+
else ()
1215
message(FATAL_ERROR "Clang-tidy is not supported when building for windows")
13-
endif()
16+
endif ()

cmake/cppcheck.cmake

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck)
1+
# Include this file if and only if you want to use cppcheck.
22

3-
if(CMAKE_CXX_CPPCHECK)
4-
list(APPEND CMAKE_CXX_CPPCHECK "--xml"
3+
if (NOT ${CPR_CPPCHECK_PATH} STREQUAL "")
4+
get_filename_component(CPPCHECK_HINT_FILENAME ${CPR_CPPCHECK_PATH} NAME)
5+
get_filename_component(CPPCHECK_HINT_PATH ${CPR_CPPCHECK_PATH} DIRECTORY)
6+
endif ()
7+
8+
find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck ${CPPCHECK_HINT_FILENAME} HINTS ${CPPCHECK_HINT_PATH} REQUIRED)
9+
message(STATUS "Found cppcheck: ${CMAKE_CXX_CPPCHECK}")
10+
11+
list(APPEND CMAKE_CXX_CPPCHECK
12+
"--xml"
513
"--error-exitcode=1"
614
"--enable=warning,style"
715
"--force"
@@ -11,5 +19,5 @@ if(CMAKE_CXX_CPPCHECK)
1119
"--cppcheck-build-dir=${PROJECT_BINARY_DIR}"
1220
"--suppress-xml=${PROJECT_SOURCE_DIR}/cppcheck-suppressions.xml"
1321
"--output-file=${PROJECT_BINARY_DIR}/cppcheck.xml"
14-
"--check-level=normal")
15-
endif()
22+
"--check-level=normal"
23+
)

cppcheck-suppressions.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
<id>y2038-unsafe-call</id>
2525
<fileName>*/include/cpr/low_speed.h</fileName>
2626
</suppress>
27+
<!-- No reason to check third party includes -->
28+
<suppress>
29+
<id>y2038-unsafe-call</id>
30+
<fileName>*/curl/curl.h</fileName>
31+
</suppress>
2732
<suppress>
2833
<id>normalCheckLevelMaxBranches</id>
2934
</suppress>

cpr/session.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <optional>
1313
#include <stdexcept>
1414
#include <string>
15+
#include <string_view>
16+
#include <type_traits>
1517
#include <utility>
1618
#include <variant>
1719
#include <vector>
@@ -26,6 +28,7 @@
2628
#include "cpr/auth.h"
2729
#include "cpr/bearer.h"
2830
#include "cpr/body.h"
31+
#include "cpr/body_view.h"
2932
#include "cpr/callback.h"
3033
#include "cpr/connect_timeout.h"
3134
#include "cpr/cookies.h"
@@ -289,6 +292,10 @@ void Session::RemoveContent() {
289292
curl_mime_free(curl_->multipart);
290293
curl_->multipart = nullptr;
291294
}
295+
} else if (std::holds_alternative<cpr::BodyView>(content_)) {
296+
// set default values, so curl does not send a body in subsequent requests
297+
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, -1);
298+
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr);
292299
}
293300
content_ = std::monostate{};
294301
}
@@ -488,6 +495,12 @@ void Session::SetBody(Body&& body) {
488495
content_ = std::move(body);
489496
}
490497

498+
// cppcheck-suppress passedByValue
499+
void Session::SetBodyView(BodyView body) {
500+
static_assert(std::is_trivially_copyable_v<BodyView>, "BodyView expected to be trivially copyable otherwise will need some std::move across codebase");
501+
content_ = body;
502+
}
503+
491504
void Session::SetLowSpeed(const LowSpeed& low_speed) {
492505
curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, static_cast<long>(low_speed.limit));
493506
curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, static_cast<long>(low_speed.time.count())); // cppcheck-suppress y2038-unsafe-call
@@ -971,6 +984,11 @@ void Session::prepareBodyPayloadOrMultipart() const {
971984
const std::string& body = std::get<cpr::Body>(content_).str();
972985
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.length()));
973986
curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str());
987+
} else if (std::holds_alternative<cpr::BodyView>(content_)) {
988+
const std::string_view body = std::get<cpr::BodyView>(content_).str();
989+
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.length()));
990+
// NOLINTNEXTLINE (bugprone-suspicious-stringview-data-usage)
991+
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, body.data());
974992
} else if (std::holds_alternative<cpr::Multipart>(content_)) {
975993
// Make sure, we have a empty multipart to start with:
976994
if (curl_->multipart) {
@@ -1020,7 +1038,7 @@ void Session::prepareBodyPayloadOrMultipart() const {
10201038
}
10211039

10221040
[[nodiscard]] bool Session::hasBodyOrPayload() const {
1023-
return std::holds_alternative<cpr::Body>(content_) || std::holds_alternative<cpr::Payload>(content_);
1041+
return std::holds_alternative<cpr::Body>(content_) || std::holds_alternative<cpr::BodyView>(content_) || std::holds_alternative<cpr::Payload>(content_);
10241042
}
10251043

10261044
// clang-format off
@@ -1057,6 +1075,8 @@ void Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); }
10571075
void Session::SetOption(const Cookies& cookies) { SetCookies(cookies); }
10581076
void Session::SetOption(const Body& body) { SetBody(body); }
10591077
void Session::SetOption(Body&& body) { SetBody(std::move(body)); }
1078+
// cppcheck-suppress passedByValue
1079+
void Session::SetOption(BodyView body) { SetBodyView(body); }
10601080
void Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); }
10611081
void Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); }
10621082
void Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); }

include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ target_sources(cpr PRIVATE
1414
cpr/auth.h
1515
cpr/bearer.h
1616
cpr/body.h
17+
cpr/body_view.h
1718
cpr/buffer.h
1819
cpr/cert_info.h
1920
cpr/cookies.h

include/cpr/body.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include <fstream>
66
#include <initializer_list>
77
#include <string>
8-
#include <vector>
98

109
#include "cpr/buffer.h"
1110
#include "cpr/cprtypes.h"

include/cpr/body_view.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef CPR_BODY_VIEW_H
2+
#define CPR_BODY_VIEW_H
3+
4+
#include <string_view>
5+
6+
#include "cpr/buffer.h"
7+
8+
namespace cpr {
9+
10+
class BodyView final {
11+
public:
12+
BodyView() = default;
13+
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
14+
BodyView(std::string_view body) : m_body(body) {}
15+
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
16+
BodyView(const char* body) : m_body(body) {}
17+
BodyView(const char* str, size_t len) : m_body(str, len) {}
18+
// NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-pro-type-reinterpret-cast)
19+
BodyView(const Buffer& buffer) : m_body(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}
20+
21+
BodyView(const BodyView& other) = default;
22+
BodyView(BodyView&& old) noexcept = default;
23+
~BodyView() = default;
24+
25+
BodyView& operator=(BodyView&& old) noexcept = default;
26+
BodyView& operator=(const BodyView& other) = default;
27+
28+
[[nodiscard]] std::string_view str() const { return m_body; }
29+
30+
private:
31+
std::string_view m_body;
32+
};
33+
34+
} // namespace cpr
35+
36+
#endif

include/cpr/session.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "cpr/auth.h"
1616
#include "cpr/bearer.h"
1717
#include "cpr/body.h"
18+
#include "cpr/body_view.h"
1819
#include "cpr/callback.h"
1920
#include "cpr/connect_timeout.h"
2021
#include "cpr/cookies.h"
@@ -46,7 +47,7 @@
4647
namespace cpr {
4748

4849
using AsyncResponse = AsyncWrapper<Response>;
49-
using Content = std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::Multipart>;
50+
using Content = std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::BodyView, cpr::Multipart>;
5051

5152
class Interceptor;
5253
class MultiPerform;
@@ -90,6 +91,7 @@ class Session : public std::enable_shared_from_this<Session> {
9091
void SetCookies(const Cookies& cookies);
9192
void SetBody(Body&& body);
9293
void SetBody(const Body& body);
94+
void SetBodyView(BodyView body);
9395
void SetLowSpeed(const LowSpeed& low_speed);
9496
void SetVerifySsl(const VerifySsl& verify);
9597
void SetUnixSocket(const UnixSocket& unix_socket);
@@ -154,6 +156,7 @@ class Session : public std::enable_shared_from_this<Session> {
154156
void SetOption(const Cookies& cookies);
155157
void SetOption(Body&& body);
156158
void SetOption(const Body& body);
159+
void SetOption(BodyView body);
157160
void SetOption(const ReadCallback& read);
158161
void SetOption(const HeaderCallback& header);
159162
void SetOption(const WriteCallback& write);

test/raw_body_tests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,20 @@ TEST(BodyPostTests, StringMoveBodyTest) {
128128
EXPECT_EQ(ErrorCode::OK, response.error.code);
129129
}
130130

131+
TEST(BodyPostTests, BodyViewTest) {
132+
const Url url{server->GetBaseUrl() + "/url_post.html"};
133+
Response response = cpr::Post(url, BodyView{"x=5"});
134+
const std::string expected_text{
135+
"{\n"
136+
" \"x\": 5\n"
137+
"}"};
138+
EXPECT_EQ(expected_text, response.text);
139+
EXPECT_EQ(url, response.url);
140+
EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
141+
EXPECT_EQ(201, response.status_code);
142+
EXPECT_EQ(ErrorCode::OK, response.error.code);
143+
}
144+
131145
int main(int argc, char** argv) {
132146
::testing::InitGoogleTest(&argc, argv);
133147
::testing::AddGlobalTestEnvironment(server);

test/session_tests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,23 @@ TEST(BodyTests, SetBodyValueTest) {
724724
EXPECT_EQ(ErrorCode::OK, response.error.code);
725725
}
726726

727+
TEST(BodyTests, SetBodyViewTest) {
728+
const Url url{server->GetBaseUrl() + "/url_post.html"};
729+
Session session;
730+
session.SetUrl(url);
731+
session.SetBodyView(BodyView{"x=5"});
732+
Response response = session.Post();
733+
const std::string expected_text{
734+
"{\n"
735+
" \"x\": 5\n"
736+
"}"};
737+
EXPECT_EQ(expected_text, response.text);
738+
EXPECT_EQ(url, response.url);
739+
EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]);
740+
EXPECT_EQ(201, response.status_code);
741+
EXPECT_EQ(ErrorCode::OK, response.error.code);
742+
}
743+
727744
TEST(DigestTests, SetDigestTest) {
728745
Url url{server->GetBaseUrl() + "/digest_auth.html"};
729746
Session session;

0 commit comments

Comments
 (0)