Reduce QUIC load balancer copies by passing the result as an argument. Code is not in production. Performance numbers (Unenc/3 pass/1 pass, ns) Tip of tree: 20.2 / 103 / 33.2 This CL: 21.4 / 101/ 26.3 PiperOrigin-RevId: 599890068
diff --git a/quiche/quic/load_balancer/load_balancer_config.cc b/quiche/quic/load_balancer/load_balancer_config.cc index 2373e6c..b4b989e 100644 --- a/quiche/quic/load_balancer/load_balancer_config.cc +++ b/quiche/quic/load_balancer/load_balancer_config.cc
@@ -89,10 +89,13 @@ : std::optional<LoadBalancerConfig>(); } -LoadBalancerServerId LoadBalancerConfig::FourPassDecrypt( - absl::Span<const uint8_t> ciphertext) const { +bool LoadBalancerConfig::FourPassDecrypt( + absl::Span<const uint8_t> ciphertext, + LoadBalancerServerId& server_id) const { + QUIC_BUG_IF(quic_bug_599862571_02, ciphertext.size() < plaintext_len()) + << "Called FourPassDecrypt with a short Connection ID"; if (!key_.has_value()) { - return LoadBalancerServerId(); + return false; } // Do 3 or 4 passes. Only 3 are necessary if the server_id is short enough // to fit in the first half of the connection ID (the decoder doesn't need @@ -112,18 +115,22 @@ if (server_id_len_ < half_len || (server_id_len_ == half_len && !is_length_odd)) { // There is no half-byte to handle - return LoadBalancerServerId(absl::Span<uint8_t>(&left[2], server_id_len_)); + memcpy(server_id.mutable_data(), &left[2], server_id_len_); + return true; } if (is_length_odd) { right[2] |= left[half_len-- + 1]; // Combine the halves of the odd byte. } - return LoadBalancerServerId( - absl::Span<uint8_t>(&left[2], half_len), - absl::Span<uint8_t>(&right[2], server_id_len_ - half_len)); + memcpy(server_id.mutable_data(), &left[2], half_len); + memcpy(server_id.mutable_data() + half_len, &right[2], + server_id_len_ - half_len); + return true; } QuicConnectionId LoadBalancerConfig::FourPassEncrypt( absl::Span<uint8_t> plaintext) const { + QUIC_BUG_IF(quic_bug_599862571_03, plaintext.size() < total_len()) + << "Called FourPassEncrypt with a short Connection ID"; if (!key_.has_value()) { return QuicConnectionId(); }
diff --git a/quiche/quic/load_balancer/load_balancer_config.h b/quiche/quic/load_balancer/load_balancer_config.h index 9ad396c..cb4521c 100644 --- a/quiche/quic/load_balancer/load_balancer_config.h +++ b/quiche/quic/load_balancer/load_balancer_config.h
@@ -31,8 +31,6 @@ // find the maximum number of configs. inline constexpr uint8_t kNumLoadBalancerConfigs = (1 << kConfigIdBits) - 1; inline constexpr uint8_t kLoadBalancerKeyLen = 16; -// Regardless of key length, the AES block size is always 16 Bytes. -inline constexpr uint8_t kLoadBalancerBlockSize = 16; // The spec says nonces can be 18 bytes, but 16 lets it be a uint128. inline constexpr uint8_t kLoadBalancerMaxNonceLen = 16; inline constexpr uint8_t kLoadBalancerMinNonceLen = 4; @@ -64,8 +62,13 @@ // Returns an invalid Server ID if ciphertext is too small, or needed keys are // missing. |ciphertext| contains the full connection ID minus the first byte. - LoadBalancerServerId FourPassDecrypt( - absl::Span<const uint8_t> ciphertext) const; + // + // IMPORTANT: The decoder data path is likely the most performance-sensitive + // part of the load balancer design, and this code has been carefully + // optimized for performance. Please do not make changes without running the + // benchmark tests to ensure there is no regression. + bool FourPassDecrypt(absl::Span<const uint8_t> ciphertext, + LoadBalancerServerId& server_id) const; // Returns an empty connection ID if the plaintext is too small, or needed // keys are missing. |plaintext| contains the full unencrypted connection ID, // including the first byte.
diff --git a/quiche/quic/load_balancer/load_balancer_config_test.cc b/quiche/quic/load_balancer/load_balancer_config_test.cc index fd40412..0e1b2f6 100644 --- a/quiche/quic/load_balancer/load_balancer_config_test.cc +++ b/quiche/quic/load_balancer/load_balancer_config_test.cc
@@ -193,15 +193,16 @@ // LoadBalancerDecoderTest, respectively. TEST_F(LoadBalancerConfigTest, InvalidBlockEncryption) { - uint8_t pt[kLoadBalancerBlockSize], ct[kLoadBalancerBlockSize]; + uint8_t pt[kLoadBalancerBlockSize + 1], ct[kLoadBalancerBlockSize]; auto pt_config = LoadBalancerConfig::CreateUnencrypted(0, 8, 8); ASSERT_TRUE(pt_config.has_value()); EXPECT_FALSE(pt_config->BlockEncrypt(pt, ct)); EXPECT_FALSE(pt_config->BlockDecrypt(ct, pt)); EXPECT_TRUE(pt_config->FourPassEncrypt(absl::Span<uint8_t>(pt, sizeof(pt))) .IsEmpty()); - EXPECT_FALSE(pt_config->FourPassDecrypt(absl::Span<uint8_t>(pt, sizeof(pt))) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(pt_config->FourPassDecrypt( + absl::Span<uint8_t>(pt, sizeof(pt) - 1), answer)); auto small_cid_config = LoadBalancerConfig::Create(0, 3, 4, absl::string_view(raw_key, 16)); ASSERT_TRUE(small_cid_config.has_value()); @@ -247,6 +248,20 @@ EXPECT_EQ(memcmp(result, ctext, sizeof(ctext)), 0); } +TEST_F(LoadBalancerConfigTest, FourPassInputTooShort) { + auto config = + LoadBalancerConfig::Create(0, 3, 4, absl::string_view(raw_key, 16)); + uint8_t input[] = {0x0d, 0xd2, 0xd0, 0x5a, 0x7b, 0x0d, 0xe9}; + LoadBalancerServerId answer; + EXPECT_QUIC_BUG( + config->FourPassDecrypt( + absl::Span<const uint8_t>(input, sizeof(input) - 1), answer), + "Called FourPassDecrypt with a short Connection ID"); + EXPECT_QUIC_BUG( + config->FourPassEncrypt(absl::Span<uint8_t>(input, sizeof(input))), + "Called FourPassEncrypt with a short Connection ID"); +} + } // namespace } // namespace test
diff --git a/quiche/quic/load_balancer/load_balancer_decoder.cc b/quiche/quic/load_balancer/load_balancer_decoder.cc index 0bbb804..05d8696 100644 --- a/quiche/quic/load_balancer/load_balancer_decoder.cc +++ b/quiche/quic/load_balancer/load_balancer_decoder.cc
@@ -36,36 +36,34 @@ // This is the core logic to extract a server ID given a valid config and // connection ID of sufficient length. -LoadBalancerServerId LoadBalancerDecoder::GetServerId( - const QuicConnectionId& connection_id) const { +bool LoadBalancerDecoder::GetServerId(const QuicConnectionId& connection_id, + LoadBalancerServerId& server_id) const { std::optional<uint8_t> config_id = GetConfigId(connection_id); if (!config_id.has_value()) { - return LoadBalancerServerId(); + return false; } std::optional<LoadBalancerConfig> config = config_[*config_id]; if (!config.has_value()) { - return LoadBalancerServerId(); + return false; } // Benchmark tests show that minimizing the computation inside // LoadBalancerConfig saves CPU cycles. if (connection_id.length() < config->total_len()) { - return LoadBalancerServerId(); + return false; } const uint8_t* data = reinterpret_cast<const uint8_t*>(connection_id.data()) + 1; uint8_t server_id_len = config->server_id_len(); + server_id.set_length(server_id_len); if (!config->IsEncrypted()) { - return LoadBalancerServerId(absl::Span<const uint8_t>(data, server_id_len)); + memcpy(server_id.mutable_data(), connection_id.data() + 1, server_id_len); + return true; } if (config->plaintext_len() == kLoadBalancerBlockSize) { - uint8_t scratch[kLoadBalancerBlockSize]; - if (!config->BlockDecrypt(data, scratch)) { - return LoadBalancerServerId(); - } - return LoadBalancerServerId(absl::Span<uint8_t>(scratch, server_id_len)); + return config->BlockDecrypt(data, server_id.mutable_data()); } return config->FourPassDecrypt( - absl::MakeConstSpan(data, connection_id.length() - 1)); + absl::MakeConstSpan(data, connection_id.length() - 1), server_id); } std::optional<uint8_t> LoadBalancerDecoder::GetConfigId(
diff --git a/quiche/quic/load_balancer/load_balancer_decoder.h b/quiche/quic/load_balancer/load_balancer_decoder.h index 21eb8b7..a352711 100644 --- a/quiche/quic/load_balancer/load_balancer_decoder.h +++ b/quiche/quic/load_balancer/load_balancer_decoder.h
@@ -8,14 +8,21 @@ #include <cstdint> #include <optional> +#include "absl/base/attributes.h" #include "quiche/quic/core/quic_connection_id.h" #include "quiche/quic/load_balancer/load_balancer_config.h" #include "quiche/quic/load_balancer/load_balancer_server_id.h" +#include "quiche/quic/platform/api/quic_export.h" namespace quic { // Manages QUIC-LB configurations to extract a server ID from a properly // encoded connection ID, usually on behalf of a load balancer. +// +// IMPORTANT: The decoder data path is likely the most performance-sensitive +// part of the load balancer design, and this code has been carefully +// optimized for performance. Please do not make changes without running the +// benchmark tests to ensure there is no regression. class QUIC_EXPORT_PRIVATE LoadBalancerDecoder { public: // Returns false if the config_id codepoint is already occupied. @@ -36,11 +43,13 @@ return &*config_[config_id]; } - // Extract a server ID from |connection_id|. If there is no config for the - // codepoint, |connection_id| is too short, or there's a decrypt error, - // returns empty. Will accept |connection_id| that is longer than necessary - // without error. - LoadBalancerServerId GetServerId(const QuicConnectionId& connection_id) const; + // Extract a server ID from |connection_id| and write it into |server_id|. If + // there is no config for the codepoint, |connection_id| is too short, or + // there's a decrypt error, returns false. Will accept |connection_id| that is + // longer than necessary without error. If GetServerId() returns false, there + // are no guarantees about the properties of |server_id|. + ABSL_MUST_USE_RESULT bool GetServerId(const QuicConnectionId& connection_id, + LoadBalancerServerId& server_id) const; // Returns the config ID stored in the first two bits of |connection_id|, or // empty if |connection_id| is empty, or the first two bits of the first byte
diff --git a/quiche/quic/load_balancer/load_balancer_decoder_test.cc b/quiche/quic/load_balancer/load_balancer_decoder_test.cc index cc86d25..6e8a399 100644 --- a/quiche/quic/load_balancer/load_balancer_decoder_test.cc +++ b/quiche/quic/load_balancer/load_balancer_decoder_test.cc
@@ -56,8 +56,10 @@ }}; for (const auto& test : test_vectors) { LoadBalancerDecoder decoder; + LoadBalancerServerId answer; EXPECT_TRUE(decoder.AddConfig(test.config)); - EXPECT_EQ(decoder.GetServerId(test.connection_id), test.server_id); + EXPECT_TRUE(decoder.GetServerId(test.connection_id, answer)); + EXPECT_EQ(answer, test.server_id); } } @@ -97,21 +99,12 @@ for (const auto& test : test_vectors) { LoadBalancerDecoder decoder; EXPECT_TRUE(decoder.AddConfig(test.config)); - EXPECT_EQ(decoder.GetServerId(test.connection_id), test.server_id); + LoadBalancerServerId answer; + EXPECT_TRUE(decoder.GetServerId(test.connection_id, answer)); + EXPECT_EQ(answer, test.server_id); } } -TEST_F(LoadBalancerDecoderTest, NoServerIdEntry) { - LoadBalancerServerId server_id({0x01, 0x02, 0x03}); - EXPECT_TRUE(server_id.IsValid()); - LoadBalancerDecoder decoder; - EXPECT_TRUE( - decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(0, 3, 4))); - QuicConnectionId no_server_id_entry( - {0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); - EXPECT_TRUE(decoder.GetServerId(no_server_id_entry).IsValid()); -} - TEST_F(LoadBalancerDecoderTest, InvalidConfigId) { LoadBalancerServerId server_id({0x01, 0x02, 0x03}); EXPECT_TRUE(server_id.IsValid()); @@ -120,10 +113,10 @@ decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(1, 3, 4))); QuicConnectionId wrong_config_id( {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}); - EXPECT_FALSE(decoder - .GetServerId(QuicConnectionId( - {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(decoder.GetServerId( + QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + answer)); } TEST_F(LoadBalancerDecoderTest, UnroutableCodepoint) { @@ -132,10 +125,10 @@ LoadBalancerDecoder decoder; EXPECT_TRUE( decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(1, 3, 4))); - EXPECT_FALSE(decoder - .GetServerId(QuicConnectionId( - {0xe0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(decoder.GetServerId( + QuicConnectionId({0xe0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + answer)); } TEST_F(LoadBalancerDecoderTest, UnroutableCodepointAnyLength) { @@ -144,7 +137,8 @@ LoadBalancerDecoder decoder; EXPECT_TRUE( decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(1, 3, 4))); - EXPECT_FALSE(decoder.GetServerId(QuicConnectionId({0xff})).IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(decoder.GetServerId(QuicConnectionId({0xff}), answer)); } TEST_F(LoadBalancerDecoderTest, ConnectionIdTooShort) { @@ -153,10 +147,9 @@ LoadBalancerDecoder decoder; EXPECT_TRUE( decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(0, 3, 4))); - EXPECT_FALSE(decoder - .GetServerId(QuicConnectionId( - {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06})) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(decoder.GetServerId( + QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}), answer)); } TEST_F(LoadBalancerDecoderTest, ConnectionIdTooLongIsOK) { @@ -164,10 +157,11 @@ LoadBalancerDecoder decoder; EXPECT_TRUE( decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(0, 3, 4))); - auto server_id_result = decoder.GetServerId( - QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})); - EXPECT_TRUE(server_id_result.IsValid()); - EXPECT_EQ(server_id_result, server_id); + LoadBalancerServerId answer; + EXPECT_TRUE(decoder.GetServerId( + QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}), + answer)); + EXPECT_EQ(answer, server_id); } TEST_F(LoadBalancerDecoderTest, DeleteConfigBadId) { @@ -176,20 +170,20 @@ decoder.DeleteConfig(0); EXPECT_QUIC_BUG(decoder.DeleteConfig(7), "Decoder deleting config with invalid config_id 7"); - EXPECT_TRUE(decoder - .GetServerId(QuicConnectionId( - {0x40, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_TRUE(decoder.GetServerId( + QuicConnectionId({0x40, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + answer)); } TEST_F(LoadBalancerDecoderTest, DeleteConfigGoodId) { LoadBalancerDecoder decoder; decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(2, 3, 4)); decoder.DeleteConfig(2); - EXPECT_FALSE(decoder - .GetServerId(QuicConnectionId( - {0x40, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})) - .IsValid()); + LoadBalancerServerId answer; + EXPECT_FALSE(decoder.GetServerId( + QuicConnectionId({0x40, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + answer)); } // Create two server IDs and make sure the decoder decodes the correct one. @@ -200,12 +194,15 @@ LoadBalancerDecoder decoder; EXPECT_TRUE( decoder.AddConfig(*LoadBalancerConfig::CreateUnencrypted(0, 3, 4))); - EXPECT_EQ(decoder.GetServerId(QuicConnectionId( - {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})), - server_id1); - EXPECT_EQ(decoder.GetServerId(QuicConnectionId( - {0x00, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a})), - server_id2); + LoadBalancerServerId answer; + EXPECT_TRUE(decoder.GetServerId( + QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + answer)); + EXPECT_EQ(answer, server_id1); + EXPECT_TRUE(decoder.GetServerId( + QuicConnectionId({0x00, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}), + answer)); + EXPECT_EQ(answer, server_id2); } TEST_F(LoadBalancerDecoderTest, GetConfigId) { @@ -250,8 +247,9 @@ *LoadBalancerConfig::Create(0, 8, 8, absl::string_view(kRawKey, 16))); LoadBalancerServerId original_server_id(absl::Span<uint8_t>(&ptext[1], 8)); QuicConnectionId cid(absl::Span<uint8_t>(ctext, sizeof(ctext))); - LoadBalancerServerId server_id = decoder.GetServerId(cid); - EXPECT_EQ(server_id, original_server_id); + LoadBalancerServerId answer; + EXPECT_TRUE(decoder.GetServerId(cid, answer)); + EXPECT_EQ(answer, original_server_id); } } // namespace
diff --git a/quiche/quic/load_balancer/load_balancer_server_id.cc b/quiche/quic/load_balancer/load_balancer_server_id.cc index 9a4685c..4861adb 100644 --- a/quiche/quic/load_balancer/load_balancer_server_id.cc +++ b/quiche/quic/load_balancer/load_balancer_server_id.cc
@@ -17,17 +17,11 @@ namespace quic { LoadBalancerServerId::LoadBalancerServerId(absl::string_view data) - : LoadBalancerServerId( - absl::MakeSpan(reinterpret_cast<const uint8_t*>(data.data()), - data.length()), - absl::Span<const uint8_t>()) {} + : LoadBalancerServerId(absl::MakeSpan( + reinterpret_cast<const uint8_t*>(data.data()), data.length())) {} LoadBalancerServerId::LoadBalancerServerId(absl::Span<const uint8_t> data) - : LoadBalancerServerId(data, absl::Span<const uint8_t>()) {} - -LoadBalancerServerId::LoadBalancerServerId(absl::Span<const uint8_t> data1, - absl::Span<const uint8_t> data2) - : length_(data1.length() + data2.length()) { + : length_(data.length()) { if (length_ == 0 || length_ > kLoadBalancerMaxServerIdLen) { QUIC_BUG(quic_bug_433312504_02) << "Attempted to create LoadBalancerServerId with length " @@ -35,11 +29,15 @@ length_ = 0; return; } - memcpy(data_.data(), data1.data(), data1.length()); - if (data2.empty()) { - return; - } - memcpy(data_.data() + data1.length(), data2.data(), data2.length()); + memcpy(data_.data(), data.data(), data.length()); +} + +void LoadBalancerServerId::set_length(uint8_t length) { + QUIC_BUG_IF(quic_bug_599862571_01, + length == 0 || length > kLoadBalancerMaxServerIdLen) + << "Attempted to set LoadBalancerServerId length to " + << static_cast<int>(length); + length_ = length; } std::string LoadBalancerServerId::ToString() const {
diff --git a/quiche/quic/load_balancer/load_balancer_server_id.h b/quiche/quic/load_balancer/load_balancer_server_id.h index d60d308..713f3e2 100644 --- a/quiche/quic/load_balancer/load_balancer_server_id.h +++ b/quiche/quic/load_balancer/load_balancer_server_id.h
@@ -17,6 +17,10 @@ // The maximum number of bytes in a LoadBalancerServerId. inline constexpr uint8_t kLoadBalancerMaxServerIdLen = 15; +// Regardless of key length, the AES block size is always 16 Bytes. +inline constexpr uint8_t kLoadBalancerBlockSize = 16; +static_assert(kLoadBalancerMaxServerIdLen <= kLoadBalancerBlockSize, + "LoadBalancerServerId array not large enough to hold Server ID"); // LoadBalancerServerId is the globally understood identifier for a given pool // member. It is unique to any given QUIC-LB configuration. See @@ -33,10 +37,6 @@ // Copies all the bytes from |data| into a new LoadBalancerServerId. explicit LoadBalancerServerId(absl::Span<const uint8_t> data); explicit LoadBalancerServerId(absl::string_view data); - // Concatenates |data1| and |data2| into a single LoadBalancerServerId. This - // is useful to reduce copying for certain decoder configurations. - explicit LoadBalancerServerId(absl::Span<const uint8_t> data1, - absl::Span<const uint8_t> data2); // Server IDs are opaque bytes, but defining these operators allows us to sort // them into a tree and define ranges. @@ -57,7 +57,10 @@ absl::Span<const uint8_t> data() const { return absl::MakeConstSpan(data_.data(), length_); } + uint8_t* mutable_data() { return data_.data(); } + uint8_t length() const { return length_; } + void set_length(uint8_t length); // Returns the server ID in hex format. std::string ToString() const; @@ -66,7 +69,9 @@ bool IsValid() { return length_ != 0; } private: - std::array<uint8_t, kLoadBalancerMaxServerIdLen> data_; + // Make the array large enough to hold an entire decrypt result, to save a + // copy from the decrypt result into LoadBalancerServerId. + std::array<uint8_t, kLoadBalancerBlockSize> data_; uint8_t length_; };
diff --git a/quiche/quic/load_balancer/load_balancer_server_id_test.cc b/quiche/quic/load_balancer/load_balancer_server_id_test.cc index 08b5e6d..6b3bc52 100644 --- a/quiche/quic/load_balancer/load_balancer_server_id_test.cc +++ b/quiche/quic/load_balancer/load_balancer_server_id_test.cc
@@ -30,27 +30,11 @@ absl::Span<const uint8_t>(kRawServerId, 16)) .IsValid()), "Attempted to create LoadBalancerServerId with length 16"); - EXPECT_QUIC_BUG(EXPECT_FALSE(LoadBalancerServerId( - absl::Span<const uint8_t>(kRawServerId, 9), - absl::Span<const uint8_t>(kRawServerId, 7)) - .IsValid()), - "Attempted to create LoadBalancerServerId with length 16"); EXPECT_QUIC_BUG( EXPECT_FALSE(LoadBalancerServerId(absl::Span<const uint8_t>()).IsValid()), "Attempted to create LoadBalancerServerId with length 0"); } -TEST_F(LoadBalancerServerIdTest, TwoPartConstructor) { - LoadBalancerServerId server_id1(absl::Span<const uint8_t>(kRawServerId, 15)); - ASSERT_TRUE(server_id1.IsValid()); - LoadBalancerServerId server_id2( - absl::Span<const uint8_t>(kRawServerId, 8), - absl::Span<const uint8_t>(&kRawServerId[8], 7)); - ASSERT_TRUE(server_id2.IsValid()); - EXPECT_TRUE(server_id1 == server_id2); - ; -} - TEST_F(LoadBalancerServerIdTest, CompareIdenticalExceptLength) { LoadBalancerServerId server_id(absl::Span<const uint8_t>(kRawServerId, 15)); ASSERT_TRUE(server_id.IsValid()); @@ -111,6 +95,18 @@ })); } +TEST_F(LoadBalancerServerIdTest, SetLengthInvalid) { + LoadBalancerServerId server_id; + EXPECT_QUIC_BUG(server_id.set_length(16), + "Attempted to set LoadBalancerServerId length to 16"); + EXPECT_QUIC_BUG(server_id.set_length(0), + "Attempted to set LoadBalancerServerId length to 0"); + server_id.set_length(1); + EXPECT_EQ(server_id.length(), 1); + server_id.set_length(15); + EXPECT_EQ(server_id.length(), 15); +} + } // namespace } // namespace test