Adopt QuicConnectionDebugVisitor from quic_client_interop_test

This CL refactors quic_client_interop_test into a class to allow adopting QuicConnectionDebugVisitor which improves the tool's reporting of the C and V letters.

gfe-relnote: n/a, test-tool-only
PiperOrigin-RevId: 293104152
Change-Id: I36c50bc44a7187ccb35437ec1b617b1d922faabc
diff --git a/quic/tools/quic_client_base.cc b/quic/tools/quic_client_base.cc
index 798ed5f..9249ddc 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -36,7 +36,8 @@
       num_sent_client_hellos_(0),
       connection_error_(QUIC_NO_ERROR),
       connected_or_attempting_connect_(false),
-      network_helper_(std::move(network_helper)) {}
+      network_helper_(std::move(network_helper)),
+      connection_debug_visitor_(nullptr) {}
 
 QuicClientBase::~QuicClientBase() = default;
 
@@ -114,6 +115,9 @@
                          can_reconnect_with_different_version
                              ? ParsedQuicVersionVector{mutual_version}
                              : supported_versions()));
+  if (connection_debug_visitor_ != nullptr) {
+    session()->connection()->set_debug_visitor(connection_debug_visitor_);
+  }
   session()->connection()->set_client_connection_id(GetClientConnectionId());
   if (initial_max_packet_length_ != 0) {
     session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
diff --git a/quic/tools/quic_client_base.h b/quic/tools/quic_client_base.h
index 3160b74..91a9587 100644
--- a/quic/tools/quic_client_base.h
+++ b/quic/tools/quic_client_base.h
@@ -213,6 +213,11 @@
     crypto_config_.set_pre_shared_key(key);
   }
 
+  void set_connection_debug_visitor(
+      QuicConnectionDebugVisitor* connection_debug_visitor) {
+    connection_debug_visitor_ = connection_debug_visitor;
+  }
+
  protected:
   // TODO(rch): Move GetNumSentClientHellosFromSession and
   // GetNumReceivedServerConfigUpdatesFromSession into a new/better
@@ -331,6 +336,10 @@
   // The network helper used to create sockets and manage the event loop.
   // Not owned by this class.
   std::unique_ptr<NetworkHelper> network_helper_;
+
+  // The debug visitor set on the connection right after it is constructed.
+  // Not owned, must be valid for the lifetime of the QuicClientBase instance.
+  QuicConnectionDebugVisitor* connection_debug_visitor_;
 };
 
 }  // namespace quic
diff --git a/quic/tools/quic_client_interop_test_bin.cc b/quic/tools/quic_client_interop_test_bin.cc
index b11065f..9638eb9 100644
--- a/quic/tools/quic_client_interop_test_bin.cc
+++ b/quic/tools/quic_client_interop_test_bin.cc
@@ -79,10 +79,53 @@
   }
 }
 
-// Attempts a resumption using |client| by disconnecting and reconnecting. If
-// resumption is successful, |features| is modified to add Feature::kResumption
-// to it, otherwise it is left unmodified.
-void AttemptResumption(QuicClient* client, std::set<Feature>* features) {
+class QuicClientInteropRunner : QuicConnectionDebugVisitor {
+ public:
+  QuicClientInteropRunner() {}
+
+  void InsertFeature(Feature feature) { features_.insert(feature); }
+
+  std::set<Feature> features() const { return features_; }
+
+  // Attempts a resumption using |client| by disconnecting and reconnecting. If
+  // resumption is successful, |features_| is modified to add
+  // Feature::kResumption to it, otherwise it is left unmodified.
+  void AttemptResumption(QuicClient* client);
+
+  void AttemptRequest(QuicSocketAddress addr,
+                      std::string authority,
+                      QuicServerId server_id,
+                      bool test_version_negotiation,
+                      bool attempt_rebind);
+
+  void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+    switch (frame.close_type) {
+      case GOOGLE_QUIC_CONNECTION_CLOSE:
+        QUIC_LOG(ERROR) << "Received unexpected GoogleQUIC connection close";
+        break;
+      case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
+        if (frame.transport_error_code == NO_IETF_QUIC_ERROR) {
+          InsertFeature(Feature::kConnectionClose);
+        }
+        break;
+      case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
+        if (frame.application_error_code == 0) {
+          InsertFeature(Feature::kConnectionClose);
+        }
+        break;
+    }
+  }
+
+  void OnVersionNegotiationPacket(
+      const QuicVersionNegotiationPacket& /*packet*/) override {
+    InsertFeature(Feature::kVersionNegotiation);
+  }
+
+ private:
+  std::set<Feature> features_;
+};
+
+void QuicClientInteropRunner::AttemptResumption(QuicClient* client) {
   client->Disconnect();
   if (!client->Initialize()) {
     QUIC_LOG(ERROR) << "Failed to reinitialize client";
@@ -94,22 +137,21 @@
   if (static_cast<QuicCryptoClientStream*>(
           test::QuicSessionPeer::GetMutableCryptoStream(client->session()))
           ->IsResumption()) {
-    features->insert(Feature::kResumption);
+    InsertFeature(Feature::kResumption);
   }
 }
 
-std::set<Feature> AttemptRequest(QuicSocketAddress addr,
-                                 std::string authority,
-                                 QuicServerId server_id,
-                                 bool test_version_negotiation,
-                                 bool attempt_rebind) {
+void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr,
+                                             std::string authority,
+                                             QuicServerId server_id,
+                                             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>();
   auto session_cache = std::make_unique<test::SimpleSessionCache>();
   QuicEpollServer epoll_server;
@@ -117,35 +159,32 @@
   auto client = std::make_unique<QuicClient>(
       addr, server_id, versions, &epoll_server, std::move(proof_verifier),
       std::move(session_cache));
+  client->set_connection_debug_visitor(this);
   if (!client->Initialize()) {
     QUIC_LOG(ERROR) << "Failed to initialize client";
-    return features;
+    return;
   }
   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);
+      InsertFeature(Feature::kRetry);
     }
     if (test_version_negotiation && connection->version() == version) {
-      features.insert(Feature::kVersionNegotiation);
+      InsertFeature(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;
+    AttemptRequest(addr, authority, server_id,
+                   /*test_version_negotiation=*/false, attempt_rebind);
+    return;
   }
   if (!client->session()->OneRttKeysAvailable()) {
-    return features;
+    return;
   }
-  features.insert(Feature::kHandshake);
+  InsertFeature(Feature::kHandshake);
 
   // Construct and send a request.
   spdy::SpdyHeaderBlock header_block;
@@ -176,19 +215,19 @@
         sent_packet_manager->GetLargestAckedPacket(ENCRYPTION_FORWARD_SECURE)
             .IsInitialized();
     if (client_stats.stream_bytes_received > 0 && received_forward_secure_ack) {
-      features.insert(Feature::kStreamData);
+      InsertFeature(Feature::kStreamData);
     }
   }
 
   if (request_timed_out || !client->connected()) {
-    return features;
+    return;
   }
 
   if (client->latest_response_code() != -1) {
-    features.insert(Feature::kHttp3);
+    InsertFeature(Feature::kHttp3);
 
     if (client->client_session()->dynamic_table_entry_referenced()) {
-      features.insert(Feature::kDynamicEntryReferenced);
+      InsertFeature(Feature::kDynamicEntryReferenced);
     }
 
     if (attempt_rebind) {
@@ -201,18 +240,15 @@
           if (epoll_clock.Now() - second_request_start_time >=
               request_timeout) {
             // Rebinding does not work, retry without attempting it.
-            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;
+            AttemptRequest(addr, authority, server_id, test_version_negotiation,
+                           /*attempt_rebind=*/false);
+            return;
           }
         }
-        features.insert(Feature::kRebinding);
+        InsertFeature(Feature::kRebinding);
 
         if (client->client_session()->dynamic_table_entry_referenced()) {
-          features.insert(Feature::kDynamicEntryReferenced);
+          InsertFeature(Feature::kDynamicEntryReferenced);
         }
       } else {
         QUIC_LOG(ERROR) << "Failed to change ephemeral port";
@@ -229,22 +265,21 @@
       client->epoll_network_helper()->RunEventLoop();
       if (epoll_clock.Now() - close_start_time >= close_timeout) {
         QUIC_LOG(ERROR) << "Timed out waiting for connection close";
-        AttemptResumption(client.get(), &features);
-        return features;
+        AttemptResumption(client.get());
+        return;
       }
     }
     const QuicErrorCode received_error = client->session()->error();
     if (received_error == QUIC_NO_ERROR ||
         received_error == QUIC_PUBLIC_RESET) {
-      features.insert(Feature::kConnectionClose);
+      InsertFeature(Feature::kConnectionClose);
     } else {
       QUIC_LOG(ERROR) << "Received error " << client->session()->error() << " "
                       << client->session()->error_details();
     }
   }
 
-  AttemptResumption(client.get(), &features);
-  return features;
+  AttemptResumption(client.get());
 }
 
 std::set<Feature> ServerSupport(std::string host, int port) {
@@ -262,9 +297,13 @@
   QuicServerId server_id(host, port, false);
   std::string authority = quiche::QuicheStrCat(host, ":", port);
 
-  return AttemptRequest(addr, authority, server_id,
+  QuicClientInteropRunner runner;
+
+  runner.AttemptRequest(addr, authority, server_id,
                         /*test_version_negotiation=*/true,
                         /*attempt_rebind=*/true);
+
+  return runner.features();
 }
 
 }  // namespace quic