diff --git a/httplib.h b/httplib.h index 33eb573..bf02a80 100644 --- a/httplib.h +++ b/httplib.h @@ -1319,6 +1319,7 @@ public: Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Put(const std::string &path); Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr); @@ -1336,6 +1337,7 @@ public: Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Patch(const std::string &path); Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr); @@ -1353,6 +1355,7 @@ public: Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Delete(const std::string &path, DownloadProgress progress = nullptr); Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr); @@ -1662,6 +1665,7 @@ public: Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Put(const std::string &path); Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr); @@ -1679,6 +1683,7 @@ public: Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Patch(const std::string &path); Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr); @@ -1696,6 +1701,7 @@ public: Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr); Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr); Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr); + Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr); Result Delete(const std::string &path, DownloadProgress progress = nullptr); Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr); @@ -9115,6 +9121,32 @@ ClientImpl::Post(const std::string &path, const Headers &headers, content_type, progress); } +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + Request req; + req.method = "POST"; + req.path = path; + req.headers = headers; + req.body = body; + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.download_progress = std::move(progress); + + if (max_timeout_msec_ > 0) { + req.start_time_ = std::chrono::steady_clock::now(); + } + + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + + return send_(std::move(req)); +} + inline Result ClientImpl::Put(const std::string &path) { return Put(path, std::string(), std::string()); } @@ -9242,6 +9274,32 @@ ClientImpl::Put(const std::string &path, const Headers &headers, content_type, progress); } +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + Request req; + req.method = "PUT"; + req.path = path; + req.headers = headers; + req.body = body; + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.download_progress = std::move(progress); + + if (max_timeout_msec_ > 0) { + req.start_time_ = std::chrono::steady_clock::now(); + } + + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + + return send_(std::move(req)); +} + inline Result ClientImpl::Patch(const std::string &path) { return Patch(path, std::string(), std::string()); } @@ -9374,6 +9432,32 @@ ClientImpl::Patch(const std::string &path, const Headers &headers, content_type, progress); } +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + Request req; + req.method = "PATCH"; + req.path = path; + req.headers = headers; + req.body = body; + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.download_progress = std::move(progress); + + if (max_timeout_msec_ > 0) { + req.start_time_ = std::chrono::steady_clock::now(); + } + + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + + return send_(std::move(req)); +} + inline Result ClientImpl::Delete(const std::string &path, DownloadProgress progress) { return Delete(path, Headers(), std::string(), std::string(), progress); @@ -10679,6 +10763,14 @@ inline Result Client::Post(const std::string &path, const Headers &headers, UploadProgress progress) { return cli_->Post(path, headers, items, provider_items, progress); } +inline Result Client::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + return cli_->Post(path, headers, body, content_type, content_receiver, + progress); +} inline Result Client::Put(const std::string &path) { return cli_->Put(path); } inline Result Client::Put(const std::string &path, const Headers &headers) { @@ -10764,6 +10856,14 @@ inline Result Client::Put(const std::string &path, const Headers &headers, UploadProgress progress) { return cli_->Put(path, headers, items, provider_items, progress); } +inline Result Client::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + return cli_->Put(path, headers, body, content_type, content_receiver, + progress); +} inline Result Client::Patch(const std::string &path) { return cli_->Patch(path); @@ -10853,6 +10953,14 @@ Client::Patch(const std::string &path, const Headers &headers, UploadProgress progress) { return cli_->Patch(path, headers, items, provider_items, progress); } +inline Result Client::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + ContentReceiver content_receiver, + DownloadProgress progress) { + return cli_->Patch(path, headers, body, content_type, content_receiver, + progress); +} inline Result Client::Delete(const std::string &path, DownloadProgress progress) { diff --git a/test/test.cc b/test/test.cc index 7f10afb..3ebc859 100644 --- a/test/test.cc +++ b/test/test.cc @@ -5302,6 +5302,195 @@ TEST_F(ServerTest, PatchContentReceiver) { ASSERT_EQ("content", res->body); } +template +void TestWithHeadersAndContentReceiver( + ClientType& cli, + std::function request_func) { + Headers headers; + headers.emplace("X-Custom-Header", "test-value"); + + std::string received_body; + auto res = request_func( + cli, "/content_receiver", headers, "content", "application/json", + [&](const char *data, size_t data_length) { + received_body.append(data, data_length); + return true; + }, nullptr); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("content", received_body); +} + +TEST_F(ServerTest, PostWithHeadersAndContentReceiver) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiver(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Post(path, headers, body, content_type, receiver, progress); + }); +} + +TEST_F(ServerTest, PutWithHeadersAndContentReceiver) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiver(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Put(path, headers, body, content_type, receiver, progress); + }); +} + +TEST_F(ServerTest, PatchWithHeadersAndContentReceiver) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiver(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Patch(path, headers, body, content_type, receiver, progress); + }); +} + +template +void TestWithHeadersAndContentReceiverWithProgress( + ClientType& cli, + std::function request_func) { + Headers headers; + headers.emplace("X-Test-Header", "progress-test"); + + std::string received_body; + auto progress_called = false; + + auto res = request_func( + cli, "/content_receiver", headers, "content", "text/plain", + [&](const char *data, size_t data_length) { + received_body.append(data, data_length); + return true; + }, + [&](uint64_t /*current*/, uint64_t /*total*/) { + progress_called = true; + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("content", received_body); + EXPECT_TRUE(progress_called); +} + +TEST_F(ServerTest, PostWithHeadersAndContentReceiverWithProgress) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverWithProgress(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Post(path, headers, body, content_type, receiver, progress); + }); +} + +TEST_F(ServerTest, PutWithHeadersAndContentReceiverWithProgress) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverWithProgress(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Put(path, headers, body, content_type, receiver, progress); + }); +} + +TEST_F(ServerTest, PatchWithHeadersAndContentReceiverWithProgress) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverWithProgress(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver, DownloadProgress progress) { + return cli.Patch(path, headers, body, content_type, receiver, progress); + }); +} + +template +void TestWithHeadersAndContentReceiverError( + ClientType& cli, + std::function request_func) { + Headers headers; + headers.emplace("X-Error-Test", "true"); + + std::string received_body; + auto receiver_failed = false; + + auto res = request_func( + cli, "/content_receiver", headers, "content", "text/plain", + [&](const char *data, size_t data_length) { + received_body.append(data, data_length); + receiver_failed = true; + return false; + }); + + ASSERT_FALSE(res); + EXPECT_TRUE(receiver_failed); +} + +TEST_F(ServerTest, PostWithHeadersAndContentReceiverError) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverError(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver) { + return cli.Post(path, headers, body, content_type, receiver); + }); +} + +TEST_F(ServerTest, PuttWithHeadersAndContentReceiverError) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverError(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver) { + return cli.Put(path, headers, body, content_type, receiver); + }); +} + +TEST_F(ServerTest, PatchWithHeadersAndContentReceiverError) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + using ClientT = SSLClient; +#else + using ClientT = Client; +#endif + TestWithHeadersAndContentReceiverError(cli_, + [](ClientT& cli, const std::string& path, const Headers& headers, const std::string& body, + const std::string& content_type, ContentReceiver receiver) { + return cli.Patch(path, headers, body, content_type, receiver); + }); +} + TEST_F(ServerTest, PostQueryStringAndBody) { auto res = cli_.Post("/query-string-and-body?key=value", "content", "text/plain");