dns: Refactor HostResolverManager::ServiceEndpointRequestImpl

This is a preparation for adding a feature to allow using stale DNS
results by using ServiceEndpointRequest:
* Add a state for resolving locally
* Add a helper method to convert HostCache::Entry to ServiceEndpoints

No behavior changes.

Bug: 383174960

Change-Id: I0e4fd71c34676f3e99ff1cce03e773f9c21c654a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6088517
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Eric Orth <ericorth@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1398190}
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 59bdd507..75b433f 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -507,6 +507,50 @@
   return copy;
 }
 
+std::vector<ServiceEndpoint> HostCache::Entry::ConvertToServiceEndpoints(
+    uint16_t port) const {
+  std::vector<ServiceEndpoint> endpoints;
+
+  std::vector<IPEndPoint> ipv4_endpoints;
+  std::vector<IPEndPoint> ipv6_endpoints;
+  for (const auto& ip_endpoint : ip_endpoints()) {
+    std::vector<IPEndPoint>& ip_endpoints =
+        ip_endpoint.address().IsIPv6() ? ipv6_endpoints : ipv4_endpoints;
+    if (ip_endpoint.port() == 0) {
+      ip_endpoints.emplace_back(ip_endpoint.address(), port);
+    } else {
+      ip_endpoints.emplace_back(ip_endpoint);
+    }
+  }
+
+  // See HostCache::Entry::GetEndpoints.
+  if (!ipv4_endpoints.empty() || !ipv6_endpoints.empty()) {
+    for (const auto& metadata : GetMetadatas()) {
+      if (!base::Contains(canonical_names(), metadata.target_name)) {
+        continue;
+      }
+
+      ServiceEndpoint endpoint;
+      endpoint.ipv4_endpoints = ipv4_endpoints;
+      endpoint.ipv6_endpoints = ipv6_endpoints;
+      endpoint.metadata = metadata;
+      endpoints.emplace_back(std::move(endpoint));
+    }
+
+    // Append Non-SVCB endpoints at the end for fallback.
+    // TODO(crbug.com/41493696): Revisit how to handle non-SVCB endpoints once
+    // the connection layer starts using this API. Adding non-SVCB endpoints
+    // here might be inconsistent with intermediate results generated by
+    // DnsTaskResultsManager, which doesn't append non-SVCB endpoints.
+    ServiceEndpoint non_alternative_endpoint;
+    non_alternative_endpoint.ipv4_endpoints = ipv4_endpoints;
+    non_alternative_endpoint.ipv6_endpoints = ipv6_endpoints;
+    endpoints.emplace_back(std::move(non_alternative_endpoint));
+  }
+
+  return endpoints;
+}
+
 HostCache::Entry& HostCache::Entry::operator=(const Entry& entry) = default;
 
 HostCache::Entry& HostCache::Entry::operator=(Entry&& entry) = default;
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index b1a65f4..73be90e 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -264,6 +264,11 @@
     // set to |port| if the current port is 0. Preserves any non-zero ports.
     HostCache::Entry CopyWithDefaultPort(uint16_t port) const;
 
+    // Converts `this` to a vector of ServiceEndpoints. Converted IP endpoint's
+    // ports set to `port` if the current port is 0. Preserves any non-zero
+    // ports.
+    std::vector<ServiceEndpoint> ConvertToServiceEndpoints(uint16_t port) const;
+
     static std::optional<base::TimeDelta> TtlFromInternalResults(
         const std::set<std::unique_ptr<HostResolverInternalResult>>& results,
         base::Time now,
diff --git a/net/dns/host_resolver_manager_service_endpoint_request_impl.cc b/net/dns/host_resolver_manager_service_endpoint_request_impl.cc
index 32ad45f..78cc965 100644
--- a/net/dns/host_resolver_manager_service_endpoint_request_impl.cc
+++ b/net/dns/host_resolver_manager_service_endpoint_request_impl.cc
@@ -224,6 +224,9 @@
       case State::kCheckIPv6ReachabilityComplete:
         rv = DoCheckIPv6ReachabilityComplete(rv);
         break;
+      case State::kDoResolveLocally:
+        rv = DoResolveLocally();
+        break;
       case State::kStartJob:
         rv = DoStartJob();
         break;
@@ -261,32 +264,36 @@
 
 int HostResolverManager::ServiceEndpointRequestImpl::
     DoCheckIPv6ReachabilityComplete(int rv) {
-  next_state_ = rv == OK ? State::kStartJob : State::kNone;
+  next_state_ = rv == OK ? State::kDoResolveLocally : State::kNone;
   return rv;
 }
 
-int HostResolverManager::ServiceEndpointRequestImpl::DoStartJob() {
-  JobKey job_key(host_, resolve_context_.get());
+int HostResolverManager::ServiceEndpointRequestImpl::DoResolveLocally() {
+  job_key_ = JobKey(host_, resolve_context_.get());
   IPAddress ip_address;
   manager_->InitializeJobKeyAndIPAddress(
-      network_anonymization_key_, parameters_, net_log_, job_key, ip_address);
+      network_anonymization_key_, parameters_, net_log_, *job_key_, ip_address);
 
   // Try to resolve locally first.
   std::optional<HostCache::EntryStaleness> stale_info;
-  std::deque<TaskType> tasks;
   HostCache::Entry results = manager_->ResolveLocally(
-      /*only_ipv6_reachable=*/false, job_key, ip_address,
+      /*only_ipv6_reachable=*/false, *job_key_, ip_address,
       parameters_.cache_usage, parameters_.secure_dns_policy,
-      parameters_.source, net_log_, host_cache(), &tasks, &stale_info);
+      parameters_.source, net_log_, host_cache(), &tasks_, &stale_info);
   if (results.error() != ERR_DNS_CACHE_MISS ||
-      parameters_.source == HostResolverSource::LOCAL_ONLY || tasks.empty()) {
+      parameters_.source == HostResolverSource::LOCAL_ONLY || tasks_.empty()) {
     SetFinalizedResultFromLegacyResults(results);
     error_info_ = ResolveErrorInfo(results.error());
     return results.error();
   }
 
-  manager_->CreateAndStartJobForServiceEndpointRequest(std::move(job_key),
-                                                       std::move(tasks), this);
+  next_state_ = State::kStartJob;
+  return OK;
+}
+
+int HostResolverManager::ServiceEndpointRequestImpl::DoStartJob() {
+  manager_->CreateAndStartJobForServiceEndpointRequest(std::move(*job_key_),
+                                                       std::move(tasks_), this);
   return ERR_IO_PENDING;
 }
 
@@ -298,44 +305,8 @@
     SetFinalizedResultFromLegacyResults(const HostCache::Entry& results) {
   CHECK(!finalized_result_);
   if (results.error() == OK && !parameters_.is_speculative) {
-    std::vector<IPEndPoint> ipv4_endpoints;
-    std::vector<IPEndPoint> ipv6_endpoints;
-    for (const auto& ip_endpoint : results.ip_endpoints()) {
-      std::vector<IPEndPoint>& ip_endpoints =
-          ip_endpoint.address().IsIPv6() ? ipv6_endpoints : ipv4_endpoints;
-      if (ip_endpoint.port() == 0) {
-        ip_endpoints.emplace_back(ip_endpoint.address(), host_.GetPort());
-      } else {
-        ip_endpoints.emplace_back(ip_endpoint);
-      }
-    }
-
-    // See HostCache::Entry::GetEndpoints.
-    std::vector<ServiceEndpoint> endpoints;
-    if (!ipv4_endpoints.empty() || !ipv6_endpoints.empty()) {
-      for (const auto& metadata : results.GetMetadatas()) {
-        if (!base::Contains(results.canonical_names(), metadata.target_name)) {
-          continue;
-        }
-
-        ServiceEndpoint endpoint;
-        endpoint.ipv4_endpoints = ipv4_endpoints;
-        endpoint.ipv6_endpoints = ipv6_endpoints;
-        endpoint.metadata = metadata;
-        endpoints.emplace_back(std::move(endpoint));
-      }
-
-      // Append Non-SVCB endpoints at the end for fallback.
-      // TODO(crbug.com/41493696): Revisit how to handle non-SVCB endpoints once
-      // the connection layer starts using this API. Adding non-SVCB endpoints
-      // here might be inconsistent with intermediate results generated by
-      // DnsTaskResultsManager, which doesn't append non-SVCB endpoints.
-      ServiceEndpoint non_alternative_endpoint;
-      non_alternative_endpoint.ipv4_endpoints = ipv4_endpoints;
-      non_alternative_endpoint.ipv6_endpoints = ipv6_endpoints;
-      endpoints.emplace_back(std::move(non_alternative_endpoint));
-    }
-
+    std::vector<ServiceEndpoint> endpoints =
+        results.ConvertToServiceEndpoints(host_.GetPort());
     finalized_result_ =
         FinalizedResult(std::move(endpoints),
                         dns_alias_utility::FixUpDnsAliases(results.aliases()));
diff --git a/net/dns/host_resolver_manager_service_endpoint_request_impl.h b/net/dns/host_resolver_manager_service_endpoint_request_impl.h
index 9ead26a2..dcb427ac 100644
--- a/net/dns/host_resolver_manager_service_endpoint_request_impl.h
+++ b/net/dns/host_resolver_manager_service_endpoint_request_impl.h
@@ -5,6 +5,7 @@
 #ifndef NET_DNS_HOST_RESOLVER_MANAGER_SERVICE_ENDPOINT_REQUEST_IMPL_H_
 #define NET_DNS_HOST_RESOLVER_MANAGER_SERVICE_ENDPOINT_REQUEST_IMPL_H_
 
+#include <deque>
 #include <optional>
 #include <set>
 #include <string>
@@ -79,12 +80,14 @@
     kNone,
     kCheckIPv6Reachability,
     kCheckIPv6ReachabilityComplete,
+    kDoResolveLocally,
     kStartJob,
   };
 
   int DoLoop(int rv);
   int DoCheckIPv6Reachability();
   int DoCheckIPv6ReachabilityComplete(int rv);
+  int DoResolveLocally();
   int DoStartJob();
 
   void OnIOComplete(int rv);
@@ -125,6 +128,11 @@
   // Set when the endpoint results are finalized.
   std::optional<FinalizedResult> finalized_result_;
 
+  // These fields are calculated by DoResolveLocally() and consumed by
+  // DoStartJob().
+  std::optional<JobKey> job_key_;
+  std::deque<TaskType> tasks_;
+
   // Set when a job is associated with `this`. Must be valid unless
   // `resolve_context_` becomes invalid. Cleared when the endpoints are
   // finalized to ensure that `job_` doesn't become a dangling pointer.