Browse Source

Only match path params that span full path segment (#1919)

* Only match path params that span full path segment

* Fix C++11 build
bgs99 1 year ago
parent
commit
80fb03628b
2 changed files with 19 additions and 5 deletions
  1. 7 5
      httplib.h
  2. 12 0
      test/test.cc

+ 7 - 5
httplib.h

@@ -840,7 +840,6 @@ public:
   bool match(Request &request) const override;
   bool match(Request &request) const override;
 
 
 private:
 private:
-  static constexpr char marker = ':';
   // Treat segment separators as the end of path parameter capture
   // Treat segment separators as the end of path parameter capture
   // Does not need to handle query parameters as they are parsed before path
   // Does not need to handle query parameters as they are parsed before path
   // matching
   // matching
@@ -5887,6 +5886,8 @@ inline socket_t BufferStream::socket() const { return 0; }
 inline const std::string &BufferStream::get_buffer() const { return buffer; }
 inline const std::string &BufferStream::get_buffer() const { return buffer; }
 
 
 inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
 inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
+  static constexpr char marker[] = "/:";
+
   // One past the last ending position of a path param substring
   // One past the last ending position of a path param substring
   std::size_t last_param_end = 0;
   std::size_t last_param_end = 0;
 
 
@@ -5899,13 +5900,14 @@ inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
 #endif
 #endif
 
 
   while (true) {
   while (true) {
-    const auto marker_pos = pattern.find(marker, last_param_end);
+    const auto marker_pos = pattern.find(
+        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
     if (marker_pos == std::string::npos) { break; }
     if (marker_pos == std::string::npos) { break; }
 
 
     static_fragments_.push_back(
     static_fragments_.push_back(
-        pattern.substr(last_param_end, marker_pos - last_param_end));
+        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
 
 
-    const auto param_name_start = marker_pos + 1;
+    const auto param_name_start = marker_pos + 2;
 
 
     auto sep_pos = pattern.find(separator, param_name_start);
     auto sep_pos = pattern.find(separator, param_name_start);
     if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
     if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
@@ -5967,7 +5969,7 @@ inline bool PathParamsMatcher::match(Request &request) const {
     request.path_params.emplace(
     request.path_params.emplace(
         param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
         param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
 
 
-    // Mark everythin up to '/' as matched
+    // Mark everything up to '/' as matched
     starting_pos = sep_pos + 1;
     starting_pos = sep_pos + 1;
   }
   }
   // Returns false if the path is longer than the pattern
   // Returns false if the path is longer than the pattern

+ 12 - 0
test/test.cc

@@ -7599,6 +7599,18 @@ TEST(PathParamsTest, SequenceOfParams) {
   EXPECT_EQ(request.path_params, expected_params);
   EXPECT_EQ(request.path_params, expected_params);
 }
 }
 
 
+TEST(PathParamsTest, SemicolonInTheMiddleIsNotAParam) {
+  const auto pattern = "/prefix:suffix";
+  detail::PathParamsMatcher matcher(pattern);
+
+  Request request;
+  request.path = "/prefix:suffix";
+  ASSERT_TRUE(matcher.match(request));
+
+  const std::unordered_map<std::string, std::string> expected_params = {};
+  EXPECT_EQ(request.path_params, expected_params);
+}
+
 TEST(UniversalClientImplTest, Ipv6LiteralAddress) {
 TEST(UniversalClientImplTest, Ipv6LiteralAddress) {
   // If ipv6 regex working, regex match codepath is taken.
   // If ipv6 regex working, regex match codepath is taken.
   // else port will default to 80 in Client impl
   // else port will default to 80 in Client impl