base::Value (de)serialization for some basic net types
Working on some HostCache::Entry replacement work, which requires going
to/from base::Value for these types. This essentially duplicates some of
the (de)serialization logic, which will hopefully eventually be deleted,
in host_cache.cc.
Change-Id: Ib28b4d4b5b1da29824f64fae3bc7d83f602269a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3964882
Commit-Queue: Eric Orth <[email protected]>
Reviewed-by: Tsuyoshi Horo <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1062892}
diff --git a/net/base/host_port_pair.cc b/net/base/host_port_pair.cc
index 1abe514..7bc4f26 100644
--- a/net/base/host_port_pair.cc
+++ b/net/base/host_port_pair.cc
@@ -12,13 +12,23 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/memory_usage_estimator.h"
+#include "base/values.h"
#include "net/base/ip_endpoint.h"
#include "net/base/url_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
namespace net {
+namespace {
+
+// Value dictionary keys
+constexpr base::StringPiece kValueHostKey = "host";
+constexpr base::StringPiece kValuePortKey = "port";
+
+} // namespace
+
HostPortPair::HostPortPair() : port_(0) {}
HostPortPair::HostPortPair(base::StringPiece in_host, uint16_t in_port)
: host_(in_host), port_(in_port) {}
@@ -76,6 +86,23 @@
return HostPortPair(host, port);
}
+// static
+absl::optional<HostPortPair> HostPortPair::FromValue(const base::Value& value) {
+ const base::Value::Dict* dict = value.GetIfDict();
+ if (!dict)
+ return absl::nullopt;
+
+ const std::string* host = dict->FindString(kValueHostKey);
+ absl::optional<int> port = dict->FindInt(kValuePortKey);
+
+ if (host == nullptr || !port.has_value() ||
+ !base::IsValueInRangeForNumericType<uint16_t>(port.value())) {
+ return absl::nullopt;
+ }
+
+ return HostPortPair(*host, base::checked_cast<uint16_t>(port.value()));
+}
+
std::string HostPortPair::ToString() const {
std::string ret(HostForURL());
ret += ':';
@@ -102,4 +129,12 @@
return host_;
}
+base::Value HostPortPair::ToValue() const {
+ base::Value::Dict dict;
+ dict.Set(kValueHostKey, host_);
+ dict.Set(kValuePortKey, port_);
+
+ return base::Value(std::move(dict));
+}
+
} // namespace net
diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h
index b86d3b2c..d694af2 100644
--- a/net/base/host_port_pair.h
+++ b/net/base/host_port_pair.h
@@ -11,7 +11,9 @@
#include <tuple>
#include "base/strings/string_piece.h"
+#include "base/values.h"
#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
class GURL;
@@ -42,6 +44,9 @@
// ToString().
static HostPortPair FromString(base::StringPiece str);
+ // Nullopt if `value` is malformed to be deserialized to HostPortPair.
+ static absl::optional<HostPortPair> FromValue(const base::Value& value);
+
// TODO(willchan): Define a functor instead.
// Comparator function so this can be placed in a std::map.
bool operator<(const HostPortPair& other) const {
@@ -78,6 +83,8 @@
// Returns |host_|, adding IPv6 brackets if needed.
std::string HostForURL() const;
+ base::Value ToValue() const;
+
private:
// If |host_| represents an IPv6 address, this string will not contain
// brackets around the address.
diff --git a/net/base/host_port_pair_unittest.cc b/net/base/host_port_pair_unittest.cc
index 89907a77..0ee593b9 100644
--- a/net/base/host_port_pair_unittest.cc
+++ b/net/base/host_port_pair_unittest.cc
@@ -4,12 +4,16 @@
#include "net/base/host_port_pair.h"
+#include "base/values.h"
#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
using std::string;
+using testing::Optional;
namespace net {
@@ -155,6 +159,39 @@
EXPECT_EQ(parsed, expected);
}
+TEST(HostPortPairTest, RoundtripThroughValue) {
+ HostPortPair pair("foo.test", 1456);
+ base::Value value = pair.ToValue();
+
+ EXPECT_THAT(HostPortPair::FromValue(value), Optional(pair));
+}
+
+TEST(HostPortPairTest, DeserializeGarbageValue) {
+ base::Value value(43);
+ EXPECT_FALSE(HostPortPair::FromValue(value).has_value());
+}
+
+TEST(HostPortPairTest, DeserializeMalformedValues) {
+ base::Value valid_value = HostPortPair("foo.test", 123).ToValue();
+ ASSERT_TRUE(HostPortPair::FromValue(valid_value).has_value());
+
+ base::Value missing_host = valid_value.Clone();
+ ASSERT_TRUE(missing_host.GetDict().Remove("host"));
+ EXPECT_FALSE(HostPortPair::FromValue(missing_host).has_value());
+
+ base::Value missing_port = valid_value.Clone();
+ ASSERT_TRUE(missing_port.GetDict().Remove("port"));
+ EXPECT_FALSE(HostPortPair::FromValue(missing_port).has_value());
+
+ base::Value negative_port = valid_value.Clone();
+ *negative_port.GetDict().Find("port") = base::Value(-1);
+ EXPECT_FALSE(HostPortPair::FromValue(negative_port).has_value());
+
+ base::Value large_port = valid_value.Clone();
+ *large_port.GetDict().Find("port") = base::Value(66000);
+ EXPECT_FALSE(HostPortPair::FromValue(large_port).has_value());
+}
+
} // namespace
} // namespace net
diff --git a/net/base/ip_address.cc b/net/base/ip_address.cc
index 28e210ef..9f33182 100644
--- a/net/base/ip_address.cc
+++ b/net/base/ip_address.cc
@@ -14,7 +14,9 @@
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
+#include "base/values.h"
#include "net/base/parse_number.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/url_canon_ip.h"
@@ -168,6 +170,19 @@
return !(*this == other);
}
+// static
+absl::optional<IPAddress> IPAddress::FromValue(const base::Value& value) {
+ if (!value.is_string())
+ return absl::nullopt;
+
+ IPAddress address;
+ bool success = address.AssignFromIPLiteral(value.GetString());
+ if (!success || !address.IsValid())
+ return absl::nullopt;
+
+ return address;
+}
+
IPAddress::IPAddress() = default;
IPAddress::IPAddress(const IPAddress& other) = default;
@@ -361,6 +376,11 @@
return str;
}
+base::Value IPAddress::ToValue() const {
+ DCHECK(IsValid());
+ return base::Value(ToString());
+}
+
std::string IPAddressToStringWithPort(const IPAddress& address, uint16_t port) {
std::string address_str = address.ToString();
if (address_str.empty())
diff --git a/net/base/ip_address.h b/net/base/ip_address.h
index a9782d8..5f0412b1 100644
--- a/net/base/ip_address.h
+++ b/net/base/ip_address.h
@@ -14,7 +14,9 @@
#include "base/check_op.h"
#include "base/strings/string_piece.h"
+#include "base/values.h"
#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace net {
@@ -99,6 +101,9 @@
public:
enum : size_t { kIPv4AddressSize = 4, kIPv6AddressSize = 16 };
+ // Nullopt if `value` is malformed to be deserialized to IPAddress.
+ static absl::optional<IPAddress> FromValue(const base::Value& value);
+
// Creates a zero-sized, invalid address.
IPAddress();
@@ -215,6 +220,9 @@
bool operator!=(const IPAddress& that) const;
bool operator<(const IPAddress& that) const;
+ // Must be a valid address (per IsValid()).
+ base::Value ToValue() const;
+
private:
IPAddressBytes ip_address_;
diff --git a/net/base/ip_address_unittest.cc b/net/base/ip_address_unittest.cc
index 1345bcd..560c6cd 100644
--- a/net/base/ip_address_unittest.cc
+++ b/net/base/ip_address_unittest.cc
@@ -9,7 +9,11 @@
#include "base/format_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using testing::Optional;
namespace net {
@@ -831,6 +835,24 @@
EXPECT_EQ("2001:db8:c000:221::", converted_ipv6_address_32.ToString());
}
+TEST(IPAddressTest, RoundtripAddressThroughValue) {
+ IPAddress address(1, 2, 3, 4);
+ ASSERT_TRUE(address.IsValid());
+
+ base::Value value = address.ToValue();
+ EXPECT_THAT(IPAddress::FromValue(value), Optional(address));
+}
+
+TEST(IPAddressTest, FromGarbageValue) {
+ base::Value value(123);
+ EXPECT_FALSE(IPAddress::FromValue(value).has_value());
+}
+
+TEST(IPAddressTest, FromInvalidValue) {
+ base::Value value("1.2.3.4.5");
+ EXPECT_FALSE(IPAddress::FromValue(value).has_value());
+}
+
} // anonymous namespace
} // namespace net
diff --git a/net/base/ip_endpoint.cc b/net/base/ip_endpoint.cc
index 05466120..7947b58 100644
--- a/net/base/ip_endpoint.cc
+++ b/net/base/ip_endpoint.cc
@@ -8,15 +8,19 @@
#include <string.h>
#include <tuple>
+#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_byteorder.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "net/base/ip_address.h"
#include "net/base/sys_addrinfo.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_WIN)
#include <winsock2.h>
@@ -27,6 +31,39 @@
namespace net {
+namespace {
+
+// Value dictionary keys
+constexpr base::StringPiece kValueAddressKey = "address";
+constexpr base::StringPiece kValuePortKey = "port";
+
+} // namespace
+
+// static
+absl::optional<IPEndPoint> IPEndPoint::FromValue(const base::Value& value) {
+ const base::Value::Dict* dict = value.GetIfDict();
+ if (!dict)
+ return absl::nullopt;
+
+ const base::Value* address_value = dict->Find(kValueAddressKey);
+ if (!address_value)
+ return absl::nullopt;
+ absl::optional<IPAddress> address = IPAddress::FromValue(*address_value);
+ if (!address.has_value())
+ return absl::nullopt;
+ // Expect IPAddress to only allow deserializing valid addresses.
+ DCHECK(address.value().IsValid());
+
+ absl::optional<int> port = dict->FindInt(kValuePortKey);
+ if (!port.has_value() ||
+ !base::IsValueInRangeForNumericType<uint16_t>(port.value())) {
+ return absl::nullopt;
+ }
+
+ return IPEndPoint(address.value(),
+ base::checked_cast<uint16_t>(port.value()));
+}
+
IPEndPoint::IPEndPoint() = default;
IPEndPoint::~IPEndPoint() = default;
@@ -182,6 +219,16 @@
return !(*this == that);
}
+base::Value IPEndPoint::ToValue() const {
+ base::Value::Dict dict;
+
+ DCHECK(address_.IsValid());
+ dict.Set(kValueAddressKey, address_.ToValue());
+ dict.Set(kValuePortKey, port_);
+
+ return base::Value(std::move(dict));
+}
+
std::ostream& operator<<(std::ostream& os, const IPEndPoint& ip_endpoint) {
return os << ip_endpoint.ToString();
}
diff --git a/net/base/ip_endpoint.h b/net/base/ip_endpoint.h
index 8e0f8a5..fa77f35 100644
--- a/net/base/ip_endpoint.h
+++ b/net/base/ip_endpoint.h
@@ -10,10 +10,12 @@
#include <ostream>
#include <string>
+#include "base/values.h"
#include "build/build_config.h"
#include "net/base/address_family.h"
#include "net/base/ip_address.h"
#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
// Replicate these from Windows headers to avoid pulling net/sys_addrinfo.h.
// Doing that transitively brings in windows.h. Including windows.h pollutes the
@@ -36,6 +38,9 @@
// * Port
class NET_EXPORT IPEndPoint {
public:
+ // Nullopt if `value` is malformed to be serialized to IPEndPoint.
+ static absl::optional<IPEndPoint> FromValue(const base::Value& value);
+
IPEndPoint();
~IPEndPoint();
IPEndPoint(const IPAddress& address, uint16_t port);
@@ -88,6 +93,8 @@
bool operator==(const IPEndPoint& that) const;
bool operator!=(const IPEndPoint& that) const;
+ base::Value ToValue() const;
+
private:
IPAddress address_;
uint16_t port_ = 0;
diff --git a/net/base/ip_endpoint_unittest.cc b/net/base/ip_endpoint_unittest.cc
index fc100b2..355f882c 100644
--- a/net/base/ip_endpoint_unittest.cc
+++ b/net/base/ip_endpoint_unittest.cc
@@ -14,12 +14,15 @@
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_byteorder.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "net/base/ip_address.h"
#include "net/base/sockaddr_storage.h"
#include "net/base/sys_addrinfo.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_WIN)
#include <winsock2.h>
@@ -31,6 +34,8 @@
#include <netinet/in.h>
#endif
+using testing::Optional;
+
namespace net {
namespace {
@@ -68,10 +73,10 @@
bool ipv6;
IPAddress ip_address;
} tests[] = {
- { "127.0.00.1", "127.0.0.1", false},
- { "192.168.1.1", "192.168.1.1", false },
- { "::1", "[::1]", true },
- { "2001:db8:0::42", "[2001:db8::42]", true },
+ {"127.0.00.1", "127.0.0.1", false},
+ {"192.168.1.1", "192.168.1.1", false},
+ {"::1", "[::1]", true},
+ {"2001:db8:0::42", "[2001:db8::42]", true},
};
class IPEndPointTest : public PlatformTest {
@@ -132,8 +137,8 @@
socklen_t expected_size =
test.ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
EXPECT_EQ(expected_size, storage.addr_len);
- EXPECT_EQ(ip_endpoint.port(), GetPortFromSockaddr(storage.addr,
- storage.addr_len));
+ EXPECT_EQ(ip_endpoint.port(),
+ GetPortFromSockaddr(storage.addr, storage.addr_len));
// And convert back to an IPEndPoint.
IPEndPoint ip_endpoint2;
EXPECT_TRUE(ip_endpoint2.FromSockAddr(storage.addr, storage.addr_len));
@@ -376,6 +381,47 @@
EXPECT_EQ("", invalid_endpoint.ToStringWithoutPort());
}
+TEST_F(IPEndPointTest, RoundtripThroughValue) {
+ for (const auto& test : tests) {
+ IPEndPoint endpoint(test.ip_address, 1645);
+ base::Value value = endpoint.ToValue();
+
+ EXPECT_THAT(IPEndPoint::FromValue(value), Optional(endpoint));
+ }
+}
+
+TEST_F(IPEndPointTest, FromGarbageValue) {
+ base::Value value(123);
+ EXPECT_FALSE(IPEndPoint::FromValue(value).has_value());
+}
+
+TEST_F(IPEndPointTest, FromMalformedValues) {
+ for (const auto& test : tests) {
+ base::Value valid_value = IPEndPoint(test.ip_address, 1111).ToValue();
+ ASSERT_TRUE(IPEndPoint::FromValue(valid_value).has_value());
+
+ base::Value missing_address = valid_value.Clone();
+ ASSERT_TRUE(missing_address.GetDict().Remove("address"));
+ EXPECT_FALSE(IPEndPoint::FromValue(missing_address).has_value());
+
+ base::Value missing_port = valid_value.Clone();
+ ASSERT_TRUE(missing_port.GetDict().Remove("port"));
+ EXPECT_FALSE(IPEndPoint::FromValue(missing_port).has_value());
+
+ base::Value invalid_address = valid_value.Clone();
+ *invalid_address.GetDict().Find("address") = base::Value("1.2.3.4.5");
+ EXPECT_FALSE(IPEndPoint::FromValue(invalid_address).has_value());
+
+ base::Value negative_port = valid_value.Clone();
+ *negative_port.GetDict().Find("port") = base::Value(-1);
+ EXPECT_FALSE(IPEndPoint::FromValue(negative_port).has_value());
+
+ base::Value large_port = valid_value.Clone();
+ *large_port.GetDict().Find("port") = base::Value(66000);
+ EXPECT_FALSE(IPEndPoint::FromValue(large_port).has_value());
+ }
+}
+
} // namespace
} // namespace net