Compare commits

...

5 Commits

Author SHA1 Message Date
yhirose 68d1281759 Resolve #839 2021-01-23 13:33:27 -05:00
yhirose 0308d60cb2 Resolve #831 (#835) 2021-01-23 12:23:06 -05:00
yhirose 59f5fdbb33 Resolve #840 2021-01-23 11:43:01 -05:00
Anonymous 13184f5f80 Return Server& from handler setters (#836)
* Update httplib.h

* Update httplib.h

* Update httplib.h

* Update httplib.h
2021-01-22 09:20:21 -05:00
Eric Lee 8d9a477edb No content check (#823)
* No content check

* unit test for no content

* fixing merge conflict break

* oops during manual merge conflict
2021-01-13 17:51:40 -05:00
2 changed files with 306 additions and 54 deletions
+229 -51
View File
@@ -597,9 +597,17 @@ inline void default_socket_options(socket_t sock) {
class Server {
public:
using Handler = std::function<void(const Request &, Response &)>;
using HandlerWithReturn = std::function<bool(const Request &, Response &)>;
enum class HandlerResponse {
Handled,
Unhandled,
};
using HandlerWithResponse =
std::function<HandlerResponse(const Request &, Response &)>;
using HandlerWithContentReader = std::function<void(
const Request &, Response &, const ContentReader &content_reader)>;
using Expect100ContinueHandler =
std::function<int(const Request &, Response &)>;
@@ -610,42 +618,56 @@ public:
virtual bool is_valid() const;
Server &Get(const char *pattern, Handler handler);
Server &Get(const char *pattern, size_t pattern_len, Handler handler);
Server &Post(const char *pattern, Handler handler);
Server &Post(const char *pattern, size_t pattern_len, Handler handler);
Server &Post(const char *pattern, HandlerWithContentReader handler);
Server &Post(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler);
Server &Put(const char *pattern, Handler handler);
Server &Put(const char *pattern, size_t pattern_len, Handler handler);
Server &Put(const char *pattern, HandlerWithContentReader handler);
Server &Put(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler);
Server &Patch(const char *pattern, Handler handler);
Server &Patch(const char *pattern, size_t pattern_len, Handler handler);
Server &Patch(const char *pattern, HandlerWithContentReader handler);
Server &Patch(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler);
Server &Delete(const char *pattern, Handler handler);
Server &Delete(const char *pattern, size_t pattern_len, Handler handler);
Server &Delete(const char *pattern, HandlerWithContentReader handler);
Server &Delete(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler);
Server &Options(const char *pattern, Handler handler);
Server &Options(const char *pattern, size_t pattern_len, Handler handler);
bool set_base_dir(const char *dir, const char *mount_point = nullptr);
bool set_mount_point(const char *mount_point, const char *dir,
Headers headers = Headers());
bool remove_mount_point(const char *mount_point);
void set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime);
void set_file_request_handler(Handler handler);
Server &set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime);
Server &set_file_request_handler(Handler handler);
void set_error_handler(HandlerWithReturn handler);
void set_error_handler(Handler handler);
void set_pre_routing_handler(HandlerWithReturn handler);
void set_post_routing_handler(Handler handler);
Server &set_error_handler(HandlerWithResponse handler);
Server &set_error_handler(Handler handler);
Server &set_pre_routing_handler(HandlerWithResponse handler);
Server &set_post_routing_handler(Handler handler);
void set_expect_100_continue_handler(Expect100ContinueHandler handler);
void set_logger(Logger logger);
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
Server &set_logger(Logger logger);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
Server &set_tcp_nodelay(bool on);
Server &set_socket_options(SocketOptions socket_options);
void set_keep_alive_max_count(size_t count);
void set_keep_alive_timeout(time_t sec);
void set_read_timeout(time_t sec, time_t usec = 0);
void set_write_timeout(time_t sec, time_t usec = 0);
void set_idle_interval(time_t sec, time_t usec = 0);
Server &set_keep_alive_max_count(size_t count);
Server &set_keep_alive_timeout(time_t sec);
Server &set_read_timeout(time_t sec, time_t usec = 0);
Server &set_write_timeout(time_t sec, time_t usec = 0);
Server &set_idle_interval(time_t sec, time_t usec = 0);
void set_payload_max_length(size_t length);
Server &set_payload_max_length(size_t length);
bool bind_to_port(const char *host, int port, int socket_flags = 0);
int bind_to_any_port(const char *host, int socket_flags = 0);
@@ -739,8 +761,8 @@ private:
Handlers delete_handlers_;
HandlersForContentReader delete_handlers_for_content_reader_;
Handlers options_handlers_;
HandlerWithReturn error_handler_;
HandlerWithReturn pre_routing_handler_;
HandlerWithResponse error_handler_;
HandlerWithResponse pre_routing_handler_;
Handler post_routing_handler_;
Logger logger_;
Expect100ContinueHandler expect_100_continue_handler_;
@@ -821,6 +843,14 @@ public:
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress);
Result Get(const char *path, const Params &params, const Headers &headers,
Progress progress = nullptr);
Result Get(const char *path, const Params &params, const Headers &headers,
ContentReceiver content_receiver, Progress progress = nullptr);
Result Get(const char *path, const Params &params, const Headers &headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress = nullptr);
Result Head(const char *path);
Result Head(const char *path, const Headers &headers);
@@ -1114,6 +1144,14 @@ public:
Result Get(const char *path, ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress);
Result Get(const char *path, const Params &params, const Headers &headers,
Progress progress = nullptr);
Result Get(const char *path, const Params &params, const Headers &headers,
ContentReceiver content_receiver, Progress progress = nullptr);
Result Get(const char *path, const Params &params, const Headers &headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress = nullptr);
Result Head(const char *path);
Result Head(const char *path, const Headers &headers);
@@ -3111,6 +3149,14 @@ inline std::string params_to_query_str(const Params &params) {
return query;
}
inline std::string append_query_params(const char *path, const Params &params) {
std::string path_with_query = path;
const static std::regex re("[^?]+\\?.*");
auto delm = std::regex_match(path, re) ? '&' : '?';
path_with_query += delm + params_to_query_str(params);
return path_with_query;
}
inline void parse_query_text(const std::string &s, Params &params) {
split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
std::string key;
@@ -4069,66 +4115,116 @@ inline Server::Server()
inline Server::~Server() {}
inline Server &Server::Get(const char *pattern, Handler handler) {
return Get(pattern, strlen(pattern), handler);
}
inline Server &Server::Get(const char *pattern, size_t pattern_len,
Handler handler) {
get_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern, Handler handler) {
return Post(pattern, strlen(pattern), handler);
}
inline Server &Server::Post(const char *pattern, size_t pattern_len,
Handler handler) {
post_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern,
HandlerWithContentReader handler) {
return Post(pattern, strlen(pattern), handler);
}
inline Server &Server::Post(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler) {
post_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern, Handler handler) {
return Put(pattern, strlen(pattern), handler);
}
inline Server &Server::Put(const char *pattern, size_t pattern_len,
Handler handler) {
put_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern,
HandlerWithContentReader handler) {
return Put(pattern, strlen(pattern), handler);
}
inline Server &Server::Put(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler) {
put_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern, Handler handler) {
return Patch(pattern, strlen(pattern), handler);
}
inline Server &Server::Patch(const char *pattern, size_t pattern_len,
Handler handler) {
patch_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern,
HandlerWithContentReader handler) {
return Patch(pattern, strlen(pattern), handler);
}
inline Server &Server::Patch(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler) {
patch_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Delete(const char *pattern, Handler handler) {
return Delete(pattern, strlen(pattern), handler);
}
inline Server &Server::Delete(const char *pattern, size_t pattern_len,
Handler handler) {
delete_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Delete(const char *pattern,
HandlerWithContentReader handler) {
return Delete(pattern, strlen(pattern), handler);
}
inline Server &Server::Delete(const char *pattern, size_t pattern_len,
HandlerWithContentReader handler) {
delete_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
inline Server &Server::Options(const char *pattern, Handler handler) {
return Options(pattern, strlen(pattern), handler);
}
inline Server &Server::Options(const char *pattern, size_t pattern_len,
Handler handler) {
options_handlers_.push_back(
std::make_pair(std::regex(pattern), std::move(handler)));
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return *this;
}
@@ -4158,72 +4254,105 @@ inline bool Server::remove_mount_point(const char *mount_point) {
return false;
}
inline void Server::set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime) {
inline Server &
Server::set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime) {
file_extension_and_mimetype_map_[ext] = mime;
return *this;
}
inline void Server::set_file_request_handler(Handler handler) {
inline Server &Server::set_file_request_handler(Handler handler) {
file_request_handler_ = std::move(handler);
return *this;
}
inline void Server::set_error_handler(HandlerWithReturn handler) {
inline Server &Server::set_error_handler(HandlerWithResponse handler) {
error_handler_ = std::move(handler);
return *this;
}
inline void Server::set_error_handler(Handler handler) {
inline Server &Server::set_error_handler(Handler handler) {
error_handler_ = [handler](const Request &req, Response &res) {
handler(req, res);
return true;
return HandlerResponse::Handled;
};
return *this;
}
inline void Server::set_pre_routing_handler(HandlerWithReturn handler) {
inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
pre_routing_handler_ = std::move(handler);
return *this;
}
inline void Server::set_post_routing_handler(Handler handler) {
inline Server &Server::set_post_routing_handler(Handler handler) {
post_routing_handler_ = std::move(handler);
return *this;
}
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
inline Server &Server::set_logger(Logger logger) {
logger_ = std::move(logger);
inline void
return *this;
}
inline Server &
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
expect_100_continue_handler_ = std::move(handler);
return *this;
}
inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
inline Server &Server::set_tcp_nodelay(bool on) {
tcp_nodelay_ = on;
inline void Server::set_socket_options(SocketOptions socket_options) {
return *this;
}
inline Server &Server::set_socket_options(SocketOptions socket_options) {
socket_options_ = std::move(socket_options);
return *this;
}
inline void Server::set_keep_alive_max_count(size_t count) {
inline Server &Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
return *this;
}
inline void Server::set_keep_alive_timeout(time_t sec) {
inline Server &Server::set_keep_alive_timeout(time_t sec) {
keep_alive_timeout_sec_ = sec;
return *this;
}
inline void Server::set_read_timeout(time_t sec, time_t usec) {
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
return *this;
}
inline void Server::set_write_timeout(time_t sec, time_t usec) {
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
return *this;
}
inline void Server::set_idle_interval(time_t sec, time_t usec) {
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
idle_interval_sec_ = sec;
idle_interval_usec_ = usec;
return *this;
}
inline void Server::set_payload_max_length(size_t length) {
inline Server &Server::set_payload_max_length(size_t length) {
payload_max_length_ = length;
return *this;
}
inline bool Server::bind_to_port(const char *host, int port, int socket_flags) {
@@ -4290,7 +4419,8 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
bool need_apply_ranges) {
assert(res.status != -1);
if (400 <= res.status && error_handler_ && error_handler_(req, res)) {
if (400 <= res.status && error_handler_ &&
error_handler_(req, res) == HandlerResponse::Handled) {
need_apply_ranges = true;
}
@@ -4627,7 +4757,10 @@ inline bool Server::listen_internal() {
}
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
if (pre_routing_handler_ && pre_routing_handler_(req, res)) { return true; }
if (pre_routing_handler_ &&
pre_routing_handler_(req, res) == HandlerResponse::Handled) {
return true;
}
// File handler
bool is_head_request = req.method == "HEAD";
@@ -5544,7 +5677,7 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
}
// Body
if (req.method != "HEAD" && req.method != "CONNECT") {
if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
auto out =
req.content_receiver_
? static_cast<ContentReceiverWithProgress>(
@@ -5700,6 +5833,35 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
return send(req);
}
inline Result ClientImpl::Get(const char *path, const Params &params,
const Headers &headers, Progress progress) {
if (params.empty()) { return Get(path, headers); }
std::string path_with_query = detail::append_query_params(path, params);
return Get(path_with_query.c_str(), headers, progress);
}
inline Result ClientImpl::Get(const char *path, const Params &params,
const Headers &headers,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, params, headers, nullptr, content_receiver, progress);
}
inline Result ClientImpl::Get(const char *path, const Params &params,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
if (params.empty()) {
return Get(path, headers, response_handler, content_receiver, progress);
}
std::string path_with_query = detail::append_query_params(path, params);
return Get(path_with_query.c_str(), params, headers, response_handler,
content_receiver, progress);
}
inline Result ClientImpl::Head(const char *path) {
return Head(path, Headers());
}
@@ -6924,6 +7086,22 @@ inline Result Client::Get(const char *path, const Headers &headers,
return cli_->Get(path, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline Result Client::Get(const char *path, const Params &params,
const Headers &headers, Progress progress) {
return cli_->Get(path, params, headers, progress);
}
inline Result Client::Get(const char *path, const Params &params,
const Headers &headers,
ContentReceiver content_receiver, Progress progress) {
return cli_->Get(path, params, headers, content_receiver, progress);
}
inline Result Client::Get(const char *path, const Params &params,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return cli_->Get(path, params, headers, response_handler, content_receiver,
progress);
}
inline Result Client::Head(const char *path) { return cli_->Head(path); }
inline Result Client::Head(const char *path, const Headers &headers) {
+77 -3
View File
@@ -816,6 +816,31 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest2, Redirect) {
SSLClient cli("nghttp2.org");
cli.set_follow_location(true);
Params params;
params.emplace("url", "http://www.google.com");
params.emplace("status_code", "302");
auto res = cli.Get("/httpbin/redirect-to", params, Headers{});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest3, Redirect) {
SSLClient cli("nghttp2.org");
cli.set_follow_location(true);
Params params;
params.emplace("url", "http://www.google.com");
auto res = cli.Get("/httpbin/redirect-to?status_code=302", params, Headers{});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST(RedirectToDifferentPort, Redirect) {
Server svr8080;
Server svr8081;
@@ -953,6 +978,30 @@ TEST(ErrorHandlerTest, ContentLength) {
ASSERT_FALSE(svr.is_running());
}
TEST(NoContentTest, ContentLength) {
Server svr;
svr.Get("/hi",
[](const Request & /*req*/, Response &res) { res.status = 204; });
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli(HOST, PORT);
auto res = cli.Get("/hi");
ASSERT_TRUE(res);
EXPECT_EQ(204, res->status);
EXPECT_EQ("0", res->get_header_value("Content-Length"));
}
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
}
TEST(RoutingHandlerTest, PreRoutingHandler) {
Server svr;
@@ -960,9 +1009,9 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
if (req.path == "/routing_handler") {
res.set_header("PRE_ROUTING", "on");
res.set_content("Routing Handler", "text/plain");
return true;
return httplib::Server::HandlerResponse::Handled;
}
return false;
return httplib::Server::HandlerResponse::Unhandled;
});
svr.set_error_handler([](const Request & /*req*/, Response &res) {
@@ -3954,7 +4003,7 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
}
#endif
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
TEST(HttpsToHttpRedirectTest, SimpleInterface) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
auto res =
@@ -3964,4 +4013,29 @@ TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
Params params;
params.emplace("url", "http://www.google.com");
params.emplace("status_code", "302");
auto res = cli.Get("/httpbin/redirect-to", params, Headers{});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
Params params;
params.emplace("url", "http://www.google.com");
auto res = cli.Get("/httpbin/redirect-to?status_code=302", params, Headers{});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
#endif