Fix folly Uri::host() return value for IPv6 address
authorJun LI <albertli@fb.com>
Wed, 16 Apr 2014 20:58:54 +0000 (13:58 -0700)
committerSara Golemon <sgolemon@fb.com>
Fri, 18 Apr 2014 19:04:15 +0000 (12:04 -0700)
Summary: folly Uri::host() returns leading and trailing brackets for IPv6 host, for example, for URI "http:://[::1]:8080/index.html", host() method returns "[::1]". But square brackets are not part of host in fact, this diff is going to remove brackets for IPv6 host.

Test Plan: fbconfig -r folly/test && fbmake runtests_dbg

Reviewed By: chip@fb.com

FB internal diff: D1276513

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

index 728b7a0c574cee6af657ffedf5853517db31f5cf..30106c9ff23ae84fca139015eb371d3795adfe6b 100644 (file)
@@ -64,8 +64,8 @@ Uri::Uri(StringPiece str) : port_(0) {
   } 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];
@@ -121,4 +121,13 @@ fbstring Uri::authority() const {
   return result;
 }
 
+fbstring 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_;
+}
+
 }  // namespace folly
index 29711197272656836f90121241dcbf6e83e1805b..5e7653092f4c716c1a23be166d80bc16e9397844 100644 (file)
@@ -47,7 +47,21 @@ class Uri {
   const fbstring& scheme() const { return scheme_; }
   const fbstring& username() const { return username_; }
   const fbstring& password() const { return password_; }
+  /**
+   * Get host part of URI. If host is an IPv6 address, square brackets will be
+   * returned, for example: "[::1]".
+   */
   const fbstring& host() const { return host_; }
+  /**
+   * Get host part of URI. If host is an IPv6 address, square brackets will not
+   * be returned, for exmaple "::1"; otherwise it returns the same thing as
+   * host().
+   *
+   * hostname() is what one needs to call if passing the host to any other tool
+   * or API that connects to that host/port; e.g. getaddrinfo() only understands
+   * IPv6 host without square brackets
+   */
+  fbstring hostname() const;
   uint16_t port() const { return port_; }
   const fbstring& path() const { return path_; }
   const fbstring& query() const { return query_; }
index 710ad2c7a7f8e35ad7f2a3b0301562d6b375bd09..a209f25576dd882b5f3db77e37f48ffeeb8e0dae 100644 (file)
@@ -79,6 +79,7 @@ TEST(Uri, Simple) {
     EXPECT_EQ("", u.username());
     EXPECT_EQ("", u.password());
     EXPECT_EQ("[::1]", u.host());
+    EXPECT_EQ("::1", u.hostname());
     EXPECT_EQ(8080, u.port());
     EXPECT_EQ("[::1]:8080", u.authority());
     EXPECT_EQ("/hello/world", u.path());
@@ -87,6 +88,38 @@ TEST(Uri, Simple) {
     EXPECT_EQ(s, u.fbstr());  // canonical
   }
 
+  {
+    fbstring s("http://[2401:db00:20:7004:face:0:29:0]:8080/hello/world?query");
+    Uri u(s);
+    EXPECT_EQ("http", u.scheme());
+    EXPECT_EQ("", u.username());
+    EXPECT_EQ("", u.password());
+    EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.host());
+    EXPECT_EQ("2401:db00:20:7004:face:0:29:0", u.hostname());
+    EXPECT_EQ(8080, u.port());
+    EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]:8080", u.authority());
+    EXPECT_EQ("/hello/world", u.path());
+    EXPECT_EQ("query", u.query());
+    EXPECT_EQ("", u.fragment());
+    EXPECT_EQ(s, u.fbstr());  // canonical
+  }
+
+  {
+    fbstring s("http://[2401:db00:20:7004:face:0:29:0]/hello/world?query");
+    Uri u(s);
+    EXPECT_EQ("http", u.scheme());
+    EXPECT_EQ("", u.username());
+    EXPECT_EQ("", u.password());
+    EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.host());
+    EXPECT_EQ("2401:db00:20:7004:face:0:29:0", u.hostname());
+    EXPECT_EQ(0, u.port());
+    EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.authority());
+    EXPECT_EQ("/hello/world", u.path());
+    EXPECT_EQ("query", u.query());
+    EXPECT_EQ("", u.fragment());
+    EXPECT_EQ(s, u.fbstr());  // canonical
+  }
+
   {
     fbstring s("http://user:pass@host.com/");
     Uri u(s);
@@ -243,4 +276,48 @@ TEST(Uri, Simple) {
       EXPECT_TRUE(boost::algorithm::ends_with(ex.what(), s));
     }
   }
+
+  {
+    fbstring s("http://[::1:8080/hello/world?query#fragment");
+
+    try {
+      Uri u(s);
+      CHECK(false) << "Control should not have reached here";
+    } catch (const std::invalid_argument& ex) {
+      // success
+    }
+  }
+
+  {
+    fbstring s("http://::1]:8080/hello/world?query#fragment");
+
+    try {
+      Uri u(s);
+      CHECK(false) << "Control should not have reached here";
+    } catch (const std::invalid_argument& ex) {
+      // success
+    }
+  }
+
+  {
+    fbstring s("http://::1:8080/hello/world?query#fragment");
+
+    try {
+      Uri u(s);
+      CHECK(false) << "Control should not have reached here";
+    } catch (const std::invalid_argument& ex) {
+      // success
+    }
+  }
+
+  {
+    fbstring s("http://2401:db00:20:7004:face:0:29:0/hello/world?query");
+
+    try {
+      Uri u(s);
+      CHECK(false) << "Control should not have reached here";
+    } catch (const std::invalid_argument& ex) {
+      // success
+    }
+  }
 }