diff --git a/example/server.cc b/example/server.cc
index 20ebf12..61eac0d 100644
--- a/example/server.cc
+++ b/example/server.cc
@@ -89,8 +89,7 @@ int main(void)
svr.set_error_handler([](httplib::Connection& c) {
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), "
Error Status: %d
", c.response.status);
- c.response.body = buf;
- c.response.set_header("Content-Type", "text/html");
+ c.response.set_content(buf, "text/html");
});
svr.set_logger([](const Connection& c) {
diff --git a/httplib.h b/httplib.h
index ea55751..0b6e9a9 100644
--- a/httplib.h
+++ b/httplib.h
@@ -89,25 +89,27 @@ public:
void post(const char* pattern, Handler handler);
void set_error_handler(Handler handler);
- void set_logger(std::function logger);
+ void set_logger(Handler logger);
bool run();
void stop();
private:
- void process_request(FILE* fp_read, FILE* fp_write);
+ typedef std::vector> Handlers;
+ void process_request(FILE* fp_read, FILE* fp_write);
bool read_request_line(FILE* fp, Request& req);
void write_response(FILE* fp, const Response& res);
+ void dispatch_request(Connection& c, Handlers& handlers);
const std::string host_;
const int port_;
socket_t sock_;
- std::vector> get_handlers_;
- std::vector> post_handlers_;
- Handler error_handler_;
- std::function logger_;
+ Handlers get_handlers_;
+ Handlers post_handlers_;
+ Handler error_handler_;
+ Handler logger_;
};
class Client {
@@ -279,14 +281,20 @@ inline int get_header_value_int(const MultiMap& map, const char* key, int def)
return def;
}
-inline void read_headers(FILE* fp, MultiMap& headers)
+inline bool read_headers(FILE* fp, MultiMap& headers)
{
static std::regex re("(.+?): (.+?)\r\n");
const size_t BUFSIZ_HEADER = 2048;
char buf[BUFSIZ_HEADER];
- while (fgets(buf, BUFSIZ_HEADER, fp) && strcmp(buf, "\r\n")) {
+ for (;;) {
+ if (!fgets(buf, BUFSIZ_HEADER, fp)) {
+ return false;
+ }
+ if (!strcmp(buf, "\r\n")) {
+ break;
+ }
std::cmatch m;
if (std::regex_match(buf, m, re)) {
auto key = std::string(m[1]);
@@ -294,6 +302,21 @@ inline void read_headers(FILE* fp, MultiMap& headers)
headers.insert(std::make_pair(key, val));
}
}
+
+ return true;
+}
+
+template
+bool read_content(T& x, FILE* fp)
+{
+ auto len = get_header_value_int(x.headers, "Content-Length", 0);
+ if (len) {
+ x.body.assign(len, 0);
+ if (!fgets(&x.body[0], x.body.size() + 1, fp)) {
+ return false;
+ }
+ }
+ return true;
}
// HTTP server implementation
@@ -332,7 +355,6 @@ inline void Response::set_content(const std::string& s, const char* content_type
{
body = s;
set_header("Content-Type", content_type);
- status = 200;
}
inline Server::Server(const char* host, int port)
@@ -368,7 +390,7 @@ inline void Server::set_error_handler(Handler handler)
error_handler_ = handler;
}
-inline void Server::set_logger(std::function logger)
+inline void Server::set_logger(Handler logger)
{
logger_ = logger;
}
@@ -474,48 +496,57 @@ inline void Server::write_response(FILE* fp, const Response& res)
}
}
+inline void Server::dispatch_request(Connection& c, Handlers& handlers)
+{
+ for (auto it = handlers.begin(); it != handlers.end(); ++it) {
+ const auto& pattern = it->first;
+ const auto& handler = it->second;
+
+ if (std::regex_match(c.request.url, c.request.match, pattern)) {
+ handler(c);
+
+ if (!c.response.status) {
+ c.response.status = 200;
+ }
+ break;
+ }
+ }
+}
+
inline void Server::process_request(FILE* fp_read, FILE* fp_write)
{
Connection c;
- if (!read_request_line(fp_read, c.request)) {
+ if (!read_request_line(fp_read, c.request) ||
+ !read_headers(fp_read, c.request.headers)) {
return;
}
-
- read_headers(fp_read, c.request.headers);
// Routing
c.response.status = 0;
if (c.request.method == "GET") {
- for (auto it = get_handlers_.begin(); it != get_handlers_.end(); ++it) {
- const auto& pattern = it->first;
- const auto& handler = it->second;
-
- if (std::regex_match(c.request.url, c.request.match, pattern)) {
- handler(c);
- break;
- }
- }
+ dispatch_request(c, get_handlers_);
} else if (c.request.method == "POST") {
- // TODO: parse body
+ if (!read_content(c.request, fp_read)) {
+ return;
+ }
+ dispatch_request(c, post_handlers_);
}
if (!c.response.status) {
c.response.status = 404;
}
- if (400 <= c.response.status) {
- if (error_handler_) {
- error_handler_(c);
- }
+ if (400 <= c.response.status && error_handler_) {
+ error_handler_(c);
}
+ write_response(fp_write, c.response);
+
if (logger_) {
logger_(c);
}
-
- write_response(fp_write, c.response);
}
// HTTP client implementation
@@ -569,21 +600,12 @@ inline bool Client::get(const char* url, Response& res)
fprintf(fp_write, "GET %s HTTP/1.0\r\n\r\n", url);
fflush(fp_write);
- if (!read_response_line(fp_read, res)) {
+ if (!read_response_line(fp_read, res) ||
+ !read_headers(fp_read, res.headers) ||
+ !read_content(res, fp_read)) {
return false;
}
- read_headers(fp_read, res.headers);
-
- // Read content body
- auto len = get_header_value_int(res.headers, "Content-Length", 0);
- if (len) {
- res.body.assign(len, 0);
- if (!fgets(&res.body[0], res.body.size() + 1, fp_read)) {
- return false;
- }
- }
-
close_client_socket(sock);
return true;
diff --git a/test/test.cc b/test/test.cc
index 81312dd..bbc77ee 100644
--- a/test/test.cc
+++ b/test/test.cc
@@ -72,8 +72,11 @@ protected:
}
virtual void SetUp() {
- svr_.get(url_, [&](httplib::Connection& c) {
- c.response.set_content(content_, mime_);
+ svr_.get("/hi", [&](httplib::Connection& c) {
+ c.response.set_content("Hello World!", "text/plain");
+ });
+ svr_.get("/", [&](httplib::Connection& c) {
+ c.response.set_redirect("/hi");
});
f_ = async([&](){ svr_.run(); });
}
@@ -83,12 +86,8 @@ protected:
f_.get();
}
- const char* host_ = "localhost";
- int port_ = 1914;
- const char* url_ = "/hi";
- const char* content_ = "Hello World!";
- const char* mime_ = "text/plain";
-
+ const char* host_ = "localhost";
+ int port_ = 1914;
Server svr_;
std::future f_;
};
@@ -96,17 +95,27 @@ protected:
TEST_F(ServerTest, GetMethod200)
{
Response res;
- bool ret = Client(host_, port_).get(url_, res);
+ bool ret = Client(host_, port_).get("/hi", res);
ASSERT_EQ(true, ret);
ASSERT_EQ(200, res.status);
- ASSERT_EQ(content_, res.body);
+ ASSERT_EQ("text/plain", res.get_header_value("Content-Type"));
+ ASSERT_EQ("Hello World!", res.body);
+}
+
+TEST_F(ServerTest, GetMethod302)
+{
+ Response res;
+ bool ret = Client(host_, port_).get("/", res);
+ ASSERT_EQ(true, ret);
+ ASSERT_EQ(302, res.status);
+ ASSERT_EQ("/hi", res.get_header_value("Location"));
}
TEST_F(ServerTest, GetMethod404)
{
Response res;
bool ret = Client(host_, port_).get("/invalid", res);
- ASSERT_EQ(false, ret);
+ ASSERT_EQ(true, ret);
ASSERT_EQ(404, res.status);
}