910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/proxy_resolution/configured_proxy_resolution_request.h"

#include <optional>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source_type.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_info.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"

namespace net {

namespace {

bool CheckDnsCondition(
    const ProxyConfig::ProxyOverrideRule::DnsProbeCondition& dns_condition,
    const ResolveHostResult& dns_result) {
  switch (dns_condition.result) {
    case ProxyConfig::ProxyOverrideRule::DnsProbeCondition::Result::kNotFound:
      return dns_result.is_address_list_empty;
    case ProxyConfig::ProxyOverrideRule::DnsProbeCondition::Result::kResolved:
      return !dns_result.is_address_list_empty;
  }
}

base::Value::Dict CreateEndHostResolutionNetLogParams(
    const url::SchemeHostPort& host,
    const ResolveHostResult& result,
    bool was_resolved_sync) {
  base::Value::Dict dict;
  dict.Set("host", host.Serialize());
  dict.Set("was_resolved_sync", was_resolved_sync);
  result.AddToDict(dict);
  return dict;
}

}  // namespace

ConfiguredProxyResolutionRequest::ConfiguredProxyResolutionRequest(
    ConfiguredProxyResolutionService* service,
    const GURL& url,
    const std::string& method,
    const NetworkAnonymizationKey& network_anonymization_key,
    ProxyInfo* results,
    CompletionOnceCallback user_callback,
    const NetLogWithSource& net_log,
    RequestPriority priority)
    : service_(service),
      user_callback_(std::move(user_callback)),
      results_(results),
      url_(url),
      method_(method),
      network_anonymization_key_(network_anonymization_key),
      net_log_(net_log),
      priority_(priority),
      creation_time_(base::TimeTicks::Now()) {
  CHECK(!user_callback_.is_null());
}

ConfiguredProxyResolutionRequest::~ConfiguredProxyResolutionRequest() {
  if (service_) {
    service_->RemovePendingRequest(this);
    Cancel();

    // This should be emitted last, after any message `Cancel()` may
    // trigger.
    net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
  }
}

// Starts the resolve proxy request.
int ConfiguredProxyResolutionRequest::Start() {
  CHECK(!was_completed());
  CHECK(!is_started());

  CHECK(service_->config_);
  traffic_annotation_ = MutableNetworkTrafficAnnotationTag(
      service_->config_->traffic_annotation());

  if (!service_->config_->value().proxy_override_rules().empty()) {
    net_log_.BeginEvent(NetLogEventType::PROXY_RESOLUTION_OVERRIDE_RULES);

    for (const auto& rule : service_->config_->value().proxy_override_rules()) {
      if (rule.MatchesDestination(url_)) {
        bool skip_rule = false;
        for (const auto& dns_condition : rule.dns_conditions) {
          // DNS resolution requests are keyed by the host and the network
          // anonymization key, but not by listeners. This means that a given
          // request may end up as listener multiple times for the same DNS host
          // resolution requests (e.g. if the same host is in multiple rules).
          // This is fine, as long as `OnDnsHostResolved` knows how to handle
          // multiple calls for the same host.
          auto result = service_->RequestHostResolution(
              dns_condition, weak_factory_.GetWeakPtr(),
              network_anonymization_key_, net_log_, priority_);
          if (result) {
            net_log_.AddEvent(
                NetLogEventType::PROXY_OVERRIDE_END_HOST_RESOLUTION, [&] {
                  return CreateEndHostResolutionNetLogParams(
                      dns_condition.host, result.value(),
                      /*was_resolved_sync=*/true);
                });

            if (!CheckDnsCondition(dns_condition, result.value())) {
              // No need to trigger DNS resolutions for the remaining hosts
              // in this rule, as it does not apply.
              skip_rule = true;
              break;
            }

            dns_results_.emplace(dns_condition.host, result.value());
          }
        }
        if (!skip_rule) {
          applicable_override_rules_.push(rule);
        }
      }
    }

    if (applicable_override_rules_.empty()) {
      // No override rules applied.
      net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_OVERRIDE_RULES);
    }
  }

  return ContinueProxyResolution();
}

void ConfiguredProxyResolutionRequest::
    StartAndCompleteCheckingForSynchronous() {
  int rv = service_->TryToCompleteSynchronously(
      url_, /*bypass_override_rules=*/false, net_log_, results_);
  if (rv == ERR_IO_PENDING) {
    rv = Start();
  }

  if (rv != ERR_IO_PENDING) {
    QueryComplete(rv);
  }
}

void ConfiguredProxyResolutionRequest::Cancel() {
  net_log_.AddEvent(NetLogEventType::CANCELLED);

  if (!applicable_override_rules_.empty()) {
    net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_OVERRIDE_RULES);
  }

  Reset();
  CHECK(!is_started());
}

int ConfiguredProxyResolutionRequest::QueryDidComplete(int result_code) {
  CHECK(!was_completed());

  // Clear state so that `is_started()` returns false while
  // DidFinishResolvingProxy() runs.
  if (is_started()) {
    Reset();
  }

  // Note that DidFinishResolvingProxy might modify `results_`.
  int rv = service_->DidFinishResolvingProxy(url_, network_anonymization_key_,
                                             method_, results_, result_code,
                                             net_log_);

  // Make a note in the results which configuration was in use at the
  // time of the resolve.
  results_->set_proxy_resolve_start_time(creation_time_);
  results_->set_proxy_resolve_end_time(base::TimeTicks::Now());

  // If annotation is not already set, e.g. through TryToCompleteSynchronously
  // function, use in-progress-resolve annotation.
  if (!results_->traffic_annotation().is_valid())
    results_->set_traffic_annotation(traffic_annotation_);

  // If proxy is set without error, ensure that an annotation is provided.
  if (result_code != ERR_ABORTED && !rv) {
    // TODO(crbug.com/469006851): Turn this back into a CHECK once crashes were
    // fully fixed.
    DCHECK(results_->traffic_annotation().is_valid());
  }

  // Reset the state associated with in-progress-resolve.
  traffic_annotation_.reset();

  return rv;
}

int ConfiguredProxyResolutionRequest::QueryDidCompleteSynchronously(
    int result_code) {
  int rv = QueryDidComplete(result_code);
  service_ = nullptr;
  return rv;
}

LoadState ConfiguredProxyResolutionRequest::GetLoadState() const {
  LoadState load_state = LOAD_STATE_IDLE;
  if (service_ && service_->GetLoadStateIfAvailable(&load_state))
    return load_state;

  if (is_started())
    return resolve_job_->GetLoadState();
  return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
}

// Callback for when the ProxyResolver request has completed.
void ConfiguredProxyResolutionRequest::QueryComplete(int result_code) {
  result_code = QueryDidComplete(result_code);

  CompletionOnceCallback callback = std::move(user_callback_);

  service_->RemovePendingRequest(this);
  service_ = nullptr;
  user_callback_.Reset();
  std::move(callback).Run(result_code);
}

int ConfiguredProxyResolutionRequest::ContinueProxyResolution() {
  if (!applicable_override_rules_.empty()) {
    int rv = EvaluateApplicableOverrideRules();
    if (rv == ERR_IO_PENDING || !results_->is_empty()) {
      return rv;
    }
  }

  if (service_->ApplyPacBypassRules(url_, results_)) {
    return OK;
  }

  // Required in case some override rules needed to run asynchronously, but
  // ended up not applying, and there are e.g. manual proxy settings as
  // fallback.
  int rv = service_->TryToCompleteSynchronously(
      url_, /*bypass_override_rules=*/true, net_log_, results_);
  if (rv != ERR_IO_PENDING) {
    return rv;
  }

  return service_->GetProxyResolver()->GetProxyForURL(
      url_, network_anonymization_key_, results_,
      base::BindOnce(&ConfiguredProxyResolutionRequest::QueryComplete,
                     base::Unretained(this)),
      &resolve_job_, net_log_);
}

void ConfiguredProxyResolutionRequest::OnDnsHostResolved(
    const url::SchemeHostPort& host,
    const ResolveHostResult& result) {
  if (applicable_override_rules_.empty()) {
    // DNS resolution is only required for override rules. Ignore these function
    // calls if the current request is not resolving override rules.
    return;
  }

  // Using `try_emplace` as this line may be hit multiple times for the same
  // host (see comment in `Start`).
  auto [unused, inserted] = dns_results_.try_emplace(host, result);
  // Entry already existed, likely with the same value as before. Same value or
  // not, we keep the old result, so there's nothing else to do.
  if (!inserted) {
    return;
  }

  net_log_.AddEvent(NetLogEventType::PROXY_OVERRIDE_END_HOST_RESOLUTION, [&] {
    return CreateEndHostResolutionNetLogParams(host, result,
                                               /*was_resolved_sync=*/false);
  });

  if (!service_ || !service_->IsReady()) {
    // Something happened during DNS resolution which invalidated the
    // service's state (e.g. config update). The service will resume
    // the current request if/when needed.
    return;
  }

  int rv = ContinueProxyResolution();
  if (rv == ERR_IO_PENDING) {
    // Missing a required DNS resolution.
    return;
  }

  QueryComplete(rv);
}

int ConfiguredProxyResolutionRequest::EvaluateApplicableOverrideRules() {
  while (!applicable_override_rules_.empty()) {
    const auto& applicable_rule = applicable_override_rules_.front();

    // Not having any conditions means they are all satisfied.
    bool conditions_satisfied = true;
    for (const auto& dns_condition : applicable_rule.dns_conditions) {
      // If no results are found in the DNS results map, it means that the DNS
      // resolution request owned by the service is still pending.
      auto dns_result_it = dns_results_.find(dns_condition.host);
      if (dns_result_it == dns_results_.end()) {
        return ERR_IO_PENDING;
      }

      conditions_satisfied =
          conditions_satisfied &&
          CheckDnsCondition(dns_condition, dns_result_it->second);
    }

    if (conditions_satisfied) {
      net_log_.AddEvent(NetLogEventType::PROXY_RESOLUTION_OVERRIDE_RULE_APPLIED,
                        [&] { return applicable_rule.ToDict(); });
      results_->UseProxyList(applicable_rule.proxy_list);
      break;
    }

    // Conditions were evaluated successfully, but are not satisfied. This
    // current rule is therefore no longer applicable.
    applicable_override_rules_.pop();
  }

  dns_results_.clear();
  net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_OVERRIDE_RULES);
  return OK;
}

void ConfiguredProxyResolutionRequest::Reset() {
  // The request may already be running in the resolver.
  resolve_job_.reset();
  applicable_override_rules_ = base::queue<ProxyConfig::ProxyOverrideRule>();
  dns_results_.clear();
}

}  // namespace net