Skip to content

Commit 96e9ec0

Browse files
committed
Improved DataSink interface
1 parent c58fca5 commit 96e9ec0

File tree

3 files changed

+140
-84
lines changed

3 files changed

+140
-84
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
129129

130130
res.set_content_provider(
131131
data->size(), // Content length
132-
[data](uint64_t offset, uint64_t length, DataSink sink) {
132+
[data](uint64_t offset, uint64_t length, DataSink &sink) {
133133
const auto &d = *data;
134-
sink(&d[offset], std::min(length, DATA_CHUNK_SIZE));
134+
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
135135
},
136136
[data] { delete data; });
137137
});
@@ -169,11 +169,11 @@ svr.Post("/content_receiver",
169169
```cpp
170170
svr.Get("/chunked", [&](const Request& req, Response& res) {
171171
res.set_chunked_content_provider(
172-
[](uint64_t offset, DataSink sink, Done done) {
173-
sink("123", 3);
174-
sink("345", 3);
175-
sink("789", 3);
176-
done();
172+
[](uint64_t offset, DataSink &sink) {
173+
sink.write("123", 3);
174+
sink.write("345", 3);
175+
sink.write("789", 3);
176+
sink.done();
177177
}
178178
);
179179
});

httplib.h

Lines changed: 88 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,6 @@ using Headers = std::multimap<std::string, std::string, detail::ci>;
195195
using Params = std::multimap<std::string, std::string>;
196196
using Match = std::smatch;
197197

198-
using DataSink = std::function<void(const char *data, size_t data_len)>;
199-
200-
using Done = std::function<void()>;
201-
202-
using ContentProvider =
203-
std::function<void(size_t offset, size_t length, DataSink sink)>;
204-
205-
using ContentProviderWithCloser =
206-
std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
207-
208198
using Progress = std::function<bool(uint64_t current, uint64_t total)>;
209199

210200
struct Response;
@@ -219,6 +209,20 @@ struct MultipartFormData {
219209
using MultipartFormDataItems = std::vector<MultipartFormData>;
220210
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
221211

212+
class DataSink {
213+
public:
214+
DataSink() = default;
215+
DataSink(const DataSink &) = delete;
216+
DataSink(const DataSink &&) = delete;
217+
218+
std::function<void(const char *data, size_t data_len)> write;
219+
std::function<void()> done;
220+
// TODO: std::function<bool()> is_alive;
221+
};
222+
223+
using ContentProvider =
224+
std::function<void(size_t offset, size_t length, DataSink &sink)>;
225+
222226
using ContentReceiver =
223227
std::function<bool(const char *data, size_t data_length)>;
224228

@@ -310,11 +314,12 @@ struct Response {
310314

311315
void set_content_provider(
312316
size_t length,
313-
std::function<void(size_t offset, size_t length, DataSink sink)> provider,
317+
std::function<void(size_t offset, size_t length, DataSink &sink)>
318+
provider,
314319
std::function<void()> resource_releaser = [] {});
315320

316321
void set_chunked_content_provider(
317-
std::function<void(size_t offset, DataSink sink, Done done)> provider,
322+
std::function<void(size_t offset, DataSink &sink)> provider,
318323
std::function<void()> resource_releaser = [] {});
319324

320325
Response() : status(-1), content_length(0) {}
@@ -327,7 +332,7 @@ struct Response {
327332

328333
// private members...
329334
size_t content_length;
330-
ContentProviderWithCloser content_provider;
335+
ContentProvider content_provider;
331336
std::function<void()> content_provider_resource_releaser;
332337
};
333338

@@ -1876,47 +1881,69 @@ inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
18761881
return write_len;
18771882
}
18781883

1879-
inline ssize_t write_content(Stream &strm,
1880-
ContentProviderWithCloser content_provider,
1884+
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
18811885
size_t offset, size_t length) {
18821886
size_t begin_offset = offset;
18831887
size_t end_offset = offset + length;
18841888
while (offset < end_offset) {
18851889
ssize_t written_length = 0;
1886-
content_provider(
1887-
offset, end_offset - offset,
1888-
[&](const char *d, size_t l) {
1889-
offset += l;
1890-
written_length = strm.write(d, l);
1891-
},
1892-
[&](void) { written_length = -1; });
1890+
1891+
DataSink data_sink;
1892+
data_sink.write = [&](const char *d, size_t l) {
1893+
offset += l;
1894+
written_length = strm.write(d, l);
1895+
};
1896+
data_sink.done = [&](void) { written_length = -1; };
1897+
1898+
content_provider(offset, end_offset - offset,
1899+
// [&](const char *d, size_t l) {
1900+
// offset += l;
1901+
// written_length = strm.write(d, l);
1902+
// },
1903+
// [&](void) { written_length = -1; }
1904+
data_sink);
18931905
if (written_length < 0) { return written_length; }
18941906
}
18951907
return static_cast<ssize_t>(offset - begin_offset);
18961908
}
18971909

1898-
inline ssize_t
1899-
write_content_chunked(Stream &strm,
1900-
ContentProviderWithCloser content_provider) {
1910+
inline ssize_t write_content_chunked(Stream &strm,
1911+
ContentProvider content_provider) {
19011912
size_t offset = 0;
19021913
auto data_available = true;
19031914
ssize_t total_written_length = 0;
19041915
while (data_available) {
19051916
ssize_t written_length = 0;
1917+
1918+
DataSink data_sink;
1919+
data_sink.write = [&](const char *d, size_t l) {
1920+
data_available = l > 0;
1921+
offset += l;
1922+
1923+
// Emit chunked response header and footer for each chunk
1924+
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
1925+
written_length = strm.write(chunk);
1926+
};
1927+
data_sink.done = [&](void) {
1928+
data_available = false;
1929+
written_length = strm.write("0\r\n\r\n");
1930+
};
1931+
19061932
content_provider(
19071933
offset, 0,
1908-
[&](const char *d, size_t l) {
1909-
data_available = l > 0;
1910-
offset += l;
1911-
1912-
// Emit chunked response header and footer for each chunk
1913-
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
1914-
written_length = strm.write(chunk);
1915-
},
1916-
[&](void) {
1917-
data_available = false;
1918-
written_length = strm.write("0\r\n\r\n");
1919-
});
1934+
// [&](const char *d, size_t l) {
1935+
// data_available = l > 0;
1936+
// offset += l;
1937+
//
1938+
// // Emit chunked response header and footer for each chunk
1939+
// auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) +
1940+
// "\r\n"; written_length = strm.write(chunk);
1941+
// },
1942+
// [&](void) {
1943+
// data_available = false;
1944+
// written_length = strm.write("0\r\n\r\n");
1945+
// }
1946+
data_sink);
19201947

19211948
if (written_length < 0) { return written_length; }
19221949
total_written_length += written_length;
@@ -2652,21 +2679,23 @@ inline void Response::set_content(const std::string &s,
26522679

26532680
inline void Response::set_content_provider(
26542681
size_t length,
2655-
std::function<void(size_t offset, size_t length, DataSink sink)> provider,
2682+
std::function<void(size_t offset, size_t length, DataSink &sink)> provider,
26562683
std::function<void()> resource_releaser) {
26572684
assert(length > 0);
26582685
content_length = length;
2659-
content_provider = [provider](size_t offset, size_t length, DataSink sink,
2660-
Done) { provider(offset, length, sink); };
2686+
content_provider = [provider](size_t offset, size_t length, DataSink &sink) {
2687+
provider(offset, length, sink);
2688+
};
26612689
content_provider_resource_releaser = resource_releaser;
26622690
}
26632691

26642692
inline void Response::set_chunked_content_provider(
2665-
std::function<void(size_t offset, DataSink sink, Done done)> provider,
2693+
std::function<void(size_t offset, DataSink &sink)> provider,
26662694
std::function<void()> resource_releaser) {
26672695
content_length = 0;
2668-
content_provider = [provider](size_t offset, size_t, DataSink sink,
2669-
Done done) { provider(offset, sink, done); };
2696+
content_provider = [provider](size_t offset, size_t, DataSink &sink) {
2697+
provider(offset, sink);
2698+
};
26702699
content_provider_resource_releaser = resource_releaser;
26712700
}
26722701

@@ -3731,12 +3760,15 @@ inline bool Client::write_request(Stream &strm, const Request &req,
37313760
if (req.content_provider) {
37323761
size_t offset = 0;
37333762
size_t end_offset = req.content_length;
3763+
3764+
DataSink data_sink;
3765+
data_sink.write = [&](const char *d, size_t l) {
3766+
auto written_length = strm.write(d, l);
3767+
offset += written_length;
3768+
};
3769+
37343770
while (offset < end_offset) {
3735-
req.content_provider(offset, end_offset - offset,
3736-
[&](const char *d, size_t l) {
3737-
auto written_length = strm.write(d, l);
3738-
offset += written_length;
3739-
});
3771+
req.content_provider(offset, end_offset - offset, data_sink);
37403772
}
37413773
}
37423774
} else {
@@ -3761,12 +3793,15 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
37613793
if (compress_) {
37623794
if (content_provider) {
37633795
size_t offset = 0;
3796+
3797+
DataSink data_sink;
3798+
data_sink.write = [&](const char *data, size_t data_len) {
3799+
req.body.append(data, data_len);
3800+
offset += data_len;
3801+
};
3802+
37643803
while (offset < content_length) {
3765-
content_provider(offset, content_length - offset,
3766-
[&](const char *data, size_t data_len) {
3767-
req.body.append(data, data_len);
3768-
offset += data_len;
3769-
});
3804+
content_provider(offset, content_length - offset, data_sink);
37703805
}
37713806
} else {
37723807
req.body = body;

test/test.cc

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -697,42 +697,57 @@ class ServerTest : public ::testing::Test {
697697
.Get("/streamed-chunked",
698698
[&](const Request & /*req*/, Response &res) {
699699
res.set_chunked_content_provider(
700-
[](uint64_t /*offset*/, DataSink sink, Done done) {
701-
sink("123", 3);
702-
sink("456", 3);
703-
sink("789", 3);
704-
done();
700+
[](uint64_t /*offset*/, DataSink &sink) {
701+
sink.write("123", 3);
702+
sink.write("456", 3);
703+
sink.write("789", 3);
704+
sink.done();
705705
});
706706
})
707+
.Get("/streamed-chunked2",
708+
[&](const Request & /*req*/, Response &res) {
709+
auto i = new int(0);
710+
res.set_chunked_content_provider(
711+
[i](uint64_t /*offset*/, DataSink &sink) {
712+
switch (*i) {
713+
case 0: sink.write("123", 3); break;
714+
case 1: sink.write("456", 3); break;
715+
case 2: sink.write("789", 3); break;
716+
case 3: sink.done(); break;
717+
}
718+
(*i)++;
719+
},
720+
[i] { delete i; });
721+
})
707722
.Get("/streamed",
708723
[&](const Request & /*req*/, Response &res) {
709724
res.set_content_provider(
710-
6, [](uint64_t offset, uint64_t /*length*/, DataSink sink) {
711-
sink(offset < 3 ? "a" : "b", 1);
725+
6, [](uint64_t offset, uint64_t /*length*/, DataSink &sink) {
726+
sink.write(offset < 3 ? "a" : "b", 1);
712727
});
713728
})
714729
.Get("/streamed-with-range",
715730
[&](const Request & /*req*/, Response &res) {
716731
auto data = new std::string("abcdefg");
717732
res.set_content_provider(
718733
data->size(),
719-
[data](uint64_t offset, uint64_t length, DataSink sink) {
734+
[data](uint64_t offset, uint64_t length, DataSink &sink) {
720735
size_t DATA_CHUNK_SIZE = 4;
721736
const auto &d = *data;
722737
auto out_len =
723738
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
724-
sink(&d[static_cast<size_t>(offset)], out_len);
739+
sink.write(&d[static_cast<size_t>(offset)], out_len);
725740
},
726741
[data] { delete data; });
727742
})
728743
.Get("/streamed-cancel",
729744
[&](const Request & /*req*/, Response &res) {
730-
res.set_content_provider(
731-
size_t(-1),
732-
[](uint64_t /*offset*/, uint64_t /*length*/, DataSink sink) {
733-
std::string data = "data_chunk";
734-
sink(data.data(), data.size());
735-
});
745+
res.set_content_provider(size_t(-1), [](uint64_t /*offset*/,
746+
uint64_t /*length*/,
747+
DataSink &sink) {
748+
std::string data = "data_chunk";
749+
sink.write(data.data(), data.size());
750+
});
736751
})
737752
.Get("/with-range",
738753
[&](const Request & /*req*/, Response &res) {
@@ -1508,6 +1523,13 @@ TEST_F(ServerTest, GetStreamedChunked) {
15081523
EXPECT_EQ(std::string("123456789"), res->body);
15091524
}
15101525

1526+
TEST_F(ServerTest, GetStreamedChunked2) {
1527+
auto res = cli_.Get("/streamed-chunked2");
1528+
ASSERT_TRUE(res != nullptr);
1529+
EXPECT_EQ(200, res->status);
1530+
EXPECT_EQ(std::string("123456789"), res->body);
1531+
}
1532+
15111533
TEST_F(ServerTest, LargeChunkedPost) {
15121534
Request req;
15131535
req.method = "POST";
@@ -1567,8 +1589,8 @@ TEST_F(ServerTest, Put) {
15671589
TEST_F(ServerTest, PutWithContentProvider) {
15681590
auto res = cli_.Put(
15691591
"/put", 3,
1570-
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
1571-
sink("PUT", 3);
1592+
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
1593+
sink.write("PUT", 3);
15721594
},
15731595
"text/plain");
15741596

@@ -1582,8 +1604,8 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
15821604
cli_.set_compress(true);
15831605
auto res = cli_.Put(
15841606
"/put", 3,
1585-
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
1586-
sink("PUT", 3);
1607+
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
1608+
sink.write("PUT", 3);
15871609
},
15881610
"text/plain");
15891611

@@ -1689,7 +1711,8 @@ TEST_F(ServerTest, PatchContentReceiver) {
16891711
}
16901712

16911713
TEST_F(ServerTest, PostQueryStringAndBody) {
1692-
auto res = cli_.Post("/query-string-and-body?key=value", "content", "text/plain");
1714+
auto res =
1715+
cli_.Post("/query-string-and-body?key=value", "content", "text/plain");
16931716
ASSERT_TRUE(res != nullptr);
16941717
ASSERT_EQ(200, res->status);
16951718
}
@@ -2139,8 +2162,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
21392162
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
21402163
msleep(1);
21412164

2142-
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
2143-
CLIENT_PRIVATE_KEY_FILE);
2165+
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
21442166
auto res = cli.Get("/test");
21452167
cli.set_timeout_sec(30);
21462168
ASSERT_TRUE(res != nullptr);
@@ -2181,8 +2203,7 @@ TEST(SSLClientServerTest, TrustDirOptional) {
21812203
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
21822204
msleep(1);
21832205

2184-
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
2185-
CLIENT_PRIVATE_KEY_FILE);
2206+
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
21862207
auto res = cli.Get("/test");
21872208
cli.set_timeout_sec(30);
21882209
ASSERT_TRUE(res != nullptr);

0 commit comments

Comments
 (0)