From 31e53d21e47d0427a56492221329c97f4ddbd7ea Mon Sep 17 00:00:00 2001 From: yhirose Date: Sun, 10 Dec 2017 22:34:37 -0500 Subject: [PATCH] Fixed #32 --- example/server.cc | 2 +- example/simplesvr.cc | 2 +- httplib.h | 47 ++++++++++++++++++++++++++++++++++---------- test/test.cc | 23 +++++++++++++++------- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/example/server.cc b/example/server.cc index 8e1f702..a4b7f6d 100644 --- a/example/server.cc +++ b/example/server.cc @@ -13,7 +13,7 @@ using namespace httplib; -std::string dump_headers(const MultiMap& headers) +std::string dump_headers(const Headers& headers) { std::string s; char buf[BUFSIZ]; diff --git a/example/simplesvr.cc b/example/simplesvr.cc index bcbe14d..13b6b66 100644 --- a/example/simplesvr.cc +++ b/example/simplesvr.cc @@ -15,7 +15,7 @@ using namespace httplib; using namespace std; -string dump_headers(const MultiMap& headers) +string dump_headers(const Headers& headers) { string s; char buf[BUFSIZ]; diff --git a/httplib.h b/httplib.h index 95d5a27..f72c9b4 100644 --- a/httplib.h +++ b/httplib.h @@ -73,9 +73,25 @@ typedef int socket_t; 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 }; -typedef std::multimap MultiMap; +typedef std::multimap Headers; +typedef std::multimap Params; typedef std::smatch Match; typedef std::function Progress; @@ -90,9 +106,9 @@ typedef std::multimap MultipartFiles; struct Request { std::string method; std::string path; - MultiMap headers; + Headers headers; std::string body; - MultiMap params; + Params params; MultipartFiles files; Match matches; Progress progress; @@ -110,7 +126,7 @@ struct Request { struct Response { int status; - MultiMap headers; + Headers headers; std::string body; bool has_header(const char* key) const; @@ -198,7 +214,7 @@ public: std::shared_ptr get(const char* path, Progress callback = [](int64_t,int64_t){}); std::shared_ptr head(const char* path); std::shared_ptr post(const char* path, const std::string& body, const char* content_type); - std::shared_ptr post(const char* path, const MultiMap& params); + std::shared_ptr post(const char* path, const Params& params); 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); if (it != headers.end()) { @@ -571,7 +587,7 @@ inline const char* get_header_value(const MultiMap& headers, const char* key, co 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); if (it != headers.end()) { @@ -580,7 +596,7 @@ inline int get_header_value_int(const MultiMap& headers, const char* key, int de return def; } -inline bool read_headers(Stream& strm, MultiMap& headers) +inline bool read_headers(Stream& strm, Headers& headers) { static std::regex re("(.+?): (.+?)\r\n"); @@ -853,7 +869,7 @@ inline std::string decode_url(const std::string& s) 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) { std::string key; @@ -980,6 +996,17 @@ public: static WSInit wsinit_; #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 // Request implementation @@ -1497,7 +1524,7 @@ inline std::shared_ptr Client::post( } inline std::shared_ptr Client::post( - const char* path, const MultiMap& params) + const char* path, const Params& params) { std::string query; for (auto it = params.begin(); it != params.end(); ++it) { diff --git a/test/test.cc b/test/test.cc index 2edf20e..abda551 100644 --- a/test/test.cc +++ b/test/test.cc @@ -32,7 +32,7 @@ TEST(StartupTest, WSAStartup) TEST(SplitTest, ParseQueryString) { 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) { string key, val; @@ -54,7 +54,7 @@ TEST(SplitTest, ParseQueryString) TEST(ParseQueryTest, ParseQueryString) { string s = "key1=val1&key2=val2&key3=val3"; - MultiMap dic; + Params dic; detail::parse_query_text(s, dic); @@ -83,28 +83,28 @@ TEST(SocketTest, OpenCloseWithAI_PASSIVE) TEST(GetHeaderValueTest, DefaultValue) { - MultiMap map = {{"Dummy","Dummy"}}; + Headers map = {{"Dummy","Dummy"}}; auto val = detail::get_header_value(map, "Content-Type", "text/plain"); ASSERT_STREQ("text/plain", val); } TEST(GetHeaderValueTest, DefaultValueInt) { - MultiMap map = {{"Dummy","Dummy"}}; + Headers map = {{"Dummy","Dummy"}}; auto val = detail::get_header_value_int(map, "Content-Length", 100); EXPECT_EQ(100, val); } 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"); ASSERT_STREQ("text/html", val); } 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); EXPECT_EQ(100, val); } @@ -310,7 +310,7 @@ TEST_F(ServerTest, PostMethod2) ASSERT_TRUE(res != nullptr); ASSERT_EQ(404, res->status); - MultiMap params; + Params params; params.emplace("name", "john2"); params.emplace("note", "coder"); @@ -512,6 +512,15 @@ TEST_F(ServerTest, MultipartFormData) 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 { protected: ServerTestWithAI_PASSIVE()