Refactor quic_client_interop to avoid dropping letters

gfe-relnote: n/a, test-only
PiperOrigin-RevId: 275510992
Change-Id: I997d1dccf444b370af2f51f39cac93d4a456f656
diff --git a/quic/tools/quic_client_interop_test_bin.cc b/quic/tools/quic_client_interop_test_bin.cc
index 278a22e..be65979 100644
--- a/quic/tools/quic_client_interop_test_bin.cc
+++ b/quic/tools/quic_client_interop_test_bin.cc
@@ -70,8 +70,14 @@
 std::set<Feature> AttemptRequest(QuicSocketAddress addr,
                                  std::string authority,
                                  QuicServerId server_id,
-                                 ParsedQuicVersionVector versions,
+                                 bool test_version_negotiation,
                                  bool attempt_rebind) {
+  ParsedQuicVersion version(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+  ParsedQuicVersionVector versions = {version};
+  if (test_version_negotiation) {
+    versions.insert(versions.begin(), QuicVersionReservedForNegotiation());
+  }
+
   std::set<Feature> features;
   auto proof_verifier = std::make_unique<FakeProofVerifier>();
   QuicEpollServer epoll_server;
@@ -79,18 +85,28 @@
   auto client = std::make_unique<QuicClient>(
       addr, server_id, versions, &epoll_server, std::move(proof_verifier));
   if (!client->Initialize()) {
+    QUIC_LOG(ERROR) << "Failed to initialize client";
     return features;
   }
-  if (!client->Connect()) {
-    QuicErrorCode error = client->session()->error();
-    if (error == QUIC_INVALID_VERSION) {
-      // QuicFramer::ProcessPacket returns RaiseError(QUIC_INVALID_VERSION) if
-      // it receives a packet containing a version in the header that is not our
-      // version. It might be possible that we didn't actually process a VN
-      // packet here.
-      features.insert(Feature::kVersionNegotiation);
-      return features;
+  const bool connect_result = client->Connect();
+  QuicConnection* connection = client->session()->connection();
+  if (connection != nullptr) {
+    QuicConnectionStats client_stats = connection->GetStats();
+    if (client_stats.retry_packet_processed) {
+      features.insert(Feature::kRetry);
     }
+    if (test_version_negotiation && connection->version() == version) {
+      features.insert(Feature::kVersionNegotiation);
+    }
+  }
+  if (test_version_negotiation && !connect_result) {
+    // Failed to negotiate version, retry without version negotiation.
+    std::set<Feature> features_without_version_negotiation =
+        AttemptRequest(addr, authority, server_id,
+                       /*test_version_negotiation=*/false, attempt_rebind);
+
+    features.insert(features_without_version_negotiation.begin(),
+                    features_without_version_negotiation.end());
     return features;
   }
   if (!client->session()->IsCryptoHandshakeConfirmed()) {
@@ -109,19 +125,17 @@
 
   const QuicTime request_start_time = epoll_clock.Now();
   static const auto request_timeout = QuicTime::Delta::FromSeconds(20);
+  bool request_timed_out = false;
   while (client->WaitForEvents()) {
     if (epoll_clock.Now() - request_start_time >= request_timeout) {
       QUIC_LOG(ERROR) << "Timed out waiting for HTTP response";
-      return features;
+      request_timed_out = true;
+      break;
     }
   }
 
-  QuicConnection* connection = client->session()->connection();
   if (connection != nullptr) {
     QuicConnectionStats client_stats = connection->GetStats();
-    if (client_stats.retry_packet_processed) {
-      features.insert(Feature::kRetry);
-    }
     QuicSentPacketManager* sent_packet_manager =
         test::QuicConnectionPeer::GetSentPacketManager(connection);
     const bool received_forward_secure_ack =
@@ -133,7 +147,7 @@
     }
   }
 
-  if (!client->connected()) {
+  if (request_timed_out || !client->connected()) {
     return features;
   }
 
@@ -150,8 +164,12 @@
           if (epoll_clock.Now() - second_request_start_time >=
               request_timeout) {
             // Rebinding does not work, retry without attempting it.
-            return AttemptRequest(addr, authority, server_id, versions,
-                                  /*attempt_rebind=*/false);
+            std::set<Feature> features_without_rebind = AttemptRequest(
+                addr, authority, server_id, test_version_negotiation,
+                /*attempt_rebind=*/false);
+            features.insert(features_without_rebind.begin(),
+                            features_without_rebind.end());
+            return features;
           }
         }
         features.insert(Feature::kRebinding);
@@ -187,31 +205,22 @@
 }
 
 std::set<Feature> ServerSupport(std::string host, int port) {
-  // Configure version list.
+  // Enable IETF version support.
   QuicVersionInitializeSupportForIetfDraft();
-  ParsedQuicVersion version =
-      ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
-  ParsedQuicVersionVector versions = {version};
-  QuicEnableVersion(version);
+  QuicEnableVersion(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99));
 
   // Build the client, and try to connect.
   QuicSocketAddress addr = tools::LookupAddress(host, QuicStrCat(port));
+  if (!addr.IsInitialized()) {
+    QUIC_LOG(ERROR) << "Failed to resolve " << host;
+    return std::set<Feature>();
+  }
   QuicServerId server_id(host, port, false);
   std::string authority = QuicStrCat(host, ":", port);
 
-  ParsedQuicVersionVector versions_with_negotiation = versions;
-  versions_with_negotiation.insert(versions_with_negotiation.begin(),
-                                   QuicVersionReservedForNegotiation());
-  auto supported_features =
-      AttemptRequest(addr, authority, server_id, versions_with_negotiation,
-                     /*attempt_rebind=*/true);
-  if (!supported_features.empty()) {
-    supported_features.insert(Feature::kVersionNegotiation);
-  } else {
-    supported_features = AttemptRequest(addr, authority, server_id, versions,
-                                        /*attempt_rebind=*/true);
-  }
-  return supported_features;
+  return AttemptRequest(addr, authority, server_id,
+                        /*test_version_negotiation=*/true,
+                        /*attempt_rebind=*/true);
 }
 
 }  // namespace quic