mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85327e19ae | |||
| ed8efea98b | |||
| 1ccddd1b0b | |||
| 992f3dc690 |
@@ -462,6 +462,7 @@ public:
|
||||
Server &Patch(const char *pattern, Handler handler);
|
||||
Server &Patch(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Delete(const char *pattern, Handler handler);
|
||||
Server &Delete(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Options(const char *pattern, Handler handler);
|
||||
|
||||
[[deprecated]] bool set_base_dir(const char *dir,
|
||||
@@ -551,6 +552,7 @@ private:
|
||||
Handlers patch_handlers_;
|
||||
HandlersForContentReader patch_handlers_for_content_reader_;
|
||||
Handlers delete_handlers_;
|
||||
HandlersForContentReader delete_handlers_for_content_reader_;
|
||||
Handlers options_handlers_;
|
||||
Handler error_handler_;
|
||||
Logger logger_;
|
||||
@@ -2515,13 +2517,24 @@ get_range_offset_and_length(const Request &req, const Response &res,
|
||||
|
||||
inline bool expect_content(const Request &req) {
|
||||
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
|
||||
req.method == "PRI") {
|
||||
req.method == "PRI" || req.method == "DELETE") {
|
||||
return true;
|
||||
}
|
||||
// TODO: check if Content-Length is set
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool has_crlf(const char* s) {
|
||||
auto p = s;
|
||||
while (*p) {
|
||||
if (*p == '\r' || *p == '\n') {
|
||||
return true;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
template <typename CTX, typename Init, typename Update, typename Final>
|
||||
inline std::string message_digest(const std::string &s, Init init,
|
||||
@@ -2708,11 +2721,15 @@ inline size_t Request::get_header_value_count(const char *key) const {
|
||||
}
|
||||
|
||||
inline void Request::set_header(const char *key, const char *val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Request::set_header(const char *key, const std::string &val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Request::has_param(const char *key) const {
|
||||
@@ -2762,16 +2779,22 @@ inline size_t Response::get_header_value_count(const char *key) const {
|
||||
}
|
||||
|
||||
inline void Response::set_header(const char *key, const char *val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_header(const char *key, const std::string &val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_redirect(const char *url) {
|
||||
set_header("Location", url);
|
||||
status = 302;
|
||||
if (!detail::has_crlf(url)) {
|
||||
set_header("Location", url);
|
||||
status = 302;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_content(const char *s, size_t n,
|
||||
@@ -2968,6 +2991,13 @@ inline Server &Server::Delete(const char *pattern, Handler handler) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Delete(const char *pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
delete_handlers_for_content_reader_.push_back(
|
||||
std::make_pair(std::regex(pattern), handler));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Options(const char *pattern, Handler handler) {
|
||||
options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
|
||||
return *this;
|
||||
@@ -3481,6 +3511,12 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (req.method == "DELETE") {
|
||||
if (dispatch_request_for_content_reader(
|
||||
req, res, reader, delete_handlers_for_content_reader_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read content into `req.body`
|
||||
@@ -4032,17 +4068,16 @@ inline bool Client::process_request(Stream &strm, const Request &req,
|
||||
|
||||
// Body
|
||||
if (req.method != "HEAD" && req.method != "CONNECT") {
|
||||
ContentReceiver out = [&](const char *buf, size_t n) {
|
||||
if (res.body.size() + n > res.body.max_size()) { return false; }
|
||||
res.body.append(buf, n);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (req.content_receiver) {
|
||||
out = [&](const char *buf, size_t n) {
|
||||
return req.content_receiver(buf, n);
|
||||
};
|
||||
}
|
||||
auto out =
|
||||
req.content_receiver
|
||||
? static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
||||
return req.content_receiver(buf, n);
|
||||
})
|
||||
: static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
||||
if (res.body.size() + n > res.body.max_size()) { return false; }
|
||||
res.body.append(buf, n);
|
||||
return true;
|
||||
});
|
||||
|
||||
int dummy_status;
|
||||
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
|
||||
@@ -4468,7 +4503,9 @@ inline bool process_and_close_socket_ssl(
|
||||
}
|
||||
}
|
||||
|
||||
SSL_shutdown(ssl);
|
||||
if (ret) {
|
||||
SSL_shutdown(ssl); // shutdown only if not already closed by remote
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(ctx_mutex);
|
||||
SSL_free(ssl);
|
||||
|
||||
@@ -697,6 +697,36 @@ protected:
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
})
|
||||
.Get("/http_response_splitting",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("a", "1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\rSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\r\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\rb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_redirect("1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("Location"));
|
||||
})
|
||||
.Get("/slow",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
@@ -882,6 +912,11 @@ protected:
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("DELETE", "text/plain");
|
||||
})
|
||||
.Delete("/delete-body",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "content");
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
.Options(R"(\*)",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
|
||||
@@ -1680,6 +1715,12 @@ TEST_F(ServerTest, GetMethodRemoteAddr) {
|
||||
EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1");
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, HTTPResponseSplitting) {
|
||||
auto res = cli_.Get("/http_response_splitting");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, SlowRequest) {
|
||||
request_threads_.push_back(
|
||||
std::thread([=]() { auto res = cli_.Get("/slow"); }));
|
||||
@@ -1765,6 +1806,13 @@ TEST_F(ServerTest, Delete) {
|
||||
EXPECT_EQ("DELETE", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, DeleteContentReceiver) {
|
||||
auto res = cli_.Delete("/delete-body", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, Options) {
|
||||
auto res = cli_.Options("*");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
|
||||
Reference in New Issue
Block a user