From aa75fbb5f9c853e1df44e4bc7a187882b46dce25 Mon Sep 17 00:00:00 2001 From: yhirose Date: Wed, 3 Oct 2012 20:11:22 -0400 Subject: [PATCH] Refactoring. --- Makefile | 4 +++ README.md | 2 +- example/client.cc | 14 +++++---- example/hello.cc | 2 +- example/server.cc | 45 +++++++++++++++------------ httplib.h | 77 ++++++++++++++++++++++++++--------------------- test/test.cc | 35 ++++++++++----------- 7 files changed, 100 insertions(+), 79 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aa86715 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ + +all: + make -C test + make -C example diff --git a/README.md b/README.md index c700e78..fd36337 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Inspired by [Sinatra](http://www.sinatrarb.com/) Server svr("localhost", 1234); svr.get("/hi", [](Connection& c) { - c.response.set_content("Hello World!"); + c.response.set_content("Hello World!", "text/plain"); }); svr.run(); diff --git a/example/client.cc b/example/client.cc index 9a30df9..5378749 100644 --- a/example/client.cc +++ b/example/client.cc @@ -6,21 +6,23 @@ // #include -#include -#include +#include +using namespace std; using namespace httplib; int main(void) { - using namespace httplib; - const char* hi = "/hi"; - Client cli("localhost", 1234); + Client cli("localhost", 8080); Response res; - cli.get(hi, res); + if (cli.get(hi, res)) { + cout << res.status << endl; + cout << res.get_header_value("Content-Type") << endl; + cout << res.body << endl; + } return 0; } diff --git a/example/hello.cc b/example/hello.cc index 414896c..58ba52f 100644 --- a/example/hello.cc +++ b/example/hello.cc @@ -13,7 +13,7 @@ int main(void) Server svr("localhost", 1234); svr.get("/hi", [](Connection& c) { - c.response.set_content("Hello World!"); + c.response.set_content("Hello World!", "text/plain"); }); svr.run(); diff --git a/example/server.cc b/example/server.cc index 858c833..20ebf12 100644 --- a/example/server.cc +++ b/example/server.cc @@ -7,14 +7,23 @@ #include #include -#include -template void signal(int sig, Fn fn) +#ifdef _WIN32 +#define snprintf sprintf_s +#endif + +std::string dump_headers(const httplib::MultiMap& headers) { - static std::function signal_handler_; - struct SignalHandler { static void fn(int sig) { signal_handler_(); } }; - signal_handler_ = fn; - signal(sig, SignalHandler::fn); + std::string s; + char buf[BUFSIZ]; + + for (auto it = headers.begin(); it != headers.end(); ++it) { + const auto& x = *it; + snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); + s += buf; + } + + return s; } std::string log(const httplib::Connection& c) @@ -40,13 +49,13 @@ std::string log(const httplib::Connection& c) snprintf(buf, sizeof(buf), "%s\n", query.c_str()); s += buf; - s += httplib::dump_headers(req.headers); + s += dump_headers(req.headers); s += "--------------------------------\n"; snprintf(buf, sizeof(buf), "%d\n", res.status); s += buf; - s += httplib::dump_headers(res.headers); + s += dump_headers(res.headers); if (!res.body.empty()) { s += res.body; @@ -57,13 +66,6 @@ std::string log(const httplib::Connection& c) return s; } -inline void error_handler(httplib::Connection& c) -{ - char buf[BUFSIZ]; - snprintf(buf, sizeof(buf), "Error Status: %d\r\n", c.response.status); - c.response.set_content(buf); -} - int main(void) { using namespace httplib; @@ -77,21 +79,24 @@ int main(void) }); svr.get("/hi", [](Connection& c) { - c.response.set_content("Hello World!"); + c.response.set_content("Hello World!", "text/plain"); }); svr.get("/dump", [](Connection& c) { - c.response.set_content(httplib::dump_headers(c.request.headers)); + c.response.set_content(dump_headers(c.request.headers), "text/plain"); }); - svr.error(error_handler); + 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"); + }); svr.set_logger([](const Connection& c) { printf("%s", log(c).c_str()); }); - signal(SIGINT, [&]() { svr.stop(); }); - svr.run(); return 0; diff --git a/httplib.h b/httplib.h index 9893bc2..ea55751 100644 --- a/httplib.h +++ b/httplib.h @@ -24,7 +24,6 @@ #include typedef SOCKET socket_t; -#define snprintf sprintf_s #else #include #include @@ -46,8 +45,8 @@ namespace httplib { typedef std::map Map; -typedef std::vector Array; typedef std::multimap MultiMap; +typedef std::smatch Match; struct Request { std::string method; @@ -55,7 +54,10 @@ struct Request { MultiMap headers; std::string body; Map query; - Array params; + Match match; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; }; struct Response { @@ -63,8 +65,12 @@ struct Response { MultiMap headers; std::string body; + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + void set_redirect(const char* url); - void set_content(const std::string& s, const char* content_type = "text/plain"); + void set_content(const std::string& s, const char* content_type); }; struct Connection { @@ -81,8 +87,8 @@ public: void get(const char* pattern, Handler handler); void post(const char* pattern, Handler handler); - void error(Handler handler); + void set_error_handler(Handler handler); void set_logger(std::function logger); bool run(); @@ -152,7 +158,7 @@ inline void get_flie_pointers(int fd, FILE*& fp_read, FILE*& fp_write) } template -inline socket_t create_socket(const char* host, int port, Fn fn) +socket_t create_socket(const char* host, int port, Fn fn) { #ifdef _WIN32 int opt = SO_SYNCHRONOUS_NONALERT; @@ -255,7 +261,7 @@ inline const char* status_message(int status) return s; } -inline const char* get_header_value(const MultiMap& map, const char* key, const char* def) +inline const char* get_header_value_text(const MultiMap& map, const char* key, const char* def) { auto it = map.find(key); if (it != map.end()) { @@ -290,31 +296,43 @@ inline void read_headers(FILE* fp, MultiMap& headers) } } -inline std::string dump_headers(const MultiMap& headers) +// HTTP server implementation +inline bool Request::has_header(const char* key) const { - std::string s; - char buf[BUFSIZ]; - - for (auto it = headers.begin(); it != headers.end(); ++it) { - const auto& x = *it; - snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); - s += buf; - } - - return s; + return headers.find(key) != headers.end(); +} + +inline std::string Request::get_header_value(const char* key) const +{ + return get_header_value_text(headers, key, ""); +} + +inline bool Response::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char* key) const +{ + return get_header_value_text(headers, key, ""); +} + +inline void Response::set_header(const char* key, const char* val) +{ + headers.insert(std::make_pair(key, val)); } -// HTTP server implementation inline void Response::set_redirect(const char* url) { - headers.insert(std::make_pair("Location", url)); + set_header("Location", url); status = 302; } inline void Response::set_content(const std::string& s, const char* content_type) { body = s; - headers.insert(std::make_pair("Content-Type", content_type)); + set_header("Content-Type", content_type); + status = 200; } inline Server::Server(const char* host, int port) @@ -345,7 +363,7 @@ inline void Server::post(const char* pattern, Handler handler) post_handlers_.push_back(std::make_pair(pattern, handler)); } -inline void Server::error(Handler handler) +inline void Server::set_error_handler(Handler handler) { error_handler_ = handler; } @@ -444,7 +462,7 @@ inline void Server::write_response(FILE* fp, const Response& res) } if (!res.body.empty()) { - auto content_type = get_header_value(res.headers, "Content-Type", "text/plain"); + auto content_type = get_header_value_text(res.headers, "Content-Type", "text/plain"); fprintf(fp, "Content-Type: %s\r\n", content_type); fprintf(fp, "Content-Length: %ld\r\n", res.body.size()); } @@ -474,22 +492,13 @@ inline void Server::process_request(FILE* fp_read, FILE* fp_write) const auto& pattern = it->first; const auto& handler = it->second; - std::smatch m; - if (std::regex_match(c.request.url, m, pattern)) { - for (size_t i = 1; i < m.size(); i++) { - c.request.params.push_back(m[i]); - } + if (std::regex_match(c.request.url, c.request.match, pattern)) { handler(c); - if (!c.response.status) { - c.response.status = 200; - } break; } } } else if (c.request.method == "POST") { // TODO: parse body - } else { - c.response.status = 400; } if (!c.response.status) { @@ -577,7 +586,7 @@ inline bool Client::get(const char* url, Response& res) close_client_socket(sock); - return res.status == 200; + return true; } } // namespace httplib diff --git a/test/test.cc b/test/test.cc index 665511d..81312dd 100644 --- a/test/test.cc +++ b/test/test.cc @@ -41,7 +41,7 @@ TEST(SocketTest, OpenClose) TEST(GetHeaderValueTest, DefaultValue) { MultiMap map = {{"Dummy","Dummy"}}; - auto val = get_header_value(map, "Content-Type", "text/plain"); + auto val = get_header_value_text(map, "Content-Type", "text/plain"); ASSERT_STREQ("text/plain", val); } @@ -55,7 +55,7 @@ TEST(GetHeaderValueTest, DefaultValueInt) TEST(GetHeaderValueTest, RegularValue) { MultiMap map = {{"Content-Type","text/html"}, {"Dummy", "Dummy"}}; - auto val = get_header_value(map, "Content-Type", "text/plain"); + auto val = get_header_value_text(map, "Content-Type", "text/plain"); ASSERT_STREQ("text/html", val); } @@ -68,43 +68,44 @@ TEST(GetHeaderValueTest, RegularValueInt) class ServerTest : public ::testing::Test { protected: - ServerTest() : svr(host, port) { + ServerTest() : svr_(host_, port_) { } virtual void SetUp() { - svr.get(url, [&](httplib::Connection& c) { - c.response.set_content(content); + svr_.get(url_, [&](httplib::Connection& c) { + c.response.set_content(content_, mime_); }); - f = async([&](){ svr.run(); }); + f_ = async([&](){ svr_.run(); }); } virtual void TearDown() { - svr.stop(); - f.get(); + svr_.stop(); + f_.get(); } - const char* host = "localhost"; - int port = 1914; - const char* url = "/hi"; - const char* content = "Hello World!"; + const char* host_ = "localhost"; + int port_ = 1914; + const char* url_ = "/hi"; + const char* content_ = "Hello World!"; + const char* mime_ = "text/plain"; - Server svr; - std::future f; + Server svr_; + std::future f_; }; TEST_F(ServerTest, GetMethod200) { Response res; - bool ret = Client(host, port).get(url, res); + bool ret = Client(host_, port_).get(url_, res); ASSERT_EQ(true, ret); ASSERT_EQ(200, res.status); - ASSERT_EQ(content, res.body); + ASSERT_EQ(content_, res.body); } TEST_F(ServerTest, GetMethod404) { Response res; - bool ret = Client(host, port).get("/invalid", res); + bool ret = Client(host_, port_).get("/invalid", res); ASSERT_EQ(false, ret); ASSERT_EQ(404, res.status); }