Set the failure reason before reporting a failure when no CID is available ValidatePath is not setting a failure reason before failing due to lack of unused connection IDs. This leads to OnPathValidationFailure being called with a nullopt reason, making it impossible for embedders to identify and prioritize connection migration failures. Similarly, there is currently no reason for embedders to use when canceling a path validation due to a writer error. Add a new kWriterError to cover this scenario. See https://crrev.com/c/8444838 for the Chromium changes that will land once QUICHE is rolled there. This should make the kProbeFailed/"Probe failed" bucket for the new Net.Quic.Migration.Attempt.FailureReason UMA actionable. Finally, mark kUnknown as deprecated: an actual failure reason should always be used. PiperOrigin-RevId: 988257046
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc index 7553af5..beda35d 100644 --- a/quiche/quic/core/quic_connection.cc +++ b/quiche/quic/core/quic_connection.cc
@@ -6951,7 +6951,9 @@ } if (!HasUnusedConnectionId()) { QUIC_DVLOG(1) << "Client cannot start new path validation as there is no " - "requried connection ID is available."; + "unused connection ID available."; + context->set_failure_reason( + PathValidationFailure::Reason::kNoAvailableConnectionId); result_delegate->OnPathValidationFailure(std::move(context)); return; }
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index b79f9fb..58ae0f1 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc
@@ -12103,11 +12103,13 @@ new_writer.last_write_source_address()); }); bool success = true; + std::optional<PathValidationFailure::Reason> failure_reason; connection_.ValidatePath( std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - &connection_, kNewSelfAddress, connection_.peer_address(), &success), + &connection_, kNewSelfAddress, connection_.peer_address(), &success, + &failure_reason), PathValidationReason::kReasonUnknown); EXPECT_EQ(0u, writer_->packets_write_attempts()); @@ -12116,15 +12118,20 @@ EXPECT_NE(kNewSelfAddress2, connection_.self_address()); TestPacketWriter new_writer2(version(), &clock_, Perspective::IS_CLIENT); bool success2 = false; + std::optional<PathValidationFailure::Reason> failure_reason2; connection_.ValidatePath( std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress2, connection_.peer_address(), &new_writer2), std::make_unique<TestValidationResultDelegate>( - &connection_, kNewSelfAddress2, connection_.peer_address(), - &success2), + &connection_, kNewSelfAddress2, connection_.peer_address(), &success2, + &failure_reason2), PathValidationReason::kReasonUnknown); EXPECT_FALSE(success); - // There is no pening path validation as there is no available connection ID. + EXPECT_EQ(failure_reason, PathValidationFailure::Reason::kNewerValidation); + EXPECT_FALSE(success2); + EXPECT_EQ(failure_reason2, + PathValidationFailure::Reason::kNoAvailableConnectionId); + // There is no pending path validation as there is no available connection ID. EXPECT_FALSE(connection_.HasPendingPathValidation()); }
diff --git a/quiche/quic/core/quic_path_validator.h b/quiche/quic/core/quic_path_validator.h index 6f0a3d6..5881163 100644 --- a/quiche/quic/core/quic_path_validator.h +++ b/quiche/quic/core/quic_path_validator.h
@@ -45,12 +45,23 @@ struct QUICHE_EXPORT PathValidationFailure { enum class Reason : uint8_t { + // Do not use this. Instead, create a new enum value specific to the new + // failure scenario. kUnknown = 0, - kStatelessReset = 1, // The peer sent a stateless reset on the path. - kNewerValidation = 2, // Starting validation on a new path. - kRetryTimeout = 3, // The validation process hit the retry limit. - kNotConnected = 4, // PATH_CHALLENGE can't be sent because the - // connection is no longer active. + // The peer sent a stateless reset on the path. + kStatelessReset = 1, + // Starting validation on a new path. + kNewerValidation = 2, + // The validation process hit the retry limit. + kRetryTimeout = 3, + // PATH_CHALLENGE can't be sent because the connection is no longer active. + kNotConnected = 4, + // No unused connection ID was available, so validation could not start. + // No PATH_CHALLENGE was ever sent on this path. + kNoAvailableConnectionId = 5, + // Writing to the path failed. The writer belongs to the caller, so this is + // normally reported by the caller while cancelling its own validation. + kWriterError = 6, }; Reason reason; };