yhirose 8 years ago
parent
commit
31e53d21e4
4 changed files with 55 additions and 19 deletions
  1. 1 1
      example/server.cc
  2. 1 1
      example/simplesvr.cc
  3. 37 10
      httplib.h
  4. 16 7
      test/test.cc

+ 1 - 1
example/server.cc

@@ -13,7 +13,7 @@
 
 
 using namespace httplib;
 using namespace httplib;
 
 
-std::string dump_headers(const MultiMap& headers)
+std::string dump_headers(const Headers& headers)
 {
 {
     std::string s;
     std::string s;
     char buf[BUFSIZ];
     char buf[BUFSIZ];

+ 1 - 1
example/simplesvr.cc

@@ -15,7 +15,7 @@
 using namespace httplib;
 using namespace httplib;
 using namespace std;
 using namespace std;
 
 
-string dump_headers(const MultiMap& headers)
+string dump_headers(const Headers& headers)
 {
 {
     string s;
     string s;
     char buf[BUFSIZ];
     char buf[BUFSIZ];

+ 37 - 10
httplib.h

@@ -73,9 +73,25 @@ typedef int socket_t;
 namespace httplib
 namespace httplib
 {
 {
 
 
+namespace detail {
+
+struct ci {
+    bool operator() (const std::string & s1, const std::string & s2) const {
+        return std::lexicographical_compare(
+            s1.begin(), s1.end(),
+            s2.begin(), s2.end(),
+            [](char c1, char c2) {
+                return std::tolower(c1) < std::tolower(c2);
+            });
+    }
+};
+
+} // namespace detail
+
 enum class HttpVersion { v1_0 = 0, v1_1 };
 enum class HttpVersion { v1_0 = 0, v1_1 };
 
 
-typedef std::multimap<std::string, std::string>              MultiMap;
+typedef std::multimap<std::string, std::string, detail::ci>  Headers;
+typedef std::multimap<std::string, std::string>              Params;
 typedef std::smatch                                          Match;
 typedef std::smatch                                          Match;
 typedef std::function<void (int64_t current, int64_t total)> Progress;
 typedef std::function<void (int64_t current, int64_t total)> Progress;
 
 
@@ -90,9 +106,9 @@ typedef std::multimap<std::string, MultipartFile> MultipartFiles;
 struct Request {
 struct Request {
     std::string    method;
     std::string    method;
     std::string    path;
     std::string    path;
-    MultiMap       headers;
+    Headers        headers;
     std::string    body;
     std::string    body;
-    MultiMap       params;
+    Params         params;
     MultipartFiles files;
     MultipartFiles files;
     Match          matches;
     Match          matches;
     Progress       progress;
     Progress       progress;
@@ -110,7 +126,7 @@ struct Request {
 
 
 struct Response {
 struct Response {
     int         status;
     int         status;
-    MultiMap    headers;
+    Headers     headers;
     std::string body;
     std::string body;
 
 
     bool has_header(const char* key) const;
     bool has_header(const char* key) const;
@@ -198,7 +214,7 @@ public:
     std::shared_ptr<Response> get(const char* path, Progress callback = [](int64_t,int64_t){});
     std::shared_ptr<Response> get(const char* path, Progress callback = [](int64_t,int64_t){});
     std::shared_ptr<Response> head(const char* path);
     std::shared_ptr<Response> head(const char* path);
     std::shared_ptr<Response> post(const char* path, const std::string& body, const char* content_type);
     std::shared_ptr<Response> post(const char* path, const std::string& body, const char* content_type);
-    std::shared_ptr<Response> post(const char* path, const MultiMap& params);
+    std::shared_ptr<Response> post(const char* path, const Params& params);
 
 
     bool send(const Request& req, Response& res);
     bool send(const Request& req, Response& res);
 
 
@@ -562,7 +578,7 @@ inline const char* status_message(int status)
     }
     }
 }
 }
 
 
-inline const char* get_header_value(const MultiMap& headers, const char* key, const char* def)
+inline const char* get_header_value(const Headers& headers, const char* key, const char* def)
 {
 {
     auto it = headers.find(key);
     auto it = headers.find(key);
     if (it != headers.end()) {
     if (it != headers.end()) {
@@ -571,7 +587,7 @@ inline const char* get_header_value(const MultiMap& headers, const char* key, co
     return def;
     return def;
 }
 }
 
 
-inline int get_header_value_int(const MultiMap& headers, const char* key, int def)
+inline int get_header_value_int(const Headers& headers, const char* key, int def)
 {
 {
     auto it = headers.find(key);
     auto it = headers.find(key);
     if (it != headers.end()) {
     if (it != headers.end()) {
@@ -580,7 +596,7 @@ inline int get_header_value_int(const MultiMap& headers, const char* key, int de
     return def;
     return def;
 }
 }
 
 
-inline bool read_headers(Stream& strm, MultiMap& headers)
+inline bool read_headers(Stream& strm, Headers& headers)
 {
 {
     static std::regex re("(.+?): (.+?)\r\n");
     static std::regex re("(.+?): (.+?)\r\n");
 
 
@@ -853,7 +869,7 @@ inline std::string decode_url(const std::string& s)
     return result;
     return result;
 }
 }
 
 
-inline void parse_query_text(const std::string& s, MultiMap& params)
+inline void parse_query_text(const std::string& s, Params& params)
 {
 {
     split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) {
     split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) {
         std::string key;
         std::string key;
@@ -980,6 +996,17 @@ public:
 static WSInit wsinit_;
 static WSInit wsinit_;
 #endif
 #endif
 
 
+inline std::string to_lower(const char* beg, const char* end)
+{
+    std::string out;
+    auto it = beg;
+    while (it != end) {
+        out += ::tolower(*it);
+        it++;
+    }
+    return out;
+}
+
 } // namespace detail
 } // namespace detail
 
 
 // Request implementation
 // Request implementation
@@ -1497,7 +1524,7 @@ inline std::shared_ptr<Response> Client::post(
 }
 }
 
 
 inline std::shared_ptr<Response> Client::post(
 inline std::shared_ptr<Response> Client::post(
-    const char* path, const MultiMap& params)
+    const char* path, const Params& params)
 {
 {
     std::string query;
     std::string query;
     for (auto it = params.begin(); it != params.end(); ++it) {
     for (auto it = params.begin(); it != params.end(); ++it) {

+ 16 - 7
test/test.cc

@@ -32,7 +32,7 @@ TEST(StartupTest, WSAStartup)
 TEST(SplitTest, ParseQueryString)
 TEST(SplitTest, ParseQueryString)
 {
 {
     string s = "key1=val1&key2=val2&key3=val3";
     string s = "key1=val1&key2=val2&key3=val3";
-    MultiMap dic;
+    Params dic;
 
 
     detail::split(s.c_str(), s.c_str() + s.size(), '&', [&](const char* b, const char* e) {
     detail::split(s.c_str(), s.c_str() + s.size(), '&', [&](const char* b, const char* e) {
         string key, val;
         string key, val;
@@ -54,7 +54,7 @@ TEST(SplitTest, ParseQueryString)
 TEST(ParseQueryTest, ParseQueryString)
 TEST(ParseQueryTest, ParseQueryString)
 {
 {
     string s = "key1=val1&key2=val2&key3=val3";
     string s = "key1=val1&key2=val2&key3=val3";
-    MultiMap dic;
+    Params dic;
 
 
     detail::parse_query_text(s, dic);
     detail::parse_query_text(s, dic);
 
 
@@ -83,28 +83,28 @@ TEST(SocketTest, OpenCloseWithAI_PASSIVE)
 
 
 TEST(GetHeaderValueTest, DefaultValue)
 TEST(GetHeaderValueTest, DefaultValue)
 {
 {
-    MultiMap map = {{"Dummy","Dummy"}};
+    Headers map = {{"Dummy","Dummy"}};
     auto val = detail::get_header_value(map, "Content-Type", "text/plain");
     auto val = detail::get_header_value(map, "Content-Type", "text/plain");
     ASSERT_STREQ("text/plain", val);
     ASSERT_STREQ("text/plain", val);
 }
 }
 
 
 TEST(GetHeaderValueTest, DefaultValueInt)
 TEST(GetHeaderValueTest, DefaultValueInt)
 {
 {
-    MultiMap map = {{"Dummy","Dummy"}};
+    Headers map = {{"Dummy","Dummy"}};
     auto val = detail::get_header_value_int(map, "Content-Length", 100);
     auto val = detail::get_header_value_int(map, "Content-Length", 100);
     EXPECT_EQ(100, val);
     EXPECT_EQ(100, val);
 }
 }
 
 
 TEST(GetHeaderValueTest, RegularValue)
 TEST(GetHeaderValueTest, RegularValue)
 {
 {
-    MultiMap map = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}};
+    Headers map = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}};
     auto val = detail::get_header_value(map, "Content-Type", "text/plain");
     auto val = detail::get_header_value(map, "Content-Type", "text/plain");
     ASSERT_STREQ("text/html", val);
     ASSERT_STREQ("text/html", val);
 }
 }
 
 
 TEST(GetHeaderValueTest, RegularValueInt)
 TEST(GetHeaderValueTest, RegularValueInt)
 {
 {
-    MultiMap map = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
+    Headers map = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
     auto val = detail::get_header_value_int(map, "Content-Length", 0);
     auto val = detail::get_header_value_int(map, "Content-Length", 0);
     EXPECT_EQ(100, val);
     EXPECT_EQ(100, val);
 }
 }
@@ -310,7 +310,7 @@ TEST_F(ServerTest, PostMethod2)
     ASSERT_TRUE(res != nullptr);
     ASSERT_TRUE(res != nullptr);
     ASSERT_EQ(404, res->status);
     ASSERT_EQ(404, res->status);
 
 
-    MultiMap params;
+    Params params;
     params.emplace("name", "john2");
     params.emplace("name", "john2");
     params.emplace("note", "coder");
     params.emplace("note", "coder");
 
 
@@ -512,6 +512,15 @@ TEST_F(ServerTest, MultipartFormData)
 	EXPECT_EQ(200, res->status);
 	EXPECT_EQ(200, res->status);
 }
 }
 
 
+TEST_F(ServerTest, CaseInsensitiveHeaderName)
+{
+    auto res = cli_.get("/hi");
+    ASSERT_TRUE(res != nullptr);
+    EXPECT_EQ(200, res->status);
+    EXPECT_EQ("text/plain", res->get_header_value("content-type"));
+    EXPECT_EQ("Hello World!", res->body);
+}
+
 class ServerTestWithAI_PASSIVE : public ::testing::Test {
 class ServerTestWithAI_PASSIVE : public ::testing::Test {
 protected:
 protected:
     ServerTestWithAI_PASSIVE()
     ServerTestWithAI_PASSIVE()