Compare commits

..

9 Commits

Author SHA1 Message Date
Nikolas 0542fdb8e4 Add exception handler (#845)
* Add exception handler

* revert content reader changes

* Add test for and fix exception handler

* Fix warning in test

* Readd exception test, improve readme note, don't rethrow errors, remove exception handler response
2021-01-28 17:19:11 -05:00
yhirose 78c474c744 Update README 2021-01-27 11:59:42 -05:00
yhirose 88411a1f52 Fix #846 2021-01-27 14:35:32 +00:00
yhirose ae6cf70bc4 Updated README 2021-01-26 08:38:28 -05:00
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
3 changed files with 400 additions and 81 deletions
+16 -3
View File
@@ -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;
});
```
+271 -75
View File
@@ -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 &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 +1149,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);
@@ -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 &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 +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 {
@@ -5544,7 +5695,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 +5851,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 +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 &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) {
+113 -3
View File
@@ -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,6 +979,65 @@ 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; });
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 +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) {
@@ -3954,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 =
@@ -3964,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