When a QUIC client probes from a new socket via a new writer or respond to peer's probing on a new socket, check if the socket is write blocked.

Currently QuicConnetion blindly write packets to the new socket, and let the platform to decide how to handle socket blockage case. It is okay if the platform silently drop the probing packet in this case. But Chrome added CHECKs to disallow QUIC to write to socket which is already blocked: https://bugs.chromium.org/p/chromium/issues/detail?id=1376754#c10. So this change is to address the CHECK violation there and unblock their further debugging on the original issue.

PiperOrigin-RevId: 538886916
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index f22f9b0..80945f6 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -5001,6 +5001,7 @@
     const QuicSocketAddress& self_address,
     const QuicSocketAddress& peer_address, bool measure_rtt) {
   const QuicTime packet_send_time = clock_->Now();
+  QUIC_BUG_IF(write using blocked writer, writer->IsWriteBlocked());
   QUIC_DVLOG(2) << ENDPOINT
                 << "Sending path probe packet for server connection ID "
                 << default_path_.server_connection_id << std::endl
@@ -6514,7 +6515,7 @@
       // other frames. This may cause connection to be closed.
       packet_creator_.AddPathChallengeFrame(data_buffer);
     }
-  } else {
+  } else if (!writer->IsWriteBlocked()) {
     // Switch to the right CID and source/peer addresses.
     QuicPacketCreator::ScopedPeerAddressContext context(
         &packet_creator_, peer_address, client_cid, server_cid);
@@ -6526,6 +6527,9 @@
     QUICHE_DCHECK_EQ(self_address, alternative_path_.self_address);
     WritePacketUsingWriter(std::move(probing_packet), writer, self_address,
                            peer_address, /*measure_rtt=*/false);
+  } else {
+    QUIC_DLOG(INFO) << ENDPOINT
+                    << "Writer blocked when sending PATH_CHALLENGE.";
   }
   return connected_;
 }
@@ -6631,6 +6635,10 @@
     return true;
   }
   QuicPacketWriter* writer = path_validator_.GetContext()->WriterToUse();
+  if (writer->IsWriteBlocked()) {
+    QUIC_DLOG(INFO) << ENDPOINT << "Writer blocked when sending PATH_RESPONSE.";
+    return true;
+  }
 
   std::unique_ptr<SerializedPacket> probing_packet =
       packet_creator_.SerializePathResponseConnectivityProbingPacket(
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index a26731f..0d48535 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -11407,6 +11407,61 @@
   EXPECT_TRUE(success);
 }
 
+TEST_P(QuicConnectionTest, PathValidationOnNewSocketWriteBlocked) {
+  if (!VersionHasIetfQuicFrames(connection_.version().transport_version)) {
+    return;
+  }
+  PathProbeTestInit(Perspective::IS_CLIENT);
+  const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Any4(), 12345);
+  EXPECT_NE(kNewSelfAddress, connection_.self_address());
+  TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+  new_writer.SetWriteBlocked();
+  bool success = false;
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  connection_.ValidatePath(
+      std::make_unique<TestQuicPathValidationContext>(
+          kNewSelfAddress, connection_.peer_address(), &new_writer),
+      std::make_unique<TestValidationResultDelegate>(
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
+  EXPECT_EQ(0u, new_writer.packets_write_attempts());
+  EXPECT_TRUE(connection_.HasPendingPathValidation());
+
+  new_writer.SetWritable();
+  // Retry after time out.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+  static_cast<test::MockRandom*>(helper_->GetRandomGenerator())->ChangeValue();
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillOnce(Invoke([&]() {
+        EXPECT_EQ(1u, new_writer.packets_write_attempts());
+        EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
+        EXPECT_EQ(1u, new_writer.padding_frames().size());
+        EXPECT_EQ(kNewSelfAddress.host(),
+                  new_writer.last_write_source_address());
+      }));
+  static_cast<TestAlarmFactory::TestAlarm*>(
+      QuicPathValidatorPeer::retry_timer(
+          QuicConnectionPeer::path_validator(&connection_)))
+      ->Fire();
+  EXPECT_EQ(1u, new_writer.packets_write_attempts());
+
+  QuicFrames frames;
+  QuicPathFrameBuffer path_frame_buffer{0, 1, 2, 3, 4, 5, 6, 7};
+  frames.push_back(QuicFrame(QuicPathChallengeFrame(0, path_frame_buffer)));
+  new_writer.SetWriteBlocked();
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillRepeatedly(Invoke([&] {
+        // Packets other than PATH_RESPONSE may be sent over the default writer.
+        EXPECT_EQ(1u, new_writer.packets_write_attempts());
+        EXPECT_TRUE(new_writer.path_response_frames().empty());
+        EXPECT_EQ(1u, writer_->packets_write_attempts());
+      }));
+  ProcessFramesPacketWithAddresses(frames, kNewSelfAddress,
+                                   connection_.peer_address(),
+                                   ENCRYPTION_FORWARD_SECURE);
+  EXPECT_EQ(1u, new_writer.packets_write_attempts());
+}
+
 TEST_P(QuicConnectionTest, NewPathValidationCancelsPreviousOne) {
   if (!VersionHasIetfQuicFrames(connection_.version().transport_version)) {
     return;
@@ -11970,7 +12025,7 @@
 }
 
 // Tests that a PATH_CHALLENGE is cached if its PATH_RESPONSE can't be sent.
-TEST_P(QuicConnectionTest, FailToWritePathResponse) {
+TEST_P(QuicConnectionTest, FailToWritePathResponseAtServer) {
   if (!VersionHasIetfQuicFrames(connection_.version().transport_version)) {
     return;
   }