chromium/chrome/browser/ash/net/network_diagnostics/http_request_manager.cc

// 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 "chrome/browser/ash/net/network_diagnostics/http_request_manager.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"

namespace ash {
namespace network_diagnostics {
namespace {

// Maximum number of retries for sending the request.
constexpr int kMaxRetries = 2;
// HTTP Get method.
const char kGetMethod[] = "GET";

net::NetworkTrafficAnnotationTag GetTrafficAnnotation() {
  return net::DefineNetworkTrafficAnnotation("network_diagnostics_routines",
                                             R"(
      semantics {
        sender: "NetworkDiagnosticsRoutines"
        description: "Routines send network traffic (http requests) to "
          "hosts in order to validate the internet connection on a device."
        trigger: "A routine makes an http request."
        data:
          "No data other than the path is sent. No user identifier is "
          "sent along with the data."
        destination: WEBSITE
      }
      policy {
        cookies_allowed: NO
        policy_exception_justification:
          "No policy defined to enable/disable or limit this request as this "
          "is on-demand user initiated operation to do the network diagnostics."
      }
  )");
}

}  // namespace

HttpRequestManager::HttpRequestManager(Profile* profile) {
  // |profile| may be null in testing.
  if (!profile) {
    return;
  }
  shared_url_loader_factory_ = profile->GetDefaultStoragePartition()
                                   ->GetURLLoaderFactoryForBrowserProcess();
}

HttpRequestManager::~HttpRequestManager() = default;

void HttpRequestManager::MakeRequest(const GURL& url,
                                     const base::TimeDelta& timeout,
                                     HttpRequestCallback callback) {
  DCHECK(url.is_valid());

  auto request = std::make_unique<network::ResourceRequest>();
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  request->method = kGetMethod;
  request->url = url;

  simple_url_loader_ = network::SimpleURLLoader::Create(std::move(request),
                                                        GetTrafficAnnotation());
  simple_url_loader_->SetRetryOptions(
      kMaxRetries,
      network::SimpleURLLoader::RetryMode::RETRY_ON_5XX |
          network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
  simple_url_loader_->SetTimeoutDuration(timeout);
  // |simple_url_loader_| is owned by |this|, so Unretained is safe to use.
  simple_url_loader_->DownloadHeadersOnly(
      shared_url_loader_factory_.get(),
      base::BindOnce(&HttpRequestManager::OnURLLoadComplete,
                     base::Unretained(this), std::move(callback)));
}

void HttpRequestManager::SetURLLoaderFactoryForTesting(
    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) {
  shared_url_loader_factory_ = shared_url_loader_factory;
}

void HttpRequestManager::OnURLLoadComplete(
    HttpRequestCallback callback,
    scoped_refptr<net::HttpResponseHeaders> headers) {
  DCHECK(simple_url_loader_);
  bool connected = headers && headers->response_code() == net::HTTP_NO_CONTENT;
  simple_url_loader_.reset();
  std::move(callback).Run(connected);
}

}  // namespace network_diagnostics
}  // namespace ash