Add QuicPathValidator implementation. PiperOrigin-RevId: 338150507 Change-Id: I96f0f70e5037a854618da69387d6002dc5e716e4
diff --git a/quic/core/quic_path_validator.cc b/quic/core/quic_path_validator.cc new file mode 100644 index 0000000..5d5e720 --- /dev/null +++ b/quic/core/quic_path_validator.cc
@@ -0,0 +1,128 @@ +// Copyright (c) 2020 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 "net/third_party/quiche/src/quic/core/quic_path_validator.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +class RetryAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit RetryAlarmDelegate(QuicPathValidator* path_validator) + : path_validator_(path_validator) {} + RetryAlarmDelegate(const RetryAlarmDelegate&) = delete; + RetryAlarmDelegate& operator=(const RetryAlarmDelegate&) = delete; + + void OnAlarm() override { path_validator_->OnRetryTimeout(); } + + private: + QuicPathValidator* path_validator_; +}; + +std::ostream& operator<<(std::ostream& os, + const QuicPathValidationContext& context) { + return os << " from " << context.self_address_ << " to " + << context.peer_address_; +} + +QuicPathValidator::QuicPathValidator(QuicAlarmFactory* alarm_factory, + QuicOneBlockArena<1024>* arena, + SendDelegate* send_delegate, + QuicRandom* random) + : send_delegate_(send_delegate), + random_(random), + retry_timer_( + alarm_factory->CreateAlarm(arena->New<RetryAlarmDelegate>(this), + arena)), + retry_count_(0u) {} + +void QuicPathValidator::OnPathResponse(const QuicPathFrameBuffer& probing_data, + QuicSocketAddress self_address) { + if (!HasPendingPathValidation()) { + return; + } + + QUIC_DVLOG(1) << "Match PATH_RESPONSE received on " << self_address; + QUIC_BUG_IF(!path_context_->self_address().IsInitialized()) + << "Self address should have been known by now"; + if (self_address != path_context_->self_address()) { + QUIC_DVLOG(1) << "Expect the response to be received on " + << path_context_->self_address(); + return; + } + // This iterates at most 3 times. + if (std::find(probing_data_.begin(), probing_data_.end(), probing_data) != + probing_data_.end()) { + result_delegate_->OnPathValidationSuccess(std::move(path_context_)); + ResetPathValidation(); + } +} + +void QuicPathValidator::StartValidingPath( + std::unique_ptr<QuicPathValidationContext> context, + std::unique_ptr<ResultDelegate> result_delegate) { + CancelPathValidation(); + DCHECK_NE(nullptr, context); + QUIC_DLOG(INFO) << "Start validating path " << *context + << " via writer: " << context->WriterToUse(); + + path_context_ = std::move(context); + result_delegate_ = std::move(result_delegate); + SendPathChallengeAndSetAlarm(); +} + +void QuicPathValidator::ResetPathValidation() { + path_context_ = nullptr; + result_delegate_ = nullptr; + retry_timer_->Cancel(); + retry_count_ = 0; +} + +void QuicPathValidator::CancelPathValidation() { + if (path_context_ == nullptr) { + return; + } + QUIC_DVLOG(1) << "Cancel validation on path" << *path_context_; + ResetPathValidation(); +} + +bool QuicPathValidator::HasPendingPathValidation() const { + return path_context_ != nullptr; +} + +const QuicPathFrameBuffer& QuicPathValidator::GeneratePathChallengePayload() { + probing_data_.push_back(QuicPathFrameBuffer()); + random_->RandBytes(probing_data_.back().data(), sizeof(QuicPathFrameBuffer)); + return probing_data_.back(); +} + +void QuicPathValidator::OnRetryTimeout() { + ++retry_count_; + if (retry_count_ > kMaxRetryTimes) { + result_delegate_->OnPathValidationFailure(std::move(path_context_)); + CancelPathValidation(); + return; + } + QUIC_DVLOG(1) << "Send another PATH_CHALLENGE on path " << *path_context_; + SendPathChallengeAndSetAlarm(); +} + +void QuicPathValidator::SendPathChallengeAndSetAlarm() { + bool should_continue = send_delegate_->SendPathChallenge( + GeneratePathChallengePayload(), path_context_->self_address(), + path_context_->peer_address(), path_context_->WriterToUse()); + + if (!should_continue) { + // The delegate doesn't want to continue the path validation. + CancelPathValidation(); + return; + } + retry_timer_->Set(send_delegate_->GetRetryTimeout( + path_context_->peer_address(), path_context_->WriterToUse())); +} + +} // namespace quic
diff --git a/quic/core/quic_path_validator.h b/quic/core/quic_path_validator.h new file mode 100644 index 0000000..8cfa81d --- /dev/null +++ b/quic/core/quic_path_validator.h
@@ -0,0 +1,135 @@ +// Copyright (c) 2020 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_CORE_QUIC_PATH_VALIDATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_PATH_VALIDATOR_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h" +#include "net/third_party/quiche/src/quic/core/quic_clock.h" +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/quic/platform/impl/quic_export_impl.h" + +namespace quic { + +namespace test { +class QuicPathValidatorPeer; +} + +class QuicConnection; + +// Interface to provide the information of the path to be validated. +class QUIC_EXPORT_PRIVATE QuicPathValidationContext { + public: + QuicPathValidationContext(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) + : self_address_(self_address), peer_address_(peer_address) {} + + virtual ~QuicPathValidationContext() = default; + + virtual QuicPacketWriter* WriterToUse() = 0; + + const QuicSocketAddress& self_address() const { return self_address_; } + const QuicSocketAddress& peer_address() const { return peer_address_; } + + private: + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicPathValidationContext& context); + + QuicSocketAddress self_address_; + QuicSocketAddress peer_address_; +}; + +// Used to validate a path by sending up to 3 PATH_CHALLENGE frames before +// declaring a path validation failure. +class QUIC_EXPORT_PRIVATE QuicPathValidator { + public: + static const uint16_t kMaxRetryTimes = 2; + + // Used to write PATH_CHALLENGE on the path to be validated and to get retry + // timeout. + class QUIC_EXPORT_PRIVATE SendDelegate { + public: + virtual ~SendDelegate() = default; + + // Send a PATH_CHALLENGE with |data_buffer| as the frame payload using given + // path information. Return false if the delegate doesn't want to continue + // the validation. + virtual bool SendPathChallenge(const QuicPathFrameBuffer& data_buffer, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicPacketWriter* writer) = 0; + // Return the time to retry sending PATH_CHALLENGE again based on given peer + // address and writer. + virtual QuicTime GetRetryTimeout(const QuicSocketAddress& peer_address, + QuicPacketWriter* writer) const = 0; + }; + + // Handles the validation result. + class QUIC_EXPORT_PRIVATE ResultDelegate { + public: + virtual ~ResultDelegate() = default; + + virtual void OnPathValidationSuccess( + std::unique_ptr<QuicPathValidationContext> context) = 0; + + virtual void OnPathValidationFailure( + std::unique_ptr<QuicPathValidationContext> context) = 0; + }; + + QuicPathValidator(QuicAlarmFactory* alarm_factory, + QuicConnectionArena* arena, + SendDelegate* delegate, + QuicRandom* random); + + // Send PATH_CHALLENGE and start the retry timer. + void StartValidingPath(std::unique_ptr<QuicPathValidationContext> context, + std::unique_ptr<ResultDelegate> result_delegate); + + // Called when a PATH_RESPONSE frame has been received. Matches the received + // PATH_RESPONSE payload with the payloads previously sent in PATH_CHALLANGE + // frames and the self address on which it was sent. + void OnPathResponse(const QuicPathFrameBuffer& probing_data, + QuicSocketAddress self_address); + + // Cancel the retry timer and reset the path and result delegate. + void CancelPathValidation(); + + bool HasPendingPathValidation() const; + + // Send another PATH_CHALLENGE on the same path. After retrying + // |kMaxRetryTimes| times, fail the current path validation. + void OnRetryTimeout(); + + private: + friend class test::QuicPathValidatorPeer; + + // Return the payload to be used in the next PATH_CHALLENGE frame. + const QuicPathFrameBuffer& GeneratePathChallengePayload(); + + void SendPathChallengeAndSetAlarm(); + + void ResetPathValidation(); + + // Has at most 3 entries due to validation timeout. + QuicInlinedVector<QuicPathFrameBuffer, 3> probing_data_; + SendDelegate* send_delegate_; + QuicRandom* random_; + std::unique_ptr<QuicPathValidationContext> path_context_; + std::unique_ptr<ResultDelegate> result_delegate_; + QuicArenaScopedPtr<QuicAlarm> retry_timer_; + size_t retry_count_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PATH_VALIDATOR_H_
diff --git a/quic/core/quic_path_validator_test.cc b/quic/core/quic_path_validator_test.cc new file mode 100644 index 0000000..656292e --- /dev/null +++ b/quic/core/quic_path_validator_test.cc
@@ -0,0 +1,242 @@ +// Copyright (c) 2020 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 "net/third_party/quiche/src/quic/core/quic_path_validator.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_path_validator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h" + +using testing::_; +using testing::Invoke; +using testing::Return; + +namespace quic { +namespace test { + +class MockSendDelegate : public QuicPathValidator::SendDelegate { + public: + // Send a PATH_CHALLENGE frame using given path information and populate + // |data_buffer| with the frame payload. Return true if the validator should + // move forward in validation, i.e. arm the retry timer. + MOCK_METHOD(bool, + SendPathChallenge, + (const QuicPathFrameBuffer&, + const QuicSocketAddress&, + const QuicSocketAddress&, + QuicPacketWriter*), + (override)); + + MOCK_METHOD(QuicTime, + GetRetryTimeout, + (const QuicSocketAddress&, QuicPacketWriter*), + (const override)); +}; + +class QuicPathValidatorTest : public QuicTest { + public: + QuicPathValidatorTest() + : path_validator_(&alarm_factory_, &arena_, &send_delegate_, &random_), + context_(new MockQuicPathValidationContext(self_address_, + peer_address_, + &writer_)), + result_delegate_(new MockQuicPathValidationResultDelegate()) { + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + ON_CALL(send_delegate_, GetRetryTimeout(_, _)) + .WillByDefault( + Return(clock_.ApproximateNow() + + 3 * QuicTime::Delta::FromMilliseconds(kInitialRttMs))); + } + + protected: + quic::test::MockAlarmFactory alarm_factory_; + MockSendDelegate send_delegate_; + MockRandom random_; + MockClock clock_; + QuicConnectionArena arena_; + QuicPathValidator path_validator_; + QuicSocketAddress self_address_{QuicIpAddress::Any4(), 443}; + QuicSocketAddress peer_address_{QuicIpAddress::Loopback4(), 443}; + MockPacketWriter writer_; + MockQuicPathValidationContext* context_; + MockQuicPathValidationResultDelegate* result_delegate_; +}; + +TEST_F(QuicPathValidatorTest, PathValidationSuccessOnFirstRound) { + QuicPathFrameBuffer challenge_data; + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + memcpy(challenge_data.data(), payload.data(), payload.size()); + return true; + })); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + EXPECT_TRUE(path_validator_.HasPendingPathValidation()); + EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_)) + .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) { + EXPECT_EQ(context.get(), context_); + })); + path_validator_.OnPathResponse(challenge_data, self_address_); + EXPECT_FALSE(path_validator_.HasPendingPathValidation()); +} + +TEST_F(QuicPathValidatorTest, RespondWithDifferentSelfAddress) { + QuicPathFrameBuffer challenge_data; + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .WillOnce(Invoke([&](const QuicPathFrameBuffer payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + memcpy(challenge_data.data(), payload.data(), payload.size()); + return true; + })); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + + // Reception of a PATH_RESPONSE on a different self address should be ignored. + const QuicSocketAddress kAlternativeSelfAddress(QuicIpAddress::Any6(), 54321); + EXPECT_NE(kAlternativeSelfAddress, self_address_); + path_validator_.OnPathResponse(challenge_data, kAlternativeSelfAddress); + + EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_)) + .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) { + EXPECT_EQ(context->self_address(), self_address_); + })); + path_validator_.OnPathResponse(challenge_data, self_address_); +} + +TEST_F(QuicPathValidatorTest, RespondAfter1stRetry) { + QuicPathFrameBuffer challenge_data; + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + // Store up the 1st PATH_CHALLANGE payload. + memcpy(challenge_data.data(), payload.data(), payload.size()); + return true; + })) + .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + EXPECT_NE(payload, challenge_data); + return true; + })); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)) + .Times(2u); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); + random_.ChangeValue(); + alarm_factory_.FireAlarm( + QuicPathValidatorPeer::retry_timer(&path_validator_)); + + EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_)); + // Respond to the 1st PATH_CHALLENGE should complete the validation. + path_validator_.OnPathResponse(challenge_data, self_address_); + EXPECT_FALSE(path_validator_.HasPendingPathValidation()); +} + +TEST_F(QuicPathValidatorTest, RespondToRetryChallenge) { + QuicPathFrameBuffer challenge_data; + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + memcpy(challenge_data.data(), payload.data(), payload.size()); + return true; + })) + .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload, + const QuicSocketAddress&, const QuicSocketAddress&, + QuicPacketWriter*) { + EXPECT_NE(challenge_data, payload); + memcpy(challenge_data.data(), payload.data(), payload.size()); + return true; + })); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)) + .Times(2u); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); + random_.ChangeValue(); + alarm_factory_.FireAlarm( + QuicPathValidatorPeer::retry_timer(&path_validator_)); + + // Respond to the 2nd PATH_CHALLENGE should complete the validation. + EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_)); + path_validator_.OnPathResponse(challenge_data, self_address_); + EXPECT_FALSE(path_validator_.HasPendingPathValidation()); +} + +TEST_F(QuicPathValidatorTest, ValidationTimeOut) { + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .Times(3u) + .WillRepeatedly(Return(true)); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)) + .Times(3u); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + + QuicPathFrameBuffer challenge_data; + memset(challenge_data.data(), 'a', challenge_data.size()); + // Reception of a PATH_RESPONSE with different payload should be ignored. + path_validator_.OnPathResponse(challenge_data, self_address_); + + // Retry 3 times. The 3rd time should fail the validation. + EXPECT_CALL(*result_delegate_, OnPathValidationFailure(_)) + .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) { + EXPECT_EQ(context_, context.get()); + })); + for (size_t i = 0; i <= QuicPathValidator::kMaxRetryTimes; ++i) { + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); + alarm_factory_.FireAlarm( + QuicPathValidatorPeer::retry_timer(&path_validator_)); + } +} + +TEST_F(QuicPathValidatorTest, SendPathChallengeError) { + EXPECT_CALL(send_delegate_, + SendPathChallenge(_, self_address_, peer_address_, &writer_)) + .WillOnce(Invoke([&](const QuicPathFrameBuffer&, const QuicSocketAddress&, + const QuicSocketAddress&, QuicPacketWriter*) { + // Abandon this validation in the call stack shouldn't cause crash and + // should cancel the alarm. + path_validator_.CancelPathValidation(); + return false; + })); + EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_)) + .Times(0u); + path_validator_.StartValidingPath( + std::unique_ptr<QuicPathValidationContext>(context_), + std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_)); + EXPECT_FALSE(path_validator_.HasPendingPathValidation()); + EXPECT_FALSE(QuicPathValidatorPeer::retry_timer(&path_validator_)->IsSet()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/test_tools/quic_path_validator_peer.cc b/quic/test_tools/quic_path_validator_peer.cc new file mode 100644 index 0000000..54d97d0 --- /dev/null +++ b/quic/test_tools/quic_path_validator_peer.cc
@@ -0,0 +1,15 @@ +// Copyright (c) 2020 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 "net/third_party/quiche/src/quic/test_tools/quic_path_validator_peer.h" + +namespace quic { +namespace test { +// static +QuicAlarm* QuicPathValidatorPeer::retry_timer(QuicPathValidator* validator) { + return validator->retry_timer_.get(); +} + +} // namespace test +} // namespace quic
diff --git a/quic/test_tools/quic_path_validator_peer.h b/quic/test_tools/quic_path_validator_peer.h new file mode 100644 index 0000000..5b2b56c --- /dev/null +++ b/quic/test_tools/quic_path_validator_peer.h
@@ -0,0 +1,20 @@ +// Copyright (c) 2020 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_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_ +#define QUICHE_QUIC_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_path_validator.h" + +namespace quic { +namespace test { + +class QuicPathValidatorPeer { + public: + static QuicAlarm* retry_timer(QuicPathValidator* validator); +}; + +} // namespace test +} // namespace quic +#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index a5a774d..8b53f8f 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -24,6 +24,7 @@ #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_path_validator.h" #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h" #include "net/third_party/quiche/src/quic/core/quic_server_id.h" #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" @@ -1627,6 +1628,33 @@ MOCK_METHOD(bool, HasUnackedStreamData, (), (const, override)); }; +class MockQuicPathValidationContext : public QuicPathValidationContext { + public: + MockQuicPathValidationContext(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicPacketWriter* writer) + : QuicPathValidationContext(self_address, peer_address), + writer_(writer) {} + QuicPacketWriter* WriterToUse() override { return writer_; } + + private: + QuicPacketWriter* writer_; +}; + +class MockQuicPathValidationResultDelegate + : public QuicPathValidator::ResultDelegate { + public: + MOCK_METHOD(void, + OnPathValidationSuccess, + (std::unique_ptr<QuicPathValidationContext>), + (override)); + + MOCK_METHOD(void, + OnPathValidationFailure, + (std::unique_ptr<QuicPathValidationContext>), + (override)); +}; + class QuicCryptoClientStreamPeer { public: QuicCryptoClientStreamPeer() = delete;