Browse Source

Fix: Query parameter including query delimiter ('?') not being parsed properly (#1713)

* Fix: Query parameter including query delimiter ('?') not being parsed properly

* Add details::split function with and without m argument to allow split parameters with/without counter

* Revert changes in SplitTest.ParseQueryString
davidalo 2 years ago
parent
commit
e426a38c3e
2 changed files with 26 additions and 2 deletions
  1. 13 2
      httplib.h
  2. 13 0
      test/test.cc

+ 13 - 2
httplib.h

@@ -1989,8 +1989,12 @@ void read_file(const std::string &path, std::string &out);
 std::string trim_copy(const std::string &s);
 
 void split(const char *b, const char *e, char d,
+  std::function<void(const char *, const char *)> fn);
+
+void split(const char *b, const char *e, char d, size_t m,
            std::function<void(const char *, const char *)> fn);
 
+
 bool process_client_socket(socket_t sock, time_t read_timeout_sec,
                            time_t read_timeout_usec, time_t write_timeout_sec,
                            time_t write_timeout_usec,
@@ -2473,14 +2477,21 @@ inline std::string trim_double_quotes_copy(const std::string &s) {
 
 inline void split(const char *b, const char *e, char d,
                   std::function<void(const char *, const char *)> fn) {
+  return split(b, e, d, std::numeric_limits<size_t>::max(), fn);
+}
+
+inline void split(const char *b, const char *e, char d, size_t m,
+                  std::function<void(const char *, const char *)> fn) {
   size_t i = 0;
   size_t beg = 0;
+  size_t count = 1;
 
   while (e ? (b + i < e) : (b[i] != '\0')) {
-    if (b[i] == d) {
+    if (b[i] == d && count < m) {
       auto r = trim(b, e, beg, i);
       if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
       beg = i + 1;
+      count++;
     }
     i++;
   }
@@ -5804,7 +5815,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
 
     size_t count = 0;
 
-    detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
+    detail::split(req.target.data(), req.target.data() + req.target.size(), '?', 2,
                   [&](const char *b, const char *e) {
                     switch (count) {
                     case 0:

+ 13 - 0
test/test.cc

@@ -1847,6 +1847,11 @@ protected:
                      return true;
                    });
              })
+        .Get("/regex-with-delimiter",
+             [&](const Request & req, Response &res) {
+                ASSERT_TRUE(req.has_param("key"));
+                EXPECT_EQ("^(?.*(value))", req.get_param_value("key"));
+             })
         .Get("/with-range",
              [&](const Request & /*req*/, Response &res) {
                res.set_content("abcdefg", "text/plain");
@@ -3352,6 +3357,14 @@ TEST_F(ServerTest, GetStreamedChunkedWithGzip2) {
   EXPECT_EQ(std::string("123456789"), res->body);
 }
 
+
+TEST_F(ServerTest, SplitDelimiterInPathRegex) {
+  auto res = cli_.Get("/regex-with-delimiter?key=^(?.*(value))");
+  ASSERT_TRUE(res);
+  EXPECT_EQ(200, res->status);
+}
+
+
 TEST(GzipDecompressor, ChunkedDecompression) {
   std::string data;
   for (size_t i = 0; i < 32 * 1024; ++i) {