Skip to content

Commit

Permalink
Merge pull request #1179 from simue/fix/reset-session-content
Browse files Browse the repository at this point in the history
Add Session::RemoveContent()
  • Loading branch information
COM8 authored Feb 19, 2025
2 parents c121ee0 + 4353d0b commit df19eb5
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 3 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json to be used by other tools (e.g. vs code)
else()
# Check required c++ standard of parent project
if(CMAKE_CXX_STANDARD)
Expand Down
22 changes: 21 additions & 1 deletion cpr/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,26 @@ void Session::SetLimitRate(const LimitRate& limit_rate) {
curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate);
}

const Content& Session::GetContent() const {
return content_;
}

void Session::RemoveContent() {
// inverse function to prepareBodyPayloadOrMultipart()
if (std::holds_alternative<cpr::Payload>(content_) || std::holds_alternative<cpr::Body>(content_)) {
// set default values, so curl does not send a body in subsequent requests
curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, -1);
curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, nullptr);
} else if (std::holds_alternative<cpr::Multipart>(content_)) {
if (curl_->multipart) {
// remove multipart data
curl_mime_free(curl_->multipart);
curl_->multipart = nullptr;
}
}
content_ = std::monostate{};
}

void Session::SetReadCallback(const ReadCallback& read) {
cbs_->readcb_ = read;
curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size);
Expand Down Expand Up @@ -928,7 +948,7 @@ const std::optional<Response> Session::intercept() {
}

void Session::prepareBodyPayloadOrMultipart() const {
// Either a body, multipart or a payload is allowed.
// Either a body, multipart or a payload is allowed. Inverse function to RemoveContent()

if (std::holds_alternative<cpr::Payload>(content_)) {
const std::string payload = std::get<cpr::Payload>(content_).GetContent(*curl_);
Expand Down
14 changes: 13 additions & 1 deletion include/cpr/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
namespace cpr {

using AsyncResponse = AsyncWrapper<Response>;
using Content = std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::Multipart>;

class Interceptor;
class MultiPerform;
Expand Down Expand Up @@ -112,6 +113,17 @@ class Session : public std::enable_shared_from_this<Session> {
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);
void SetLimitRate(const LimitRate& limit_rate);

/**
* Returns a reference to the content sent in previous request.
**/
[[nodiscard]] const Content& GetContent() const;

/**
* Removes the content sent in previous request from internal state, so it will not be sent with the next request.
* Call this before doing a request that is specified not to send a body, e.g. GET.
**/
void RemoveContent();

// For cancellable requests
void SetCancellationParam(std::shared_ptr<std::atomic_bool> param);

Expand Down Expand Up @@ -237,7 +249,7 @@ class Session : public std::enable_shared_from_this<Session> {


bool chunkedTransferEncoding_{false};
std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::Multipart> content_{std::monostate{}};
Content content_{std::monostate{}};
std::shared_ptr<CurlHolder> curl_;
Url url_;
Parameters parameters_;
Expand Down
13 changes: 13 additions & 0 deletions test/httpServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ void HttpServer::OnRequestBasicJson(mg_connection* conn, mg_http_message* /*msg*
}

void HttpServer::OnRequestHeaderReflect(mg_connection* conn, mg_http_message* msg) {
if (std::string_view{msg->method.ptr, msg->method.len} == "GET") {
if (msg->body.len > 0) {
std::string errorMessage{"Bad Request: GET shouldn't contain a body."};
SendError(conn, 400, errorMessage);
return;
} else if (msg->chunk.len > 0) {
std::string errorMessage{"Bad Request: GET shouldn't contain a body."};
SendError(conn, 400, errorMessage);
return;
}
}

std::string response = "Header reflect " + std::string{msg->method.ptr, msg->method.len};
std::string headers;
bool hasContentTypeHeader = false;
Expand Down Expand Up @@ -852,6 +864,7 @@ void HttpServer::OnRequestGetDownloadFileLength(mg_connection* conn, mg_http_mes

void HttpServer::OnRequest(mg_connection* conn, mg_http_message* msg) {
std::string uri = std::string(msg->uri.ptr, msg->uri.len);

if (uri == "/") {
OnRequestRoot(conn, msg);
} else if (uri == "/hello.html") {
Expand Down
3 changes: 3 additions & 0 deletions test/prepare_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ TEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) {
Session session;
for (size_t i = 0; i < 3; ++i) {
{
session.RemoveContent();
session.SetUrl(url);
session.PrepareDelete();
CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
Expand All @@ -91,6 +92,7 @@ TEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(url);
session.PrepareGet();
CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
Expand Down Expand Up @@ -118,6 +120,7 @@ TEST(PrepareTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(url);
session.PrepareHead();
CURLcode curl_result = curl_easy_perform(session.GetCurlHolder()->handle);
Expand Down
29 changes: 28 additions & 1 deletion test/session_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <gtest/gtest.h>

#include <chrono>
#include <stdexcept>
#include <string>

Expand Down Expand Up @@ -1005,9 +1005,34 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
Url url{server->GetBaseUrl() + "/header_reflect.html"};
Url urlPost{server->GetBaseUrl() + "/post_reflect.html"};
Url urlPut{server->GetBaseUrl() + "/put.html"};
Url urlMultipartPost{server->GetBaseUrl() + "/post_file_upload.html"};
Session session;
for (size_t i = 0; i < 10; ++i) {
{
session.RemoveContent();
session.SetUrl(url);
Response response = session.Get();
std::string expected_text{"Header reflect GET"};
EXPECT_EQ(expected_text, response.text);
EXPECT_EQ(url, response.url);
EXPECT_EQ(200, response.status_code);
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(urlMultipartPost);
std::string fileContentsBinary{"this is a binary payload"};
std::string fileExtension = ".myfile";
session.SetMultipart(cpr::Multipart{{"files", cpr::Buffer{fileContentsBinary.begin(), fileContentsBinary.end(), "myfile.jpg"}}, {"file_types", "[\"" + fileExtension + "\"]"}});
Response response = session.Post();
std::string expected_text{"{\n \"files\": \"myfile.jpg=this is a binary payload\",\n \"file_types\": \"[\".myfile\"]\"\n}"};
EXPECT_EQ(expected_text, response.text);
EXPECT_EQ(urlMultipartPost, response.url);
EXPECT_EQ(201, response.status_code);
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(url);
Response response = session.Delete();
std::string expected_text{"Header reflect DELETE"};
Expand All @@ -1027,6 +1052,7 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(url);
Response response = session.Get();
std::string expected_text{"Header reflect GET"};
Expand All @@ -1050,6 +1076,7 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
session.SetUrl(url);
Response response = session.Head();
std::string expected_text{"Header reflect HEAD"};
Expand Down

0 comments on commit df19eb5

Please sign in to comment.