diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 76a2127a..d1fc1e22 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include "httpserver/http_utils.hpp" @@ -50,9 +52,11 @@ class deferred_response : public string_response { const std::string& content = "", int response_code = http::http_utils::http_ok, const std::string& content_type = http::http_utils::text_plain): - string_response(content, response_code, content_type), + string_response("", response_code, content_type), cycle_callback(cycle_callback), - closure_data(closure_data) { } + closure_data(closure_data), + initial_content(content), + content_offset(0) { } deferred_response(const deferred_response& other) = default; deferred_response(deferred_response&& other) noexcept = default; @@ -68,9 +72,22 @@ class deferred_response : public string_response { private: ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); std::shared_ptr closure_data; + std::string initial_content; + size_t content_offset; static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) { deferred_response* dfr = static_cast*>(cls); + + // First, send any remaining initial content + if (dfr->content_offset < dfr->initial_content.size()) { + size_t remaining = dfr->initial_content.size() - dfr->content_offset; + size_t to_copy = std::min(remaining, max); + std::memcpy(buf, dfr->initial_content.data() + dfr->content_offset, to_copy); + dfr->content_offset += to_copy; + return static_cast(to_copy); + } + + // Then call user's callback return dfr->cycle_callback(dfr->closure_data, buf, max); } }; diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index bc5e33f6..64ccfb12 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -106,6 +106,13 @@ class deferred_resource_with_data : public http_resource { } }; +class deferred_resource_empty_content : public http_resource { + public: + shared_ptr render_GET(const http_request&) { + return std::make_shared>(test_callback, nullptr); + } +}; + #ifdef HTTPSERVER_PORT #define PORT HTTPSERVER_PORT #else @@ -145,7 +152,7 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_suite) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); - LT_CHECK_EQ(s, "testtest"); + LT_CHECK_EQ(s, "cycle callback responsetesttest"); curl_easy_cleanup(curl); LT_END_AUTO_TEST(deferred_response_suite) @@ -163,10 +170,28 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); - LT_CHECK_EQ(s, "test42test84"); + LT_CHECK_EQ(s, "cycle callback responsetest42test84"); curl_easy_cleanup(curl); LT_END_AUTO_TEST(deferred_response_with_data) +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_empty_content) + deferred_resource_empty_content resource; + LT_ASSERT_EQ(true, ws->register_resource("base", &resource)); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "testtest"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response_empty_content) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV()