123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- // Copyright 2020 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 "chromeos/printing/printer_config_cache.h"
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/callback.h"
- #include "base/containers/queue.h"
- #include "base/location.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/weak_ptr.h"
- #include "base/sequence_checker.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_piece.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/time/clock.h"
- #include "base/time/time.h"
- #include "net/base/load_flags.h"
- #include "net/base/net_errors.h"
- #include "services/network/public/cpp/resource_request.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "url/gurl.h"
- namespace chromeos {
- namespace {
- // Defines the serving root in which all PPDs and PPD metadata reside.
- const char kServingRoot[] =
- "https://printerconfigurations.googleusercontent.com/"
- "chromeos_printing/";
- // Prepends the serving root to |name|, returning the result.
- std::string PrependServingRoot(const std::string& name) {
- return base::StrCat({base::StringPiece(kServingRoot), name});
- }
- // Accepts a relative |path| to a value in the Chrome OS Printing
- // serving root) and returns a resource request to satisfy the same.
- std::unique_ptr<network::ResourceRequest> FormRequest(const std::string& path) {
- GURL full_url(PrependServingRoot(path));
- if (!full_url.is_valid()) {
- return nullptr;
- }
- auto request = std::make_unique<network::ResourceRequest>();
- request->url = full_url;
- request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
- request->credentials_mode = network::mojom::CredentialsMode::kOmit;
- return request;
- }
- } // namespace
- // In case of fetch failure, only the key is meaningful feedback.
- // static
- PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Failure(
- const std::string& key) {
- return PrinterConfigCache::FetchResult{false, key, std::string(),
- base::Time()};
- }
- // static
- PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Success(
- const std::string& key,
- const std::string& contents,
- base::Time time_of_fetch) {
- return PrinterConfigCache::FetchResult{true, key, contents, time_of_fetch};
- }
- class PrinterConfigCacheImpl : public PrinterConfigCache {
- public:
- explicit PrinterConfigCacheImpl(
- const base::Clock* clock,
- base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
- loader_factory_dispenser)
- : clock_(clock),
- loader_factory_dispenser_(std::move(loader_factory_dispenser)),
- weak_factory_(this) {}
- ~PrinterConfigCacheImpl() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- }
- void Fetch(const std::string& key,
- base::TimeDelta expiration,
- FetchCallback cb) override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Try to answer this fetch request locally.
- const auto& finding = cache_.find(key);
- if (finding != cache_.end()) {
- const Entry& entry = finding->second;
- if (entry.time_of_fetch + expiration > clock_->Now()) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), FetchResult::Success(
- key, entry.contents,
- entry.time_of_fetch)));
- return;
- }
- }
- // We couldn't answer this request locally. Issue a networked fetch
- // and defer the answer to when we hear back.
- auto context = std::make_unique<FetchContext>(key, std::move(cb));
- fetch_queue_.push(std::move(context));
- TryToStartNetworkedFetch();
- }
- void Drop(const std::string& key) override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- cache_.erase(key);
- }
- private:
- // A FetchContext saves off the key and the FetchCallback that a
- // caller passes to PrinterConfigCacheImpl::Fetch().
- struct FetchContext {
- const std::string key;
- PrinterConfigCache::FetchCallback cb;
- FetchContext(const std::string& arg_key,
- PrinterConfigCache::FetchCallback arg_cb)
- : key(arg_key), cb(std::move(arg_cb)) {}
- ~FetchContext() = default;
- };
- // If a PrinterConfigCache maps keys to values, then Entry structs
- // represent values.
- struct Entry {
- std::string contents;
- base::Time time_of_fetch;
- Entry(const std::string& arg_contents, base::Time time)
- : contents(arg_contents), time_of_fetch(time) {}
- ~Entry() = default;
- };
- void TryToStartNetworkedFetch() {
- // Either
- // 1. a networked fetch is already in flight or
- // 2. there are no more pending networked fetches to act upon.
- // In either case, we can't do anything at the moment; back off
- // and let a future call to Fetch() or FinishNetworkedFetch()
- // return here to try again.
- if (fetcher_ || fetch_queue_.empty()) {
- return;
- }
- std::unique_ptr<FetchContext> context = std::move(fetch_queue_.front());
- fetch_queue_.pop();
- auto request = FormRequest(context->key);
- // TODO(crbug.com/888189): add traffic annotation.
- fetcher_ = network::SimpleURLLoader::Create(std::move(request),
- MISSING_TRAFFIC_ANNOTATION);
- fetcher_->DownloadToString(
- loader_factory_dispenser_.Run(),
- base::BindOnce(&PrinterConfigCacheImpl::FinishNetworkedFetch,
- weak_factory_.GetWeakPtr(), std::move(context)),
- network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
- }
- // Called by |fetcher_| once DownloadToString() completes.
- void FinishNetworkedFetch(std::unique_ptr<FetchContext> context,
- std::unique_ptr<std::string> contents) {
- // Wherever |fetcher_| works its sorcery, it had better have posted
- // back onto _our_ sequence.
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (fetcher_->NetError() == net::Error::OK) {
- // We only want to update our local cache if the |fetcher_|
- // succeeded; otherwise, prefer to either retain the stale entry
- // (if extant) or retain no entry at all (if not).
- const Entry newly_inserted = Entry(*contents, clock_->Now());
- cache_.insert_or_assign(context->key, newly_inserted);
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(context->cb),
- FetchResult::Success(
- context->key, newly_inserted.contents,
- newly_inserted.time_of_fetch)));
- } else {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(context->cb),
- FetchResult::Failure(context->key)));
- }
- fetcher_.reset();
- TryToStartNetworkedFetch();
- }
- // The heart of an PrinterConfigCache: the local cache itself.
- base::flat_map<std::string, Entry> cache_;
- // Enqueues networked requests.
- base::queue<std::unique_ptr<FetchContext>> fetch_queue_;
- // Dispenses Time objects to mark time of fetch on Entry instances.
- raw_ptr<const base::Clock> clock_;
- // Dispenses fresh URLLoaderFactory instances; see header comment
- // on Create().
- base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
- loader_factory_dispenser_;
- // Talks to the networked service to fetch resources.
- //
- // Because this class is sequenced, a non-nullptr value here (observed
- // on-sequence) denotes an ongoing fetch. See the
- // TryToStartNetworkedFetch() and FinishNetworkedFetch() methods.
- std::unique_ptr<network::SimpleURLLoader> fetcher_;
- SEQUENCE_CHECKER(sequence_checker_);
- // Dispenses weak pointers to our |fetcher_|. This is necessary
- // because |this| could be deleted while the loader is in flight
- // off-sequence.
- base::WeakPtrFactory<PrinterConfigCacheImpl> weak_factory_;
- };
- // static
- std::unique_ptr<PrinterConfigCache> PrinterConfigCache::Create(
- const base::Clock* clock,
- base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
- loader_factory_dispenser) {
- return std::make_unique<PrinterConfigCacheImpl>(
- clock, std::move(loader_factory_dispenser));
- }
- } // namespace chromeos
|