Refactoring.
This commit is contained in:
parent
aa75fbb5f9
commit
ffde8b7e4b
@ -89,8 +89,7 @@ int main(void)
|
|||||||
svr.set_error_handler([](httplib::Connection& c) {
|
svr.set_error_handler([](httplib::Connection& c) {
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
snprintf(buf, sizeof(buf), "<p>Error Status: <span style='color:red;'>%d</span></p>", c.response.status);
|
snprintf(buf, sizeof(buf), "<p>Error Status: <span style='color:red;'>%d</span></p>", c.response.status);
|
||||||
c.response.body = buf;
|
c.response.set_content(buf, "text/html");
|
||||||
c.response.set_header("Content-Type", "text/html");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.set_logger([](const Connection& c) {
|
svr.set_logger([](const Connection& c) {
|
||||||
|
104
httplib.h
104
httplib.h
@ -89,25 +89,27 @@ public:
|
|||||||
void post(const char* pattern, Handler handler);
|
void post(const char* pattern, Handler handler);
|
||||||
|
|
||||||
void set_error_handler(Handler handler);
|
void set_error_handler(Handler handler);
|
||||||
void set_logger(std::function<void (const Connection&)> logger);
|
void set_logger(Handler logger);
|
||||||
|
|
||||||
bool run();
|
bool run();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_request(FILE* fp_read, FILE* fp_write);
|
typedef std::vector<std::pair<std::regex, Handler>> Handlers;
|
||||||
|
|
||||||
|
void process_request(FILE* fp_read, FILE* fp_write);
|
||||||
bool read_request_line(FILE* fp, Request& req);
|
bool read_request_line(FILE* fp, Request& req);
|
||||||
void write_response(FILE* fp, const Response& res);
|
void write_response(FILE* fp, const Response& res);
|
||||||
|
void dispatch_request(Connection& c, Handlers& handlers);
|
||||||
|
|
||||||
const std::string host_;
|
const std::string host_;
|
||||||
const int port_;
|
const int port_;
|
||||||
socket_t sock_;
|
socket_t sock_;
|
||||||
|
|
||||||
std::vector<std::pair<std::regex, Handler>> get_handlers_;
|
Handlers get_handlers_;
|
||||||
std::vector<std::pair<std::string, Handler>> post_handlers_;
|
Handlers post_handlers_;
|
||||||
Handler error_handler_;
|
Handler error_handler_;
|
||||||
std::function<void (const Connection&)> logger_;
|
Handler logger_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
@ -279,14 +281,20 @@ inline int get_header_value_int(const MultiMap& map, const char* key, int def)
|
|||||||
return 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");
|
static std::regex re("(.+?): (.+?)\r\n");
|
||||||
|
|
||||||
const size_t BUFSIZ_HEADER = 2048;
|
const size_t BUFSIZ_HEADER = 2048;
|
||||||
char buf[BUFSIZ_HEADER];
|
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;
|
std::cmatch m;
|
||||||
if (std::regex_match(buf, m, re)) {
|
if (std::regex_match(buf, m, re)) {
|
||||||
auto key = std::string(m[1]);
|
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));
|
headers.insert(std::make_pair(key, val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
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
|
// HTTP server implementation
|
||||||
@ -332,7 +355,6 @@ inline void Response::set_content(const std::string& s, const char* content_type
|
|||||||
{
|
{
|
||||||
body = s;
|
body = s;
|
||||||
set_header("Content-Type", content_type);
|
set_header("Content-Type", content_type);
|
||||||
status = 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Server::Server(const char* host, int port)
|
inline Server::Server(const char* host, int port)
|
||||||
@ -368,7 +390,7 @@ inline void Server::set_error_handler(Handler handler)
|
|||||||
error_handler_ = handler;
|
error_handler_ = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Server::set_logger(std::function<void (const Connection&)> logger)
|
inline void Server::set_logger(Handler logger)
|
||||||
{
|
{
|
||||||
logger_ = 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)
|
inline void Server::process_request(FILE* fp_read, FILE* fp_write)
|
||||||
{
|
{
|
||||||
Connection c;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_headers(fp_read, c.request.headers);
|
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
c.response.status = 0;
|
c.response.status = 0;
|
||||||
|
|
||||||
if (c.request.method == "GET") {
|
if (c.request.method == "GET") {
|
||||||
for (auto it = get_handlers_.begin(); it != get_handlers_.end(); ++it) {
|
dispatch_request(c, get_handlers_);
|
||||||
const auto& pattern = it->first;
|
|
||||||
const auto& handler = it->second;
|
|
||||||
|
|
||||||
if (std::regex_match(c.request.url, c.request.match, pattern)) {
|
|
||||||
handler(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (c.request.method == "POST") {
|
} 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) {
|
if (!c.response.status) {
|
||||||
c.response.status = 404;
|
c.response.status = 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (400 <= c.response.status) {
|
if (400 <= c.response.status && error_handler_) {
|
||||||
if (error_handler_) {
|
error_handler_(c);
|
||||||
error_handler_(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_response(fp_write, c.response);
|
||||||
|
|
||||||
if (logger_) {
|
if (logger_) {
|
||||||
logger_(c);
|
logger_(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
write_response(fp_write, c.response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP client implementation
|
// 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);
|
fprintf(fp_write, "GET %s HTTP/1.0\r\n\r\n", url);
|
||||||
fflush(fp_write);
|
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;
|
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);
|
close_client_socket(sock);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
31
test/test.cc
31
test/test.cc
@ -72,8 +72,11 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
svr_.get(url_, [&](httplib::Connection& c) {
|
svr_.get("/hi", [&](httplib::Connection& c) {
|
||||||
c.response.set_content(content_, mime_);
|
c.response.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
svr_.get("/", [&](httplib::Connection& c) {
|
||||||
|
c.response.set_redirect("/hi");
|
||||||
});
|
});
|
||||||
f_ = async([&](){ svr_.run(); });
|
f_ = async([&](){ svr_.run(); });
|
||||||
}
|
}
|
||||||
@ -83,12 +86,8 @@ protected:
|
|||||||
f_.get();
|
f_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* host_ = "localhost";
|
const char* host_ = "localhost";
|
||||||
int port_ = 1914;
|
int port_ = 1914;
|
||||||
const char* url_ = "/hi";
|
|
||||||
const char* content_ = "Hello World!";
|
|
||||||
const char* mime_ = "text/plain";
|
|
||||||
|
|
||||||
Server svr_;
|
Server svr_;
|
||||||
std::future<void> f_;
|
std::future<void> f_;
|
||||||
};
|
};
|
||||||
@ -96,17 +95,27 @@ protected:
|
|||||||
TEST_F(ServerTest, GetMethod200)
|
TEST_F(ServerTest, GetMethod200)
|
||||||
{
|
{
|
||||||
Response res;
|
Response res;
|
||||||
bool ret = Client(host_, port_).get(url_, res);
|
bool ret = Client(host_, port_).get("/hi", res);
|
||||||
ASSERT_EQ(true, ret);
|
ASSERT_EQ(true, ret);
|
||||||
ASSERT_EQ(200, res.status);
|
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)
|
TEST_F(ServerTest, GetMethod404)
|
||||||
{
|
{
|
||||||
Response res;
|
Response res;
|
||||||
bool ret = Client(host_, port_).get("/invalid", res);
|
bool ret = Client(host_, port_).get("/invalid", res);
|
||||||
ASSERT_EQ(false, ret);
|
ASSERT_EQ(true, ret);
|
||||||
ASSERT_EQ(404, res.status);
|
ASSERT_EQ(404, res.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user