mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2025-05-11 21:53:57 +00:00
Simplified ContentReceiver interface
This commit is contained in:
parent
d03937e144
commit
9d57899352
@ -194,7 +194,7 @@ int main(void)
|
|||||||
std::string body;
|
std::string body;
|
||||||
|
|
||||||
auto res = cli.Get("/large-data",
|
auto res = cli.Get("/large-data",
|
||||||
[&](const char *data, uint64_t data_length, uint64_t offset, uint64_t content_length) {
|
[&](const char *data, uint64_t data_length) {
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
50
httplib.h
50
httplib.h
@ -202,8 +202,7 @@ typedef std::function<void(size_t offset, size_t length, DataSink sink,
|
|||||||
Done done)>
|
Done done)>
|
||||||
ContentProviderWithCloser;
|
ContentProviderWithCloser;
|
||||||
|
|
||||||
typedef std::function<bool(const char *data, size_t data_length, size_t offset,
|
typedef std::function<bool(const char *data, size_t data_length)>
|
||||||
uint64_t content_length)>
|
|
||||||
ContentReceiver;
|
ContentReceiver;
|
||||||
|
|
||||||
typedef std::function<bool(ContentReceiver receiver)> ContentReader;
|
typedef std::function<bool(ContentReceiver receiver)> ContentReader;
|
||||||
@ -1523,12 +1522,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::function<bool(const char *data, size_t data_length)>
|
|
||||||
ContentReceiverCore;
|
|
||||||
|
|
||||||
inline bool read_content_with_length(Stream &strm, uint64_t len,
|
inline bool read_content_with_length(Stream &strm, uint64_t len,
|
||||||
Progress progress,
|
Progress progress, ContentReceiver out) {
|
||||||
ContentReceiverCore out) {
|
|
||||||
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
||||||
|
|
||||||
uint64_t r = 0;
|
uint64_t r = 0;
|
||||||
@ -1560,7 +1555,7 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) {
|
inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
|
||||||
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
|
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
|
||||||
@ -1575,7 +1570,7 @@ inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool read_content_chunked(Stream &strm, ContentReceiverCore out) {
|
inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
|
||||||
const auto bufsiz = 16;
|
const auto bufsiz = 16;
|
||||||
char buf[bufsiz];
|
char buf[bufsiz];
|
||||||
|
|
||||||
@ -1615,9 +1610,9 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
||||||
Progress progress, ContentReceiverCore receiver) {
|
Progress progress, ContentReceiver receiver) {
|
||||||
|
|
||||||
ContentReceiverCore out = [&](const char *buf, size_t n) {
|
ContentReceiver out = [&](const char *buf, size_t n) {
|
||||||
return receiver(buf, n);
|
return receiver(buf, n);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2673,19 +2668,9 @@ inline bool
|
|||||||
Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
|
Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
|
||||||
Request &req, Response &res,
|
Request &req, Response &res,
|
||||||
ContentReceiver receiver) {
|
ContentReceiver receiver) {
|
||||||
size_t offset = 0;
|
if (!detail::read_content(
|
||||||
|
strm, req, payload_max_length_, res.status, Progress(),
|
||||||
size_t length = 0;
|
[&](const char *buf, size_t n) { return receiver(buf, n); })) {
|
||||||
if (req.get_header_value("Content-Encoding") != "gzip") {
|
|
||||||
length = get_header_value_uint64(req.headers, "Content-Length", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!detail::read_content(strm, req, payload_max_length_, res.status,
|
|
||||||
Progress(), [&](const char *buf, size_t n) {
|
|
||||||
auto ret = receiver(buf, n, offset, length);
|
|
||||||
offset += n;
|
|
||||||
return ret;
|
|
||||||
})) {
|
|
||||||
return write_response(strm, last_connection, req, res);
|
return write_response(strm, last_connection, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3253,26 +3238,15 @@ inline bool Client::process_request(Stream &strm, const Request &req,
|
|||||||
|
|
||||||
// Body
|
// Body
|
||||||
if (req.method != "HEAD") {
|
if (req.method != "HEAD") {
|
||||||
detail::ContentReceiverCore out = [&](const char *buf, size_t n) {
|
ContentReceiver out = [&](const char *buf, size_t n) {
|
||||||
if (res.body.size() + n > res.body.max_size()) { return false; }
|
if (res.body.size() + n > res.body.max_size()) { return false; }
|
||||||
res.body.append(buf, n);
|
res.body.append(buf, n);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (req.content_receiver) {
|
if (req.content_receiver) {
|
||||||
auto offset = std::make_shared<size_t>();
|
out = [&](const char *buf, size_t n) {
|
||||||
|
return req.content_receiver(buf, n);
|
||||||
size_t length = 0;
|
|
||||||
if (res.get_header_value("Content-Encoding") != "gzip") {
|
|
||||||
length = get_header_value_uint64(res.headers, "Content-Length", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto receiver = req.content_receiver;
|
|
||||||
|
|
||||||
out = [offset, length, receiver](const char *buf, size_t n) {
|
|
||||||
auto ret = receiver(buf, n, *offset, length);
|
|
||||||
(*offset) += n;
|
|
||||||
return ret;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
80
test/test.cc
80
test/test.cc
@ -229,7 +229,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
|
|||||||
std::string body;
|
std::string body;
|
||||||
auto res =
|
auto res =
|
||||||
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
|
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
|
||||||
[&](const char *data, size_t data_length, uint64_t, uint64_t) {
|
[&](const char *data, size_t data_length) {
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -261,7 +261,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
|||||||
EXPECT_EQ(200, response.status);
|
EXPECT_EQ(200, response.status);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[&](const char *data, size_t data_length, uint64_t, uint64_t) {
|
[&](const char *data, size_t data_length) {
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -745,18 +745,10 @@ protected:
|
|||||||
EXPECT_EQ("5", req.get_header_value("Content-Length"));
|
EXPECT_EQ("5", req.get_header_value("Content-Length"));
|
||||||
})
|
})
|
||||||
.Post("/content_receiver",
|
.Post("/content_receiver",
|
||||||
[&](const Request & req, Response &res,
|
[&](const Request & /*req*/, Response &res,
|
||||||
const ContentReader &content_reader) {
|
const ContentReader &content_reader) {
|
||||||
std::string body;
|
std::string body;
|
||||||
content_reader([&](const char *data, size_t data_length,
|
content_reader([&](const char *data, size_t data_length) {
|
||||||
size_t offset,
|
|
||||||
uint64_t content_length) {
|
|
||||||
EXPECT_EQ(offset, 0);
|
|
||||||
if (req.get_header_value("Content-Encoding") == "gzip") {
|
|
||||||
EXPECT_EQ(content_length, 0);
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(content_length, 7);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(data_length, 7);
|
EXPECT_EQ(data_length, 7);
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
@ -768,9 +760,7 @@ protected:
|
|||||||
[&](const Request & /*req*/, Response &res,
|
[&](const Request & /*req*/, Response &res,
|
||||||
const ContentReader &content_reader) {
|
const ContentReader &content_reader) {
|
||||||
std::string body;
|
std::string body;
|
||||||
content_reader([&](const char *data, size_t data_length,
|
content_reader([&](const char *data, size_t data_length) {
|
||||||
size_t /*offset*/,
|
|
||||||
uint64_t /*content_length*/) {
|
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -781,9 +771,7 @@ protected:
|
|||||||
[&](const Request & /*req*/, Response &res,
|
[&](const Request & /*req*/, Response &res,
|
||||||
const ContentReader &content_reader) {
|
const ContentReader &content_reader) {
|
||||||
std::string body;
|
std::string body;
|
||||||
content_reader([&](const char *data, size_t data_length,
|
content_reader([&](const char *data, size_t data_length) {
|
||||||
size_t /*offset*/,
|
|
||||||
uint64_t /*content_length*/) {
|
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -1284,10 +1272,15 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetStreamedEndless) {
|
TEST_F(ServerTest, GetStreamedEndless) {
|
||||||
|
size_t offset = 0;
|
||||||
auto res = cli_.Get("/streamed-cancel",
|
auto res = cli_.Get("/streamed-cancel",
|
||||||
[](const char * /*data*/, uint64_t /*data_length*/,
|
[&](const char * /*data*/, uint64_t data_length) {
|
||||||
uint64_t offset,
|
if (offset < 100) {
|
||||||
uint64_t /*content_length*/) { return offset < 100; });
|
offset += data_length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1579,15 +1572,12 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
|||||||
Headers headers;
|
Headers headers;
|
||||||
headers.emplace("Accept-Encoding", "gzip, deflate");
|
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||||
std::string body;
|
std::string body;
|
||||||
auto res = cli_.Get("/gzip", headers,
|
auto res =
|
||||||
[&](const char *data, uint64_t data_length,
|
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
|
||||||
uint64_t offset, uint64_t content_length) {
|
EXPECT_EQ(data_length, 100);
|
||||||
EXPECT_EQ(data_length, 100);
|
body.append(data, data_length);
|
||||||
EXPECT_EQ(offset, 0);
|
return true;
|
||||||
EXPECT_EQ(content_length, 0);
|
});
|
||||||
body.append(data, data_length);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
||||||
@ -1602,15 +1592,12 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
|||||||
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||||
Headers headers;
|
Headers headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
auto res = cli_.Get("/gzip", headers,
|
auto res =
|
||||||
[&](const char *data, uint64_t data_length,
|
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
|
||||||
uint64_t offset, uint64_t content_length) {
|
EXPECT_EQ(data_length, 100);
|
||||||
EXPECT_EQ(data_length, 100);
|
body.append(data, data_length);
|
||||||
EXPECT_EQ(offset, 0);
|
return true;
|
||||||
EXPECT_EQ(content_length, 100);
|
});
|
||||||
body.append(data, data_length);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
||||||
@ -1641,15 +1628,12 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
|
|||||||
Headers headers;
|
Headers headers;
|
||||||
headers.emplace("Accept-Encoding", "gzip, deflate");
|
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||||
std::string body;
|
std::string body;
|
||||||
auto res = cli_.Get("/nogzip", headers,
|
auto res =
|
||||||
[&](const char *data, uint64_t data_length,
|
cli_.Get("/nogzip", headers, [&](const char *data, uint64_t data_length) {
|
||||||
uint64_t offset, uint64_t content_length) {
|
EXPECT_EQ(data_length, 100);
|
||||||
EXPECT_EQ(data_length, 100);
|
body.append(data, data_length);
|
||||||
EXPECT_EQ(offset, 0);
|
return true;
|
||||||
EXPECT_EQ(content_length, 100);
|
});
|
||||||
body.append(data, data_length);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(false, res->has_header("Content-Encoding"));
|
EXPECT_EQ(false, res->has_header("Content-Encoding"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user