1
0
mirror of synced 2025-04-19 00:24:02 +03:00
This commit is contained in:
yhirose 2017-12-10 22:34:37 -05:00
parent 459f197ed0
commit 31e53d21e4
4 changed files with 55 additions and 19 deletions

View File

@ -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];

View File

@ -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];

View File

@ -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) {

View File

@ -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()