You've already forked cpp-httplib
							
							Fix query parsing when value has = characters (#1822)
				
					
				
			* Implement string divider to replace splitter * Divide query string in half * Add a test case for query values containing the '=' character * Add test cases for string divider * Fix warnings
This commit is contained in:
		
							
								
								
									
										80
									
								
								httplib.h
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								httplib.h
									
									
									
									
									
								
							@@ -2178,6 +2178,16 @@ void read_file(const std::string &path, std::string &out);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::string trim_copy(const std::string &s);
 | 
					std::string trim_copy(const std::string &s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void divide(
 | 
				
			||||||
 | 
					    const char *data, std::size_t size, char d,
 | 
				
			||||||
 | 
					    std::function<void(const char *, std::size_t, const char *, std::size_t)>
 | 
				
			||||||
 | 
					        fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void divide(
 | 
				
			||||||
 | 
					    const std::string &str, char d,
 | 
				
			||||||
 | 
					    std::function<void(const char *, std::size_t, const char *, std::size_t)>
 | 
				
			||||||
 | 
					        fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void split(const char *b, const char *e, char d,
 | 
					void split(const char *b, const char *e, char d,
 | 
				
			||||||
           std::function<void(const char *, const char *)> fn);
 | 
					           std::function<void(const char *, const char *)> fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2201,6 +2211,8 @@ const char *get_header_value(const Headers &headers, const std::string &key,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::string params_to_query_str(const Params ¶ms);
 | 
					std::string params_to_query_str(const Params ¶ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void parse_query_text(const char *data, std::size_t size, Params ¶ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void parse_query_text(const std::string &s, Params ¶ms);
 | 
					void parse_query_text(const std::string &s, Params ¶ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool parse_multipart_boundary(const std::string &content_type,
 | 
					bool parse_multipart_boundary(const std::string &content_type,
 | 
				
			||||||
@@ -2669,6 +2681,27 @@ inline std::string trim_double_quotes_copy(const std::string &s) {
 | 
				
			|||||||
  return s;
 | 
					  return s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void
 | 
				
			||||||
 | 
					divide(const char *data, std::size_t size, char d,
 | 
				
			||||||
 | 
					       std::function<void(const char *, std::size_t, const char *, std::size_t)>
 | 
				
			||||||
 | 
					           fn) {
 | 
				
			||||||
 | 
					  const auto it = std::find(data, data + size, d);
 | 
				
			||||||
 | 
					  const auto found = static_cast<std::size_t>(it != data + size);
 | 
				
			||||||
 | 
					  const auto lhs_data = data;
 | 
				
			||||||
 | 
					  const auto lhs_size = static_cast<std::size_t>(it - data);
 | 
				
			||||||
 | 
					  const auto rhs_data = it + found;
 | 
				
			||||||
 | 
					  const auto rhs_size = size - lhs_size - found;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fn(lhs_data, lhs_size, rhs_data, rhs_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void
 | 
				
			||||||
 | 
					divide(const std::string &str, char d,
 | 
				
			||||||
 | 
					       std::function<void(const char *, std::size_t, const char *, std::size_t)>
 | 
				
			||||||
 | 
					           fn) {
 | 
				
			||||||
 | 
					  divide(str.data(), str.size(), d, std::move(fn));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline void split(const char *b, const char *e, char d,
 | 
					inline void split(const char *b, const char *e, char d,
 | 
				
			||||||
                  std::function<void(const char *, const char *)> fn) {
 | 
					                  std::function<void(const char *, const char *)> fn) {
 | 
				
			||||||
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
 | 
					  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
 | 
				
			||||||
@@ -4392,21 +4425,21 @@ inline std::string params_to_query_str(const Params ¶ms) {
 | 
				
			|||||||
  return query;
 | 
					  return query;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline void parse_query_text(const std::string &s, Params ¶ms) {
 | 
					inline void parse_query_text(const char *data, std::size_t size,
 | 
				
			||||||
 | 
					                             Params ¶ms) {
 | 
				
			||||||
  std::set<std::string> cache;
 | 
					  std::set<std::string> cache;
 | 
				
			||||||
  split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
 | 
					  split(data, data + size, '&', [&](const char *b, const char *e) {
 | 
				
			||||||
    std::string kv(b, e);
 | 
					    std::string kv(b, e);
 | 
				
			||||||
    if (cache.find(kv) != cache.end()) { return; }
 | 
					    if (cache.find(kv) != cache.end()) { return; }
 | 
				
			||||||
    cache.insert(kv);
 | 
					    cache.insert(std::move(kv));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string key;
 | 
					    std::string key;
 | 
				
			||||||
    std::string val;
 | 
					    std::string val;
 | 
				
			||||||
    split(b, e, '=', [&](const char *b2, const char *e2) {
 | 
					    divide(b, static_cast<std::size_t>(e - b), '=',
 | 
				
			||||||
      if (key.empty()) {
 | 
					           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
 | 
				
			||||||
        key.assign(b2, e2);
 | 
					               std::size_t rhs_size) {
 | 
				
			||||||
      } else {
 | 
					             key.assign(lhs_data, lhs_size);
 | 
				
			||||||
        val.assign(b2, e2);
 | 
					             val.assign(rhs_data, rhs_size);
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
           });
 | 
					           });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!key.empty()) {
 | 
					    if (!key.empty()) {
 | 
				
			||||||
@@ -4415,6 +4448,10 @@ inline void parse_query_text(const std::string &s, Params ¶ms) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void parse_query_text(const std::string &s, Params ¶ms) {
 | 
				
			||||||
 | 
					  parse_query_text(s.data(), s.size(), params);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline bool parse_multipart_boundary(const std::string &content_type,
 | 
					inline bool parse_multipart_boundary(const std::string &content_type,
 | 
				
			||||||
                                     std::string &boundary) {
 | 
					                                     std::string &boundary) {
 | 
				
			||||||
  auto boundary_keyword = "boundary=";
 | 
					  auto boundary_keyword = "boundary=";
 | 
				
			||||||
@@ -6072,26 +6109,13 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_t count = 0;
 | 
					    detail::divide(req.target, '?',
 | 
				
			||||||
 | 
					                   [&](const char *lhs_data, std::size_t lhs_size,
 | 
				
			||||||
    detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
 | 
					                       const char *rhs_data, std::size_t rhs_size) {
 | 
				
			||||||
                  2, [&](const char *b, const char *e) {
 | 
					                     req.path = detail::decode_url(
 | 
				
			||||||
                    switch (count) {
 | 
					                         std::string(lhs_data, lhs_size), false);
 | 
				
			||||||
                    case 0:
 | 
					                     detail::parse_query_text(rhs_data, rhs_size, req.params);
 | 
				
			||||||
                      req.path = detail::decode_url(std::string(b, e), false);
 | 
					 | 
				
			||||||
                      break;
 | 
					 | 
				
			||||||
                    case 1: {
 | 
					 | 
				
			||||||
                      if (e - b > 0) {
 | 
					 | 
				
			||||||
                        detail::parse_query_text(std::string(b, e), req.params);
 | 
					 | 
				
			||||||
                      }
 | 
					 | 
				
			||||||
                      break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    default: break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    count++;
 | 
					 | 
				
			||||||
                   });
 | 
					                   });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (count > 2) { return false; }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								test/test.cc
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								test/test.cc
									
									
									
									
									
								
							@@ -116,6 +116,76 @@ TEST(TrimTests, TrimStringTests) {
 | 
				
			|||||||
  EXPECT_TRUE(detail::trim_copy("").empty());
 | 
					  EXPECT_TRUE(detail::trim_copy("").empty());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(DivideTest, DivideStringTests) {
 | 
				
			||||||
 | 
					  auto divide = [](const std::string &str, char d) {
 | 
				
			||||||
 | 
					    std::string lhs;
 | 
				
			||||||
 | 
					    std::string rhs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    detail::divide(str, d,
 | 
				
			||||||
 | 
					                   [&](const char *lhs_data, std::size_t lhs_size,
 | 
				
			||||||
 | 
					                       const char *rhs_data, std::size_t rhs_size) {
 | 
				
			||||||
 | 
					                     lhs.assign(lhs_data, lhs_size);
 | 
				
			||||||
 | 
					                     rhs.assign(rhs_data, rhs_size);
 | 
				
			||||||
 | 
					                   });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return std::make_pair(std::move(lhs), std::move(rhs));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("=", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide(" ", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, " ");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("a", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "a");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("a=", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "a");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("=b", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "b");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("a=b", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "a");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "b");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("a=b=", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "a");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "b=");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const auto res = divide("a=b=c", '=');
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.first, "a");
 | 
				
			||||||
 | 
					    EXPECT_EQ(res.second, "b=c");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(SplitTest, ParseQueryString) {
 | 
					TEST(SplitTest, ParseQueryString) {
 | 
				
			||||||
  string s = "key1=val1&key2=val2&key3=val3";
 | 
					  string s = "key1=val1&key2=val2&key3=val3";
 | 
				
			||||||
  Params dic;
 | 
					  Params dic;
 | 
				
			||||||
@@ -156,7 +226,8 @@ TEST(SplitTest, ParseInvalidQueryTests) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(ParseQueryTest, ParseQueryString) {
 | 
					TEST(ParseQueryTest, ParseQueryString) {
 | 
				
			||||||
  string s = "key1=val1&key2=val2&key3=val3";
 | 
					  {
 | 
				
			||||||
 | 
					    std::string s = "key1=val1&key2=val2&key3=val3";
 | 
				
			||||||
    Params dic;
 | 
					    Params dic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    detail::parse_query_text(s, dic);
 | 
					    detail::parse_query_text(s, dic);
 | 
				
			||||||
@@ -164,6 +235,19 @@ TEST(ParseQueryTest, ParseQueryString) {
 | 
				
			|||||||
    EXPECT_EQ("val1", dic.find("key1")->second);
 | 
					    EXPECT_EQ("val1", dic.find("key1")->second);
 | 
				
			||||||
    EXPECT_EQ("val2", dic.find("key2")->second);
 | 
					    EXPECT_EQ("val2", dic.find("key2")->second);
 | 
				
			||||||
    EXPECT_EQ("val3", dic.find("key3")->second);
 | 
					    EXPECT_EQ("val3", dic.find("key3")->second);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    std::string s = "key1&key2=val1&key3=val1=val2&key4=val1=val2=val3";
 | 
				
			||||||
 | 
					    Params dic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    detail::parse_query_text(s, dic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXPECT_EQ("", dic.find("key1")->second);
 | 
				
			||||||
 | 
					    EXPECT_EQ("val1", dic.find("key2")->second);
 | 
				
			||||||
 | 
					    EXPECT_EQ("val1=val2", dic.find("key3")->second);
 | 
				
			||||||
 | 
					    EXPECT_EQ("val1=val2=val3", dic.find("key4")->second);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(ParamsToQueryTest, ConvertParamsToQuery) {
 | 
					TEST(ParamsToQueryTest, ConvertParamsToQuery) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user