Return if we handle any error messages to avoid unnecessarily calling recv/send
[folly.git] / folly / Uri.cpp
index 9ae5c713d1d6042b4212949b8193e7a8aa5f4b4a..d1bdfae73b736c1d59bc0bb5e8f357a71c1f7281 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "folly/Uri.h"
+#include <folly/Uri.h>
+
+#include <cctype>
 
-#include <ctype.h>
 #include <boost/regex.hpp>
 
 namespace folly {
 
 namespace {
 
-fbstring submatch(const boost::cmatch& m, size_t idx) {
+std::string submatch(const boost::cmatch& m, int idx) {
   auto& sub = m[idx];
-  return fbstring(sub.first, sub.second);
+  return std::string(sub.first, sub.second);
 }
 
 template <class String>
 void toLower(String& s) {
   for (auto& c : s) {
-    c = tolower(c);
+    c = char(tolower(c));
   }
 }
 
-}  // namespace
+} // namespace
 
-Uri::Uri(StringPiece str) : port_(0) {
+Uri::Uri(StringPiece str) : hasAuthority_(false), port_(0) {
   static const boost::regex uriRegex(
       "([a-zA-Z][a-zA-Z0-9+.-]*):"  // scheme:
       "([^?#]*)"                    // authority and path
@@ -47,7 +48,7 @@ Uri::Uri(StringPiece str) : port_(0) {
 
   boost::cmatch match;
   if (UNLIKELY(!boost::regex_match(str.begin(), str.end(), match, uriRegex))) {
-    throw std::invalid_argument("invalid URI");
+    throw std::invalid_argument(to<std::string>("invalid URI ", str));
   }
 
   scheme_ = submatch(match, 1);
@@ -60,12 +61,13 @@ Uri::Uri(StringPiece str) : port_(0) {
                           authorityAndPathMatch,
                           authorityAndPathRegex)) {
     // Does not start with //, doesn't have authority
-    path_ = authorityAndPath.fbstr();
+    hasAuthority_ = false;
+    path_ = authorityAndPath.str();
   } else {
     static const boost::regex authorityRegex(
         "(?:([^@:]*)(?::([^@]*))?@)?"  // username, password
-        "(\\[[^\\]]*\\]|[^\\[:]*)"     // host (IP-literal, dotted-IPv4, or
-                                       // named host)
+        "(\\[[^\\]]*\\]|[^\\[:]*)"     // host (IP-literal (e.g. '['+IPv6+']',
+                                       // dotted-IPv4, or named host)
         "(?::(\\d*))?");               // port
 
     auto authority = authorityAndPathMatch[1];
@@ -74,14 +76,17 @@ Uri::Uri(StringPiece str) : port_(0) {
                             authority.second,
                             authorityMatch,
                             authorityRegex)) {
-      throw std::invalid_argument("invalid URI authority");
+      throw std::invalid_argument(
+          to<std::string>("invalid URI authority ",
+                          StringPiece(authority.first, authority.second)));
     }
 
     StringPiece port(authorityMatch[4].first, authorityMatch[4].second);
     if (!port.empty()) {
-      port_ = to<uint32_t>(port);
+      port_ = to<uint16_t>(port);
     }
 
+    hasAuthority_ = true;
     username_ = submatch(authorityMatch, 1);
     password_ = submatch(authorityMatch, 2);
     host_ = submatch(authorityMatch, 3);
@@ -92,4 +97,66 @@ Uri::Uri(StringPiece str) : port_(0) {
   fragment_ = submatch(match, 4);
 }
 
-}  // namespace folly
+std::string Uri::authority() const {
+  std::string result;
+
+  // Port is 5 characters max and we have up to 3 delimiters.
+  result.reserve(host().size() + username().size() + password().size() + 8);
+
+  if (!username().empty() || !password().empty()) {
+    result.append(username());
+
+    if (!password().empty()) {
+      result.push_back(':');
+      result.append(password());
+    }
+
+    result.push_back('@');
+  }
+
+  result.append(host());
+
+  if (port() != 0) {
+    result.push_back(':');
+    toAppend(port(), &result);
+  }
+
+  return result;
+}
+
+std::string Uri::hostname() const {
+  if (host_.size() > 0 && host_[0] == '[') {
+    // If it starts with '[', then it should end with ']', this is ensured by
+    // regex
+    return host_.substr(1, host_.size() - 2);
+  }
+  return host_;
+}
+
+const std::vector<std::pair<std::string, std::string>>& Uri::getQueryParams() {
+  if (!query_.empty() && queryParams_.empty()) {
+    // Parse query string
+    static const boost::regex queryParamRegex(
+        "(^|&)" /*start of query or start of parameter "&"*/
+        "([^=&]*)=?" /*parameter name and "=" if value is expected*/
+        "([^=&]*)" /*parameter value*/
+        "(?=(&|$))" /*forward reference, next should be end of query or
+                      start of next parameter*/);
+    boost::cregex_iterator paramBeginItr(
+        query_.data(), query_.data() + query_.size(), 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(
+          std::string((*itr)[2].first, (*itr)[2].second), // parameter name
+          std::string((*itr)[3].first, (*itr)[3].second) // parameter value
+          );
+    }
+  }
+  return queryParams_;
+}
+
+} // namespace folly