Skip to content

Commit 54c800e

Browse files
authored
Fix deferred_response content parameter not being used (issue #331) (#352)
The deferred_response constructor accepts a content parameter that was documented to send initial content before callback data, but was never actually used. This fix stores the content and sends it first before calling the user's callback. Changes: - Store content in initial_content member instead of passing to base class - Track content_offset for proper chunking of large content - Send initial content bytes before invoking user's cycle_callback - Add test for backward compatibility with empty content parameter
1 parent 8ed6340 commit 54c800e

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

src/httpserver/deferred_response.hpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <stddef.h>
2929
#include <stdint.h>
3030
#include <sys/types.h>
31+
#include <algorithm>
32+
#include <cstring>
3133
#include <memory>
3234
#include <string>
3335
#include "httpserver/http_utils.hpp"
@@ -50,9 +52,11 @@ class deferred_response : public string_response {
5052
const std::string& content = "",
5153
int response_code = http::http_utils::http_ok,
5254
const std::string& content_type = http::http_utils::text_plain):
53-
string_response(content, response_code, content_type),
55+
string_response("", response_code, content_type),
5456
cycle_callback(cycle_callback),
55-
closure_data(closure_data) { }
57+
closure_data(closure_data),
58+
initial_content(content),
59+
content_offset(0) { }
5660

5761
deferred_response(const deferred_response& other) = default;
5862
deferred_response(deferred_response&& other) noexcept = default;
@@ -68,9 +72,22 @@ class deferred_response : public string_response {
6872
private:
6973
ssize_t (*cycle_callback)(std::shared_ptr<T>, char*, size_t);
7074
std::shared_ptr<T> closure_data;
75+
std::string initial_content;
76+
size_t content_offset;
7177

7278
static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) {
7379
deferred_response<T>* dfr = static_cast<deferred_response<T>*>(cls);
80+
81+
// First, send any remaining initial content
82+
if (dfr->content_offset < dfr->initial_content.size()) {
83+
size_t remaining = dfr->initial_content.size() - dfr->content_offset;
84+
size_t to_copy = std::min(remaining, max);
85+
std::memcpy(buf, dfr->initial_content.data() + dfr->content_offset, to_copy);
86+
dfr->content_offset += to_copy;
87+
return static_cast<ssize_t>(to_copy);
88+
}
89+
90+
// Then call user's callback
7491
return dfr->cycle_callback(dfr->closure_data, buf, max);
7592
}
7693
};

test/integ/deferred.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ class deferred_resource_with_data : public http_resource {
106106
}
107107
};
108108

109+
class deferred_resource_empty_content : public http_resource {
110+
public:
111+
shared_ptr<http_response> render_GET(const http_request&) {
112+
return std::make_shared<deferred_response<void>>(test_callback, nullptr);
113+
}
114+
};
115+
109116
#ifdef HTTPSERVER_PORT
110117
#define PORT HTTPSERVER_PORT
111118
#else
@@ -145,7 +152,7 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_suite)
145152
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
146153
res = curl_easy_perform(curl);
147154
LT_ASSERT_EQ(res, 0);
148-
LT_CHECK_EQ(s, "testtest");
155+
LT_CHECK_EQ(s, "cycle callback responsetesttest");
149156
curl_easy_cleanup(curl);
150157
LT_END_AUTO_TEST(deferred_response_suite)
151158

@@ -163,10 +170,28 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data)
163170
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
164171
res = curl_easy_perform(curl);
165172
LT_ASSERT_EQ(res, 0);
166-
LT_CHECK_EQ(s, "test42test84");
173+
LT_CHECK_EQ(s, "cycle callback responsetest42test84");
167174
curl_easy_cleanup(curl);
168175
LT_END_AUTO_TEST(deferred_response_with_data)
169176

177+
LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_empty_content)
178+
deferred_resource_empty_content resource;
179+
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
180+
curl_global_init(CURL_GLOBAL_ALL);
181+
182+
std::string s;
183+
CURL *curl = curl_easy_init();
184+
CURLcode res;
185+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
186+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
187+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
188+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
189+
res = curl_easy_perform(curl);
190+
LT_ASSERT_EQ(res, 0);
191+
LT_CHECK_EQ(s, "testtest");
192+
curl_easy_cleanup(curl);
193+
LT_END_AUTO_TEST(deferred_response_empty_content)
194+
170195
LT_BEGIN_AUTO_TEST_ENV()
171196
AUTORUN_TESTS()
172197
LT_END_AUTO_TEST_ENV()

0 commit comments

Comments
 (0)