Add method to parse parameter list in query string to folly::Uri
authorJun LI <albertli@fb.com>
Fri, 25 Jul 2014 00:07:36 +0000 (17:07 -0700)
committerChip Turner <chip@fb.com>
Fri, 25 Jul 2014 16:07:17 +0000 (09:07 -0700)
Summary:
Add a method to folly::Uri to get parsed query string

e.g. http://localhost?key1=foo&key2=bar

We can get key value map containing:
"key1" => "foo"
"key2" => "bar"

Test Plan:
fbconfig folly/test
fbmake runtests_dbg

Reviewed By: tudorb@fb.com

Subscribers: wormhole-dev@

FB internal diff: D1455158

Tasks: 4768038

folly/Uri.cpp
folly/Uri.h
folly/test/UriTest.cpp

index 5b2c155b80e7853888040a61476b191ac34fccde..f7bf80fafeec1a8fbcca141082284bee316e678a 100644 (file)
@@ -91,6 +91,26 @@ Uri::Uri(StringPiece str) : port_(0) {
   }
 
   query_ = submatch(match, 3);
+  if (!query_.empty()) {
+    // Parse query string
+    static const boost::regex queryParamRegex(
+      "(^|&)([^=&]*)=?([^=&]*)(?=(&|$))");
+    boost::cregex_iterator paramBeginItr(
+      match[3].first,
+      match[3].second,
+      queryParamRegex);
+    boost::cregex_iterator paramEndItr;
+    for(auto itr = paramBeginItr; itr != paramEndItr; itr++) {
+      if (itr->length(2) == 0) {
+        // key is empty, ignore it
+        continue;
+      }
+      queryParams_.emplace_back(
+        fbstring((*itr)[2].first, (*itr)[2].second), // parameter name
+        fbstring((*itr)[3].first, (*itr)[3].second)  // parameter value
+      );
+    }
+  }
   fragment_ = submatch(match, 4);
 }
 
index e7b38615801261799f2bebeb2a0564a850c640f5..581307d928d8315e3ab417a38fa1345159ef048c 100644 (file)
@@ -18,6 +18,7 @@
 #define FOLLY_URI_H_
 
 #include <folly/String.h>
+#include <vector>
 
 namespace folly {
 
@@ -77,6 +78,26 @@ class Uri {
 
   void setPort(uint16_t port) {port_ = port;}
 
+  /**
+   * Get query parameters as key-value pairs.
+   * e.g. for URI containing query string:  key1=foo&key2=&key3&=bar&=bar=
+   * In returned list, there are 3 entries:
+   *     "key1" => "foo"
+   *     "key2" => ""
+   *     "key3" => ""
+   * Parts "=bar" and "=bar=" are ignored, as they are not valid query
+   * parameters. "=bar" is missing parameter name, while "=bar=" has more than
+   * one equal signs, we don't know which one is the delimiter for key and
+   * value.
+   *
+   * @return  query parameter key-value pairs in a vector, each element is a
+   *          pair of which the first element is parameter name and the second
+   *          one is parameter value
+   */
+  const std::vector<std::pair<fbstring, fbstring>>& getQueryParams() const {
+    return queryParams_;
+  };
+
  private:
   fbstring scheme_;
   fbstring username_;
@@ -86,6 +107,7 @@ class Uri {
   fbstring path_;
   fbstring query_;
   fbstring fragment_;
+  std::vector<std::pair<fbstring, fbstring>> queryParams_;
 };
 
 }  // namespace folly
index 7edc84d9c79402434eb5d24249e16d5340de4022..7f8faee8847d5ad2728b9f94f104a53407f85a58 100644 (file)
@@ -19,6 +19,7 @@
 #include <boost/algorithm/string.hpp>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
+#include <map>
 
 using namespace folly;
 
@@ -255,6 +256,73 @@ TEST(Uri, Simple) {
     EXPECT_EQ(s, u.fbstr());
   }
 
+  {
+    // test query parameters
+    fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
+    Uri u(s);
+    auto paramsList = u.getQueryParams();
+    std::map<fbstring, fbstring> params;
+    for (auto& param : paramsList) {
+      params[param.first] = param.second;
+    }
+    EXPECT_EQ(3, params.size());
+    EXPECT_EQ("foo", params["key1"]);
+    EXPECT_NE(params.end(), params.find("key2"));
+    EXPECT_EQ("", params["key2"]);
+    EXPECT_NE(params.end(), params.find("key3"));
+    EXPECT_EQ("", params["key3"]);
+  }
+
+  {
+    // test query parameters
+    fbstring s("http://localhost?&&&&&&&&&&&&&&&");
+    Uri u(s);
+    auto params = u.getQueryParams();
+    EXPECT_TRUE(params.empty());
+  }
+
+  {
+    // test query parameters
+    fbstring s("http://localhost?&=invalid_key&key2&key3=foo");
+    Uri u(s);
+    auto paramsList = u.getQueryParams();
+    std::map<fbstring, fbstring> params;
+    for (auto& param : paramsList) {
+      params[param.first] = param.second;
+    }
+    EXPECT_EQ(2, params.size());
+    EXPECT_NE(params.end(), params.find("key2"));
+    EXPECT_EQ("", params["key2"]);
+    EXPECT_EQ("foo", params["key3"]);
+  }
+
+  {
+    // test query parameters
+    fbstring s("http://localhost?&key1=====&&=key2&key3=");
+    Uri u(s);
+    auto paramsList = u.getQueryParams();
+    std::map<fbstring, fbstring> params;
+    for (auto& param : paramsList) {
+      params[param.first] = param.second;
+    }
+    EXPECT_EQ(1, params.size());
+    EXPECT_NE(params.end(), params.find("key3"));
+    EXPECT_EQ("", params["key3"]);
+  }
+
+  {
+    // test query parameters
+    fbstring s("http://localhost?key1=foo=bar&key2=foobar&");
+    Uri u(s);
+    auto paramsList = u.getQueryParams();
+    std::map<fbstring, fbstring> params;
+    for (auto& param : paramsList) {
+      params[param.first] = param.second;
+    }
+    EXPECT_EQ(1, params.size());
+    EXPECT_EQ("foobar", params["key2"]);
+  }
+
   {
     fbstring s("2http://www.facebook.com");