mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0542fdb8e4 | |||
| 78c474c744 | |||
| 88411a1f52 | |||
| ae6cf70bc4 | |||
| 68d1281759 | |||
| 0308d60cb2 | |||
| 59f5fdbb33 | |||
| 13184f5f80 |
@@ -7,7 +7,7 @@ A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
|
||||
It's extremely easy to setup. Just include the **httplib.h** file in your code!
|
||||
|
||||
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
||||
NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
||||
|
||||
Simple examples
|
||||
---------------
|
||||
@@ -177,15 +177,28 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Exception handler
|
||||
The exception handler gets called if a user routing handler throws an error.
|
||||
|
||||
```cpp
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
|
||||
res.status = 500;
|
||||
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
res.set_content(buf, "text/html");
|
||||
});
|
||||
```
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
||||
if (req.path == "/hello") {
|
||||
res.set_content("world", "text/html");
|
||||
return true; // This request is handled
|
||||
return Server::HandlerResponse::Handled;
|
||||
}
|
||||
return false; // Let the router handle this request
|
||||
return Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -597,9 +597,20 @@ 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 &)>;
|
||||
|
||||
using ExceptionHandler =
|
||||
std::function<void(const Request &, Response &, std::exception &e)>;
|
||||
|
||||
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 +621,57 @@ 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_exception_handler(ExceptionHandler 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 +765,9 @@ private:
|
||||
Handlers delete_handlers_;
|
||||
HandlersForContentReader delete_handlers_for_content_reader_;
|
||||
Handlers options_handlers_;
|
||||
HandlerWithReturn error_handler_;
|
||||
HandlerWithReturn pre_routing_handler_;
|
||||
HandlerWithResponse error_handler_;
|
||||
ExceptionHandler exception_handler_;
|
||||
HandlerWithResponse pre_routing_handler_;
|
||||
Handler post_routing_handler_;
|
||||
Logger logger_;
|
||||
Expect100ContinueHandler expect_100_continue_handler_;
|
||||
@@ -821,6 +848,14 @@ public:
|
||||
ResponseHandler response_handler, ContentReceiver content_receiver,
|
||||
Progress progress);
|
||||
|
||||
Result Get(const char *path, const Params ¶ms, const Headers &headers,
|
||||
Progress progress = nullptr);
|
||||
Result Get(const char *path, const Params ¶ms, const Headers &headers,
|
||||
ContentReceiver content_receiver, Progress progress = nullptr);
|
||||
Result Get(const char *path, const Params ¶ms, 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 +1149,14 @@ public:
|
||||
Result Get(const char *path, ResponseHandler response_handler,
|
||||
ContentReceiver content_receiver, Progress progress);
|
||||
|
||||
Result Get(const char *path, const Params ¶ms, const Headers &headers,
|
||||
Progress progress = nullptr);
|
||||
Result Get(const char *path, const Params ¶ms, const Headers &headers,
|
||||
ContentReceiver content_receiver, Progress progress = nullptr);
|
||||
Result Get(const char *path, const Params ¶ms, 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);
|
||||
|
||||
@@ -2446,7 +2489,7 @@ public:
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
|
||||
ret = deflate(&strm_, flush);
|
||||
assert(ret != Z_STREAM_ERROR);
|
||||
if (ret == Z_STREAM_ERROR) { return false; }
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
@@ -3111,6 +3154,14 @@ inline std::string params_to_query_str(const Params ¶ms) {
|
||||
return query;
|
||||
}
|
||||
|
||||
inline std::string append_query_params(const char *path, const Params ¶ms) {
|
||||
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 ¶ms) {
|
||||
split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
|
||||
std::string key;
|
||||
@@ -4069,66 +4120,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 +4259,110 @@ 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_exception_handler(ExceptionHandler handler) {
|
||||
exception_handler_ = std::move(handler);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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 +4429,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 +4767,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";
|
||||
@@ -4652,26 +4795,26 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
|
||||
if (req.method == "POST") {
|
||||
if (dispatch_request_for_content_reader(
|
||||
req, res, std::move(reader),
|
||||
post_handlers_for_content_reader_)) {
|
||||
req, res, std::move(reader),
|
||||
post_handlers_for_content_reader_)) {
|
||||
return true;
|
||||
}
|
||||
} else if (req.method == "PUT") {
|
||||
if (dispatch_request_for_content_reader(
|
||||
req, res, std::move(reader),
|
||||
put_handlers_for_content_reader_)) {
|
||||
req, res, std::move(reader),
|
||||
put_handlers_for_content_reader_)) {
|
||||
return true;
|
||||
}
|
||||
} else if (req.method == "PATCH") {
|
||||
if (dispatch_request_for_content_reader(
|
||||
req, res, std::move(reader),
|
||||
patch_handlers_for_content_reader_)) {
|
||||
req, res, std::move(reader),
|
||||
patch_handlers_for_content_reader_)) {
|
||||
return true;
|
||||
}
|
||||
} else if (req.method == "DELETE") {
|
||||
if (dispatch_request_for_content_reader(
|
||||
req, res, std::move(reader),
|
||||
delete_handlers_for_content_reader_)) {
|
||||
req, res, std::move(reader),
|
||||
delete_handlers_for_content_reader_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4702,22 +4845,14 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
|
||||
inline bool Server::dispatch_request(Request &req, Response &res,
|
||||
const Handlers &handlers) {
|
||||
try {
|
||||
for (const auto &x : handlers) {
|
||||
const auto &pattern = x.first;
|
||||
const auto &handler = x.second;
|
||||
for (const auto &x : handlers) {
|
||||
const auto &pattern = x.first;
|
||||
const auto &handler = x.second;
|
||||
|
||||
if (std::regex_match(req.path, req.matches, pattern)) {
|
||||
handler(req, res);
|
||||
return true;
|
||||
}
|
||||
if (std::regex_match(req.path, req.matches, pattern)) {
|
||||
handler(req, res);
|
||||
return true;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", ex.what());
|
||||
} catch (...) {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", "UNKNOWN");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -4931,7 +5066,23 @@ Server::process_request(Stream &strm, bool close_connection,
|
||||
}
|
||||
|
||||
// Rounting
|
||||
if (routing(req, res, strm)) {
|
||||
bool routed = false;
|
||||
try {
|
||||
routed = routing(req, res, strm);
|
||||
} catch (std::exception & e) {
|
||||
if (exception_handler_) {
|
||||
exception_handler_(req, res, e);
|
||||
routed = true;
|
||||
} else {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", e.what());
|
||||
}
|
||||
} catch (...) {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", "UNKNOWN");
|
||||
}
|
||||
|
||||
if (routed) {
|
||||
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
|
||||
return write_response_with_content(strm, close_connection, req, res);
|
||||
} else {
|
||||
@@ -5700,6 +5851,35 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
|
||||
return send(req);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Get(const char *path, const Params ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 +7104,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 ¶ms,
|
||||
const Headers &headers, Progress progress) {
|
||||
return cli_->Get(path, params, headers, progress);
|
||||
}
|
||||
inline Result Client::Get(const char *path, const Params ¶ms,
|
||||
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 ¶ms,
|
||||
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) {
|
||||
|
||||
+91
-6
@@ -6,6 +6,7 @@
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <stdexcept>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_CERT2_FILE "./cert2.pem"
|
||||
@@ -816,6 +817,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,12 +979,46 @@ TEST(ErrorHandlerTest, ContentLength) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(ExceptionHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.set_exception_handler([](const Request & /*req*/, Response &res, std::exception & /*e*/) {
|
||||
res.status = 500;
|
||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||
"text/html"); // <= Content-Length still 13
|
||||
});
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
throw std::runtime_error("abc");
|
||||
});
|
||||
|
||||
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(500, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(NoContentTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.status = 204;
|
||||
});
|
||||
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.
|
||||
@@ -985,9 +1045,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) {
|
||||
@@ -3979,7 +4039,7 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
TEST(HttpsToHttpRedirectTest, SimpleInterface) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
@@ -3989,4 +4049,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
|
||||
|
||||
Reference in New Issue
Block a user