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;