From 59c7c8eafcd986b169fecaaff870d2d29267d029 Mon Sep 17 00:00:00 2001 From: Jun LI Date: Wed, 16 Apr 2014 13:58:54 -0700 Subject: [PATCH] Fix folly Uri::host() return value for IPv6 address 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 | 13 +++++-- folly/Uri.h | 14 ++++++++ folly/test/UriTest.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/folly/Uri.cpp b/folly/Uri.cpp index 728b7a0c..30106c9f 100644 --- a/folly/Uri.cpp +++ b/folly/Uri.cpp @@ -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 diff --git a/folly/Uri.h b/folly/Uri.h index 29711197..5e765309 100644 --- a/folly/Uri.h +++ b/folly/Uri.h @@ -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_; } diff --git a/folly/test/UriTest.cpp b/folly/test/UriTest.cpp index 710ad2c7..a209f255 100644 --- a/folly/test/UriTest.cpp +++ b/folly/test/UriTest.cpp @@ -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 + } + } } -- 2.34.1