printer_config_cache.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chromeos/printing/printer_config_cache.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/callback.h"
  9. #include "base/containers/queue.h"
  10. #include "base/location.h"
  11. #include "base/memory/raw_ptr.h"
  12. #include "base/memory/weak_ptr.h"
  13. #include "base/sequence_checker.h"
  14. #include "base/strings/strcat.h"
  15. #include "base/strings/string_piece.h"
  16. #include "base/threading/sequenced_task_runner_handle.h"
  17. #include "base/time/clock.h"
  18. #include "base/time/time.h"
  19. #include "net/base/load_flags.h"
  20. #include "net/base/net_errors.h"
  21. #include "services/network/public/cpp/resource_request.h"
  22. #include "third_party/abseil-cpp/absl/types/optional.h"
  23. #include "url/gurl.h"
  24. namespace chromeos {
  25. namespace {
  26. // Defines the serving root in which all PPDs and PPD metadata reside.
  27. const char kServingRoot[] =
  28. "https://printerconfigurations.googleusercontent.com/"
  29. "chromeos_printing/";
  30. // Prepends the serving root to |name|, returning the result.
  31. std::string PrependServingRoot(const std::string& name) {
  32. return base::StrCat({base::StringPiece(kServingRoot), name});
  33. }
  34. // Accepts a relative |path| to a value in the Chrome OS Printing
  35. // serving root) and returns a resource request to satisfy the same.
  36. std::unique_ptr<network::ResourceRequest> FormRequest(const std::string& path) {
  37. GURL full_url(PrependServingRoot(path));
  38. if (!full_url.is_valid()) {
  39. return nullptr;
  40. }
  41. auto request = std::make_unique<network::ResourceRequest>();
  42. request->url = full_url;
  43. request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
  44. request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  45. return request;
  46. }
  47. } // namespace
  48. // In case of fetch failure, only the key is meaningful feedback.
  49. // static
  50. PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Failure(
  51. const std::string& key) {
  52. return PrinterConfigCache::FetchResult{false, key, std::string(),
  53. base::Time()};
  54. }
  55. // static
  56. PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Success(
  57. const std::string& key,
  58. const std::string& contents,
  59. base::Time time_of_fetch) {
  60. return PrinterConfigCache::FetchResult{true, key, contents, time_of_fetch};
  61. }
  62. class PrinterConfigCacheImpl : public PrinterConfigCache {
  63. public:
  64. explicit PrinterConfigCacheImpl(
  65. const base::Clock* clock,
  66. base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
  67. loader_factory_dispenser)
  68. : clock_(clock),
  69. loader_factory_dispenser_(std::move(loader_factory_dispenser)),
  70. weak_factory_(this) {}
  71. ~PrinterConfigCacheImpl() override {
  72. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  73. }
  74. void Fetch(const std::string& key,
  75. base::TimeDelta expiration,
  76. FetchCallback cb) override {
  77. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  78. // Try to answer this fetch request locally.
  79. const auto& finding = cache_.find(key);
  80. if (finding != cache_.end()) {
  81. const Entry& entry = finding->second;
  82. if (entry.time_of_fetch + expiration > clock_->Now()) {
  83. base::SequencedTaskRunnerHandle::Get()->PostTask(
  84. FROM_HERE, base::BindOnce(std::move(cb), FetchResult::Success(
  85. key, entry.contents,
  86. entry.time_of_fetch)));
  87. return;
  88. }
  89. }
  90. // We couldn't answer this request locally. Issue a networked fetch
  91. // and defer the answer to when we hear back.
  92. auto context = std::make_unique<FetchContext>(key, std::move(cb));
  93. fetch_queue_.push(std::move(context));
  94. TryToStartNetworkedFetch();
  95. }
  96. void Drop(const std::string& key) override {
  97. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  98. cache_.erase(key);
  99. }
  100. private:
  101. // A FetchContext saves off the key and the FetchCallback that a
  102. // caller passes to PrinterConfigCacheImpl::Fetch().
  103. struct FetchContext {
  104. const std::string key;
  105. PrinterConfigCache::FetchCallback cb;
  106. FetchContext(const std::string& arg_key,
  107. PrinterConfigCache::FetchCallback arg_cb)
  108. : key(arg_key), cb(std::move(arg_cb)) {}
  109. ~FetchContext() = default;
  110. };
  111. // If a PrinterConfigCache maps keys to values, then Entry structs
  112. // represent values.
  113. struct Entry {
  114. std::string contents;
  115. base::Time time_of_fetch;
  116. Entry(const std::string& arg_contents, base::Time time)
  117. : contents(arg_contents), time_of_fetch(time) {}
  118. ~Entry() = default;
  119. };
  120. void TryToStartNetworkedFetch() {
  121. // Either
  122. // 1. a networked fetch is already in flight or
  123. // 2. there are no more pending networked fetches to act upon.
  124. // In either case, we can't do anything at the moment; back off
  125. // and let a future call to Fetch() or FinishNetworkedFetch()
  126. // return here to try again.
  127. if (fetcher_ || fetch_queue_.empty()) {
  128. return;
  129. }
  130. std::unique_ptr<FetchContext> context = std::move(fetch_queue_.front());
  131. fetch_queue_.pop();
  132. auto request = FormRequest(context->key);
  133. // TODO(crbug.com/888189): add traffic annotation.
  134. fetcher_ = network::SimpleURLLoader::Create(std::move(request),
  135. MISSING_TRAFFIC_ANNOTATION);
  136. fetcher_->DownloadToString(
  137. loader_factory_dispenser_.Run(),
  138. base::BindOnce(&PrinterConfigCacheImpl::FinishNetworkedFetch,
  139. weak_factory_.GetWeakPtr(), std::move(context)),
  140. network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
  141. }
  142. // Called by |fetcher_| once DownloadToString() completes.
  143. void FinishNetworkedFetch(std::unique_ptr<FetchContext> context,
  144. std::unique_ptr<std::string> contents) {
  145. // Wherever |fetcher_| works its sorcery, it had better have posted
  146. // back onto _our_ sequence.
  147. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  148. if (fetcher_->NetError() == net::Error::OK) {
  149. // We only want to update our local cache if the |fetcher_|
  150. // succeeded; otherwise, prefer to either retain the stale entry
  151. // (if extant) or retain no entry at all (if not).
  152. const Entry newly_inserted = Entry(*contents, clock_->Now());
  153. cache_.insert_or_assign(context->key, newly_inserted);
  154. base::SequencedTaskRunnerHandle::Get()->PostTask(
  155. FROM_HERE, base::BindOnce(std::move(context->cb),
  156. FetchResult::Success(
  157. context->key, newly_inserted.contents,
  158. newly_inserted.time_of_fetch)));
  159. } else {
  160. base::SequencedTaskRunnerHandle::Get()->PostTask(
  161. FROM_HERE, base::BindOnce(std::move(context->cb),
  162. FetchResult::Failure(context->key)));
  163. }
  164. fetcher_.reset();
  165. TryToStartNetworkedFetch();
  166. }
  167. // The heart of an PrinterConfigCache: the local cache itself.
  168. base::flat_map<std::string, Entry> cache_;
  169. // Enqueues networked requests.
  170. base::queue<std::unique_ptr<FetchContext>> fetch_queue_;
  171. // Dispenses Time objects to mark time of fetch on Entry instances.
  172. raw_ptr<const base::Clock> clock_;
  173. // Dispenses fresh URLLoaderFactory instances; see header comment
  174. // on Create().
  175. base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
  176. loader_factory_dispenser_;
  177. // Talks to the networked service to fetch resources.
  178. //
  179. // Because this class is sequenced, a non-nullptr value here (observed
  180. // on-sequence) denotes an ongoing fetch. See the
  181. // TryToStartNetworkedFetch() and FinishNetworkedFetch() methods.
  182. std::unique_ptr<network::SimpleURLLoader> fetcher_;
  183. SEQUENCE_CHECKER(sequence_checker_);
  184. // Dispenses weak pointers to our |fetcher_|. This is necessary
  185. // because |this| could be deleted while the loader is in flight
  186. // off-sequence.
  187. base::WeakPtrFactory<PrinterConfigCacheImpl> weak_factory_;
  188. };
  189. // static
  190. std::unique_ptr<PrinterConfigCache> PrinterConfigCache::Create(
  191. const base::Clock* clock,
  192. base::RepeatingCallback<network::mojom::URLLoaderFactory*()>
  193. loader_factory_dispenser) {
  194. return std::make_unique<PrinterConfigCacheImpl>(
  195. clock, std::move(loader_factory_dispenser));
  196. }
  197. } // namespace chromeos