Server ID object for QUIC-LB. Wraps an array of uint8_t with validation to make sure the length is correct. This will be used by other classes.
PiperOrigin-RevId: 434513538
diff --git a/quic/load_balancer/load_balancer_server_id.cc b/quic/load_balancer/load_balancer_server_id.cc
new file mode 100644
index 0000000..12f1135
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/load_balancer/load_balancer_server_id.h"
+
+#include "absl/strings/escaping.h"
+#include "absl/types/span.h"
+#include "quic/core/quic_types.h"
+#include "quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+namespace {
+
+// Helper to allow setting the const array during initialization.
+std::array<uint8_t, kLoadBalancerMaxServerIdLen> MakeArray(
+ const absl::Span<const uint8_t> data, const uint8_t length) {
+ std::array<uint8_t, kLoadBalancerMaxServerIdLen> array;
+ memcpy(array.data(), data.data(), length);
+ return array;
+}
+
+} // namespace
+
+absl::optional<LoadBalancerServerId> LoadBalancerServerId::Create(
+ const absl::Span<const uint8_t> data) {
+ if (data.length() == 0 || data.length() > kLoadBalancerMaxServerIdLen) {
+ QUIC_BUG(quic_bug_433312504_01)
+ << "Attempted to create LoadBalancerServerId with length "
+ << data.length();
+ return absl::optional<LoadBalancerServerId>();
+ }
+ return LoadBalancerServerId(data);
+}
+
+std::string LoadBalancerServerId::ToString() const {
+ return absl::BytesToHexString(
+ absl::string_view(reinterpret_cast<const char*>(data_.data()), length_));
+}
+
+LoadBalancerServerId::LoadBalancerServerId(const absl::Span<const uint8_t> data)
+ : data_(MakeArray(data, data.length())), length_(data.length()) {}
+
+} // namespace quic
diff --git a/quic/load_balancer/load_balancer_server_id.h b/quic/load_balancer/load_balancer_server_id.h
new file mode 100644
index 0000000..e729437
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
+#define QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
+
+#include <array>
+
+#include "quic/core/quic_types.h"
+#include "quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The maximum number of bytes in a LoadBalancerServerId.
+inline constexpr uint8_t kLoadBalancerMaxServerIdLen = 15;
+
+// LoadBalancerServerId is the globally understood identifier for a given pool
+// member. It is unique to any given QUIC-LB configuration. See
+// draft-ietf-quic-load-balancers-12.
+// Note: this has nothing to do with QuicServerID. It's an unfortunate collision
+// between an internal term for the destination identifiers for a particular
+// deployment (QuicServerID) and the object of a load balancing decision
+// (LoadBalancerServerId).
+class QUIC_EXPORT_PRIVATE LoadBalancerServerId {
+ public:
+ // Copies all the bytes from |data| into a new LoadBalancerServerId.
+ static absl::optional<LoadBalancerServerId> Create(
+ const absl::Span<const uint8_t> data);
+
+ // Server IDs are opaque bytes, but defining these operators allows us to sort
+ // them into a tree and define ranges.
+ bool operator<(const LoadBalancerServerId& other) const {
+ return data() < other.data();
+ }
+ bool operator==(const LoadBalancerServerId& other) const {
+ return data() == other.data();
+ }
+
+ // Hash function to allow use as a key in unordered maps.
+ template <typename H>
+ friend H AbslHashValue(H h, const LoadBalancerServerId& server_id) {
+ return H::combine_contiguous(std::move(h), server_id.data().data(),
+ server_id.length());
+ }
+
+ absl::Span<const uint8_t> data() const {
+ return absl::MakeConstSpan(data_.data(), length_);
+ }
+ inline uint8_t length() const { return length_; }
+
+ // Returns the server ID in hex format.
+ std::string ToString() const;
+
+ private:
+ // The constructor is private because it can't validate the input.
+ LoadBalancerServerId(const absl::Span<const uint8_t> data);
+
+ const std::array<uint8_t, kLoadBalancerMaxServerIdLen> data_;
+ const uint8_t length_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
diff --git a/quic/load_balancer/load_balancer_server_id_test.cc b/quic/load_balancer/load_balancer_server_id_test.cc
new file mode 100644
index 0000000..8160830
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id_test.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/load_balancer/load_balancer_server_id.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+
+#include "absl/hash/hash_testing.h"
+
+#pragma clang diagnostic pop
+
+#include "quic/platform/api/quic_expect_bug.h"
+#include "quic/platform/api/quic_test.h"
+#include "quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+class LoadBalancerServerIdTest : public QuicTest {};
+
+constexpr uint8_t kRawServerId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f};
+
+TEST_F(LoadBalancerServerIdTest, CreateReturnsNullIfTooLong) {
+ EXPECT_QUIC_BUG(EXPECT_FALSE(LoadBalancerServerId::Create(
+ absl::Span<const uint8_t>(kRawServerId, 16))
+ .has_value()),
+ "Attempted to create LoadBalancerServerId with length 16");
+ EXPECT_QUIC_BUG(
+ EXPECT_FALSE(LoadBalancerServerId::Create(absl::Span<const uint8_t>())
+ .has_value()),
+ "Attempted to create LoadBalancerServerId with length 0");
+}
+
+TEST_F(LoadBalancerServerIdTest, CompareIdenticalExceptLength) {
+ auto server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+ ASSERT_TRUE(server_id.has_value());
+ EXPECT_EQ(server_id->length(), 15);
+ auto shorter_server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+ ASSERT_TRUE(shorter_server_id.has_value());
+ EXPECT_EQ(shorter_server_id->length(), 5);
+ // Shorter comes before longer if all bits match
+ EXPECT_TRUE(shorter_server_id < server_id);
+ EXPECT_FALSE(server_id < shorter_server_id);
+ // Different lengths are never equal.
+ EXPECT_FALSE(shorter_server_id == server_id);
+}
+
+TEST_F(LoadBalancerServerIdTest, AccessorFunctions) {
+ auto server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+ EXPECT_TRUE(server_id.has_value());
+ EXPECT_EQ(server_id->length(), 5);
+ EXPECT_EQ(memcmp(server_id->data().data(), kRawServerId, 5), 0);
+ EXPECT_EQ(server_id->ToString(), "0001020304");
+}
+
+TEST_F(LoadBalancerServerIdTest, CompareDifferentServerIds) {
+ auto server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+ ASSERT_TRUE(server_id.has_value());
+ auto reverse = LoadBalancerServerId::Create({0x0f, 0x0e, 0x0d, 0x0c, 0x0b});
+ ASSERT_TRUE(reverse.has_value());
+ EXPECT_TRUE(server_id < reverse);
+ auto long_server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+ EXPECT_TRUE(long_server_id < reverse);
+}
+
+TEST_F(LoadBalancerServerIdTest, EqualityOperators) {
+ auto server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+ ASSERT_TRUE(server_id.has_value());
+ auto shorter_server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+ ASSERT_TRUE(shorter_server_id.has_value());
+ EXPECT_FALSE(server_id == shorter_server_id);
+ auto server_id2 = server_id;
+ EXPECT_TRUE(server_id == server_id2);
+}
+
+TEST_F(LoadBalancerServerIdTest, SupportsHash) {
+ auto server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+ ASSERT_TRUE(server_id.has_value());
+ auto shorter_server_id =
+ LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+ ASSERT_TRUE(shorter_server_id.has_value());
+ auto different_server_id =
+ LoadBalancerServerId::Create({0x0f, 0x0e, 0x0d, 0x0c, 0x0b});
+ ASSERT_TRUE(different_server_id.has_value());
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+ *server_id,
+ *shorter_server_id,
+ *different_server_id,
+ }));
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace quic