summaryrefslogtreecommitdiffstats
path: root/chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc')
-rw-r--r--chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc295
1 files changed, 295 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc b/chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc
new file mode 100644
index 00000000000..1542996d79b
--- /dev/null
+++ b/chromium/components/policy/core/common/cloud/external_policy_data_fetcher.cc
@@ -0,0 +1,295 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/cloud/external_policy_data_fetcher.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/location.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+namespace policy {
+
+class ExternalPolicyDataFetcher::Job
+ : public network::SimpleURLLoaderStreamConsumer {
+ public:
+ Job(std::unique_ptr<network::PendingSharedURLLoaderFactory>
+ pending_url_loader_factory,
+ base::WeakPtr<ExternalPolicyDataFetcher> fetcher,
+ scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner,
+ ExternalPolicyDataFetcher::FetchCallback callback);
+ Job(const Job&) = delete;
+ Job& operator=(const Job&) = delete;
+
+ void Start(const GURL& url, int64_t max_size);
+ void Cancel();
+ void OnResponseStarted(const GURL& final_url,
+ const network::mojom::URLResponseHead& response_head);
+
+ // network::SimpleURLLoaderStreamConsumer implementation
+ void OnDataReceived(base::StringPiece string_piece,
+ base::OnceClosure resume) override;
+ void OnComplete(bool success) override;
+ void OnRetry(base::OnceClosure start_retry) override;
+
+ private:
+ void ReportFinished(Result result, std::unique_ptr<std::string> data);
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ std::unique_ptr<network::PendingSharedURLLoaderFactory>
+ pending_url_loader_factory_;
+ base::WeakPtr<ExternalPolicyDataFetcher> fetcher_;
+ scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner_;
+ ExternalPolicyDataFetcher::FetchCallback callback_;
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
+ std::string response_body_;
+ int64_t max_size_ = 0;
+};
+
+ExternalPolicyDataFetcher::Job::Job(
+ std::unique_ptr<network::PendingSharedURLLoaderFactory>
+ pending_url_loader_factory,
+ base::WeakPtr<ExternalPolicyDataFetcher> fetcher,
+ scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner,
+ ExternalPolicyDataFetcher::FetchCallback callback)
+ : pending_url_loader_factory_(std::move(pending_url_loader_factory)),
+ fetcher_(std::move(fetcher)),
+ fetcher_task_runner_(std::move(fetcher_task_runner)),
+ callback_(std::move(callback)) {
+ // A job is created on the fetcher sequence but it then lives on the separate
+ // job sequence.
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+void ExternalPolicyDataFetcher::Job::Start(
+ const GURL& url,
+ int64_t max_size) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ DCHECK_GE(max_size, 0);
+ max_size_ = max_size;
+
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
+ resource_request->load_flags =
+ net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
+ resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("external_policy_fetcher", R"(
+ semantics {
+ sender: "Cloud Policy"
+ description:
+ "Used to fetch policy for extensions, policy-controlled wallpaper, "
+ "and custom terms of service."
+ trigger:
+ "Periodically loaded when a managed user is signed in to Chrome."
+ data:
+ "This request does not send any data. It loads external resources "
+ "by a unique URL provided by the admin."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "This feature cannot be controlled by Chrome settings, but users "
+ "can sign out of Chrome to disable it."
+ policy_exception_justification:
+ "Not implemented, considered not useful. This request is part of "
+ "the policy fetcher itself."
+ })");
+
+ url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ url_loader_->SetRetryOptions(
+ 3, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ url_loader_->SetOnResponseStartedCallback(
+ base::BindOnce(&ExternalPolicyDataFetcher::Job::OnResponseStarted,
+ base::Unretained(this)));
+ url_loader_->DownloadAsStream(network::SharedURLLoaderFactory::Create(
+ std::move(pending_url_loader_factory_))
+ .get(),
+ this);
+}
+
+void ExternalPolicyDataFetcher::Job::Cancel() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ url_loader_.reset();
+}
+
+void ExternalPolicyDataFetcher::Job::OnResponseStarted(
+ const GURL& /* final_url */,
+ const network::mojom::URLResponseHead& response_head) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (response_head.content_length != -1 &&
+ response_head.content_length > max_size_) {
+ url_loader_.reset();
+ ReportFinished(MAX_SIZE_EXCEEDED, nullptr);
+ return;
+ }
+}
+
+void ExternalPolicyDataFetcher::Job::OnDataReceived(
+ base::StringPiece string_piece,
+ base::OnceClosure resume) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (response_body_.length() + string_piece.length() >
+ static_cast<uint64_t>(max_size_)) {
+ url_loader_.reset();
+ ReportFinished(MAX_SIZE_EXCEEDED, nullptr);
+ return;
+ }
+
+ response_body_.append(string_piece.data(), string_piece.length());
+ std::move(resume).Run();
+}
+
+void ExternalPolicyDataFetcher::Job::OnComplete(bool /* success */) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::unique_ptr<network::SimpleURLLoader> url_loader = std::move(url_loader_);
+
+ int response_code = 0;
+ if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+ response_code = url_loader->ResponseInfo()->headers->response_code();
+
+ Result result;
+ std::unique_ptr<std::string> data;
+
+ if (url_loader->NetError() == net::ERR_CONNECTION_RESET ||
+ url_loader->NetError() == net::ERR_TEMPORARILY_THROTTLED ||
+ url_loader->NetError() == net::ERR_CONNECTION_CLOSED) {
+ // The connection was interrupted.
+ result = CONNECTION_INTERRUPTED;
+ } else if (url_loader->NetError() == net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
+ // net::ERR_HTTP_RESPONSE_CODE_FAILURE signals that a non-2xx HTTP response
+ // has been received.
+ if (response_code >= 500) {
+ // Problem at the server.
+ result = SERVER_ERROR;
+ } else if (response_code >= 400) {
+ // Client error.
+ result = CLIENT_ERROR;
+ } else {
+ // Any other type of HTTP failure.
+ result = HTTP_ERROR;
+ }
+ } else if (url_loader->NetError() != net::OK) {
+ // Another network error occurred.
+ result = NETWORK_ERROR;
+ } else {
+ result = SUCCESS;
+ data = std::make_unique<std::string>(std::move(response_body_));
+ }
+
+ ReportFinished(result, std::move(data));
+}
+
+void ExternalPolicyDataFetcher::Job::OnRetry(base::OnceClosure start_retry) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ response_body_.clear();
+ std::move(start_retry).Run();
+}
+
+void ExternalPolicyDataFetcher::Job::ReportFinished(
+ Result result,
+ std::unique_ptr<std::string> data) {
+ fetcher_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ExternalPolicyDataFetcher::OnJobFinished, fetcher_,
+ std::move(callback_), this, result, std::move(data)));
+}
+
+ExternalPolicyDataFetcher::ExternalPolicyDataFetcher(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)),
+ job_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+ // |url_loader_factory| is null in some tests.
+ if (url_loader_factory)
+ pending_url_loader_factory_ = url_loader_factory->Clone();
+}
+
+ExternalPolicyDataFetcher::~ExternalPolicyDataFetcher() {
+ // No RunsTasksInCurrentSequence() check to avoid unit tests failures.
+ // In unit tests the browser process instance is deleted only after test ends
+ // and test task scheduler is shutted down. Therefore we need to delete some
+ // components of BrowserPolicyConnector (ResourceCache and
+ // CloudExternalDataManagerBase::Backend) manually when task runner doesn't
+ // accept new tasks (DeleteSoon in this case). This leads to the situation
+ // when this destructor is called not on |task_runner|.
+
+ for (auto it = jobs_.begin(); it != jobs_.end(); ++it)
+ CancelJob(*it);
+}
+
+ExternalPolicyDataFetcher::Job* ExternalPolicyDataFetcher::StartJob(
+ const GURL& url,
+ int64_t max_size,
+ FetchCallback callback) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ if (!cloned_url_loader_factory_) {
+ cloned_url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+ std::move(pending_url_loader_factory_));
+ }
+ Job* job =
+ new Job(cloned_url_loader_factory_->Clone(), weak_factory_.GetWeakPtr(),
+ task_runner_, std::move(callback));
+ jobs_.insert(job);
+ job_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Job::Start, base::Unretained(job), url, max_size));
+ return job;
+}
+
+void ExternalPolicyDataFetcher::CancelJob(Job* job) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(jobs_.find(job) != jobs_.end());
+ jobs_.erase(job);
+ // Post a task that will cancel the |job| in the |job_task_runner_|. The |job|
+ // is removed from |jobs_| immediately to indicate that it has been canceled
+ // but is not actually deleted until the cancellation has reached the
+ // |job_task_runner_| and a confirmation has been posted back. This ensures
+ // that no new job can be allocated at the same address while an
+ // OnJobFinished() callback may still be pending for the canceled |job|.
+ job_task_runner_->PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&Job::Cancel, base::Unretained(job)),
+ base::BindOnce([](Job*) {}, base::Owned(job)));
+}
+
+void ExternalPolicyDataFetcher::OnJobFinished(
+ FetchCallback callback,
+ Job* job,
+ Result result,
+ std::unique_ptr<std::string> data) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ auto it = jobs_.find(job);
+ if (it == jobs_.end()) {
+ // The |job| has been canceled and removed from |jobs_| already. This can
+ // happen because the jobs run on a different sequence and a |job| may
+ // finish before the cancellation has reached that sequence.
+ return;
+ }
+ std::move(callback).Run(result, std::move(data));
+ jobs_.erase(it);
+ delete job;
+}
+
+} // namespace policy