Skip to content
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
2 changes: 1 addition & 1 deletion cpr/curlholder.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include "cpr/curlholder.h"
#include "cpr/secure_string.h"
#include <cassert>
#include <curl/curl.h>
#include <curl/easy.h>
#include <string_view>
#include "cpr/secure_string.h"

namespace cpr {
CurlHolder::CurlHolder() {
Expand Down
18 changes: 18 additions & 0 deletions cpr/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,24 @@ 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() {
if (std::holds_alternative<cpr::Payload>(content_) || std::holds_alternative<cpr::Body>(content_)) {
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_)) {
// Make sure, we have a empty multipart to start with:
if (curl_->multipart) {
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
5 changes: 4 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 @@ -111,6 +112,8 @@ class Session : public std::enable_shared_from_this<Session> {
void SetAcceptEncoding(const AcceptEncoding& accept_encoding);
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);
void SetLimitRate(const LimitRate& limit_rate);
[[nodiscard]] const Content& GetContent() const;
void RemoveContent();

// For cancellable requests
void SetCancellationParam(std::shared_ptr<std::atomic_bool> param);
Expand Down Expand Up @@ -237,7 +240,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
55 changes: 45 additions & 10 deletions 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 @@ -1001,13 +1001,52 @@ TEST(DifferentMethodTests, MultipleGetPostTest) {
}
}

void PrintContentType(const std::variant<std::monostate, cpr::Payload, cpr::Body, cpr::Multipart>& content) {
if (std::holds_alternative<std::monostate>(content)) {
std::cout << "std::monostate" << std::endl;
} else if (std::holds_alternative<cpr::Payload>(content)) {
std::cout << "cpr::Payload" << std::endl;
} else if (std::holds_alternative<cpr::Body>(content)) {
std::cout << "cpr::Body" << std::endl;
} else if (std::holds_alternative<cpr::Multipart>(content)) {
std::cout << "cpr::Multipart" << std::endl;
} else {
std::cout << "unknown content type" << std::endl;
}
}

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{"kn3d0c9j213riknmfd039dj10idn2309cmxkle2rsdj0239mdf"};
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=kn3d0c9j213riknmfd039dj10idn2309cmxkle2rsdj0239mdf\",\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 @@ -1017,6 +1056,7 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
PrintContentType(session.GetContent());
session.SetUrl(urlPost);
std::string expectedBody = "a1b2c3Post";
session.SetBody(expectedBody);
Expand All @@ -1026,16 +1066,9 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(200, response.status_code);
EXPECT_EQ(ErrorCode::OK, response.error.code);
}

{
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);
}
{
PrintContentType(session.GetContent());
session.SetUrl(urlPut);
session.SetPayload({{"x", "5"}});
Response response = session.Put();
Expand All @@ -1050,6 +1083,8 @@ TEST(DifferentMethodTests, MultipleDeleteHeadPutGetPostTest) {
EXPECT_EQ(ErrorCode::OK, response.error.code);
}
{
session.RemoveContent();
PrintContentType(session.GetContent());
session.SetUrl(url);
Response response = session.Head();
std::string expected_text{"Header reflect HEAD"};
Expand Down
Loading