Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Unified Diff: chromeos/printing/ppd_provider.cc

Issue 2343983004: Add PPDProvider barebones implementation and associated cache skeleton. (Closed)
Patch Set: Initial PPDProvider/PPDCache implementation. Also, add associated unittests. This doesn't plumb th… Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chromeos/printing/ppd_provider.h ('k') | chromeos/printing/ppd_provider_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromeos/printing/ppd_provider.cc
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ecd74653c1e2f8b470bcc1ccd4c7b2478dcee972
--- /dev/null
+++ b/chromeos/printing/ppd_provider.cc
@@ -0,0 +1,237 @@
+// Copyright 2016 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/ppd_provider.h"
+
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "base/json/json_parser.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequence_checker.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chromeos/printing/ppd_cache.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+using base::FilePath;
+using net::URLFetcher;
+using std::string;
+using std::unique_ptr;
+
+namespace chromeos {
+namespace printing {
+namespace {
+
+// Expected fields from the quirks server.
+const char kJSONPPDKey[] = "compressedPpd";
+const char kJSONLastUpdatedKey[] = "lastUpdatedTime";
+
+class PpdProviderImpl;
+
+// URLFetcherDelegate that just forwards the complete callback back to
+// the PpdProvider that owns the delegate.
+class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate {
+ public:
+ explicit ForwardingURLFetcherDelegate(PpdProviderImpl* owner)
+ : owner_(owner) {}
+ ~ForwardingURLFetcherDelegate() override {}
+
+ // URLFetcherDelegate API method. Defined below since we need the
+ // PpdProviderImpl definition first.
+ void OnURLFetchComplete(const URLFetcher* source) override;
+
+ private:
+ // owner of this delegate.
+ PpdProviderImpl* const owner_;
+};
+
+class PpdProviderImpl : public PpdProvider {
+ public:
+ PpdProviderImpl(
+ const string& api_key,
+ scoped_refptr<net::URLRequestContextGetter> url_context_getter,
+ unique_ptr<PpdCache> cache,
+ const PpdProvider::Options& options)
+ : api_key_(api_key),
+ forwarding_delegate_(this),
+ url_context_getter_(url_context_getter),
+ cache_(std::move(cache)),
+ options_(options) {
+ CHECK_GT(options_.max_ppd_contents_size_, 0U);
+ }
+ ~PpdProviderImpl() override {}
+
+ void Resolve(const Printer::PpdReference& ppd_reference,
+ PpdProvider::ResolveCallback cb) override {
+ CHECK(sequence_checker_.CalledOnValidSequence());
+ CHECK(base::SequencedTaskRunnerHandle::IsSet())
+ << "Resolve must be called from a SequencedTaskRunner context";
+
+ CHECK(fetcher_ == nullptr)
+ << "Can't have concurrent PpdProvider Resolve calls";
+
+ base::Optional<FilePath> tmp = cache_->Find(ppd_reference);
+ if (tmp) {
+ // Cache hit. Schedule the callback now and return.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value()));
+ return;
+ }
+
+ // We don't have a way to automatically resolve user-supplied PPDs yet. So
+ // if we have one specified, and it's not cached, we fail out rather than
+ // fall back to quirks-server based resolution. The reasoning here is that
+ // if the user has specified a PPD when a quirks-server one exists, it
+ // probably means the quirks server one doesn't work for some reason, so we
+ // shouldn't silently use it.
+ if (!ppd_reference.user_supplied_ppd_url.empty()) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath()));
+ return;
+ }
+
+ active_reference_ = ppd_reference;
+ done_callback_ = cb;
+
+ fetcher_ = URLFetcher::Create(GetQuirksServerLookupURL(ppd_reference),
+ URLFetcher::GET, &forwarding_delegate_);
+ fetcher_->SetRequestContext(url_context_getter_.get());
+ fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA);
+ fetcher_->Start();
+ };
+
+ void AbortResolve() override {
+ // UrlFetcher guarantees that when the object has been destroyed, no further
+ // callbacks will occur.
+ fetcher_.reset();
+ }
+
+ bool CachePpd(const Printer::PpdReference& ppd_reference,
+ const base::FilePath& ppd_path) override {
+ string buf;
+ if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf,
+ options_.max_ppd_contents_size_)) {
+ return false;
+ }
+ return static_cast<bool>(cache_->Store(ppd_reference, buf));
+ }
+
+ // Called on the same thread as Resolve() when the fetcher completes its
+ // fetch.
+ void OnURLFetchComplete() {
+ CHECK(sequence_checker_.CalledOnValidSequence());
+ // Scope the allocated |fetcher_| into this function so we clean it up when
+ // we're done here instead of leaving it around until the next Resolve call.
+ auto fetcher = std::move(fetcher_);
+ string contents;
+ if ((fetcher->GetStatus().status() != net::URLRequestStatus::SUCCESS) ||
+ (fetcher->GetResponseCode() != net::HTTP_OK) ||
+ !fetcher->GetResponseAsString(&contents)) {
+ // Something went wrong with the fetch.
+ done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
+ return;
+ }
+
+ auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents));
+ if (dict == nullptr) {
+ done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
+ return;
+ }
+ string ppd_contents;
+ string last_updated_time_string;
+ uint64_t last_updated_time;
+ if (!(dict->GetString(kJSONPPDKey, &ppd_contents) &&
+ dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) &&
+ base::StringToUint64(last_updated_time_string, &last_updated_time))) {
+ // Malformed response. TODO(justincarlson) - LOG something here?
+ done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
+ return;
+ }
+
+ if (ppd_contents.size() > options_.max_ppd_contents_size_) {
+ // PPD is too big.
+ //
+ // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
+ // a user that's not from an explicitly trusted source, we should also
+ // check *uncompressed* size here to head off zip-bombs (e.g. let's
+ // compress 1GBs of zeros into a 900kb file and see what cups does when it
+ // tries to expand that...)
+ done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
+ return;
+ }
+
+ auto ppd_file = cache_->Store(active_reference_, ppd_contents);
+ if (!ppd_file) {
+ // Failed to store.
+ done_callback_.Run(PpdProvider::INTERNAL_ERROR, FilePath());
+ return;
+ }
+ done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value());
+ }
+
+ private:
+ // Generate a url to look up a manufacturer/model from the quirks server
+ GURL GetQuirksServerLookupURL(
+ const Printer::PpdReference& ppd_reference) const {
+ return GURL(base::StringPrintf(
+ "https://%s/v2/printer/manufacturers/%s/models/%s?key=%s",
+ options_.quirks_server.c_str(),
+ ppd_reference.effective_manufacturer.c_str(),
+ ppd_reference.effective_model.c_str(), api_key_.c_str()));
+ }
+
+ // API key for accessing quirks server.
+ const string api_key_;
+
+ // Reference we're currently trying to resolve.
+ Printer::PpdReference active_reference_;
+
+ ForwardingURLFetcherDelegate forwarding_delegate_;
+ scoped_refptr<net::URLRequestContextGetter> url_context_getter_;
+ unique_ptr<PpdCache> cache_;
+
+ PpdProvider::ResolveCallback done_callback_;
+
+ // Check that Resolve() and its callback are sequenced appropriately.
+ base::SequenceChecker sequence_checker_;
+
+ // Fetcher for the current resolve call, if any.
+ unique_ptr<URLFetcher> fetcher_;
+
+ // Construction-time options, immutable.
+ const PpdProvider::Options options_;
+};
+
+void ForwardingURLFetcherDelegate::OnURLFetchComplete(
+ const URLFetcher* source) {
+ owner_->OnURLFetchComplete();
+}
+
+} // namespace
+
+// static
+unique_ptr<PpdProvider> PpdProvider::Create(
+ const string& api_key,
+ scoped_refptr<net::URLRequestContextGetter> url_context_getter,
+ unique_ptr<PpdCache> cache,
+ const PpdProvider::Options& options) {
+ return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter,
+ std::move(cache), options);
+}
+
+} // namespace printing
+} // namespace chromeos
« no previous file with comments | « chromeos/printing/ppd_provider.h ('k') | chromeos/printing/ppd_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698