Browse Source

Refactoring.

yhirose 13 years ago
parent
commit
aa75fbb5f9
7 changed files with 98 additions and 77 deletions
  1. 4 0
      Makefile
  2. 1 1
      README.md
  3. 8 6
      example/client.cc
  4. 1 1
      example/hello.cc
  5. 25 20
      example/server.cc
  6. 41 32
      httplib.h
  7. 18 17
      test/test.cc

+ 4 - 0
Makefile

@@ -0,0 +1,4 @@
+
+all:
+	make -C test
+	make -C example

+ 1 - 1
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();

+ 8 - 6
example/client.cc

@@ -6,21 +6,23 @@
 //
 
 #include <httplib.h>
-#include <cstdio>
-#include <signal.h>
+#include <iostream>
 
+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;
 }

+ 1 - 1
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();

+ 25 - 20
example/server.cc

@@ -7,14 +7,23 @@
 
 #include <httplib.h>
 #include <cstdio>
-#include <signal.h>
 
-template<typename Fn> void signal(int sig, Fn fn)
+#ifdef _WIN32
+#define snprintf sprintf_s
+#endif
+
+std::string dump_headers(const httplib::MultiMap& headers)
 {
-    static std::function<void ()> 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), "<p>Error Status: <span style='color:red;'>%d</span></p>", 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;

+ 41 - 32
httplib.h

@@ -24,7 +24,6 @@
 #include <winsock2.h>
 
 typedef SOCKET socket_t;
-#define snprintf sprintf_s
 #else
 #include <pthread.h>
 #include <unistd.h>
@@ -46,8 +45,8 @@ namespace httplib
 {
 
 typedef std::map<std::string, std::string>      Map;
-typedef std::vector<std::string>                Array;
 typedef std::multimap<std::string, std::string> 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<void (const Connection&)> logger);
 
     bool run();
@@ -152,7 +158,7 @@ inline void get_flie_pointers(int fd, FILE*& fp_read, FILE*& fp_write)
 }
 
 template <typename Fn>
-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];
+    return headers.find(key) != headers.end();
+}
 
-    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;
-    }
+inline std::string Request::get_header_value(const char* key) const
+{
+    return get_header_value_text(headers, key, "");
+}
 
-    return s;
+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

+ 18 - 17
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<void> f;
+    Server            svr_;
+    std::future<void> 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);
 }