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

Side by Side 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, 1 month 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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
5 #include "chromeos/printing/ppd_provider.h"
6
7 #include <utility>
8
9 #include "base/files/file_util.h"
10 #include "base/json/json_parser.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/sequence_checker.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/sequenced_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "chromeos/printing/ppd_cache.h"
20 #include "net/base/load_flags.h"
21 #include "net/http/http_status_code.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_fetcher_delegate.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "url/gurl.h"
26
27 using base::FilePath;
28 using net::URLFetcher;
29 using std::string;
30 using std::unique_ptr;
31
32 namespace chromeos {
33 namespace printing {
34 namespace {
35
36 // Expected fields from the quirks server.
37 const char kJSONPPDKey[] = "compressedPpd";
38 const char kJSONLastUpdatedKey[] = "lastUpdatedTime";
39
40 class PpdProviderImpl;
41
42 // URLFetcherDelegate that just forwards the complete callback back to
43 // the PpdProvider that owns the delegate.
44 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate {
45 public:
46 explicit ForwardingURLFetcherDelegate(PpdProviderImpl* owner)
47 : owner_(owner) {}
48 ~ForwardingURLFetcherDelegate() override {}
49
50 // URLFetcherDelegate API method. Defined below since we need the
51 // PpdProviderImpl definition first.
52 void OnURLFetchComplete(const URLFetcher* source) override;
53
54 private:
55 // owner of this delegate.
56 PpdProviderImpl* const owner_;
57 };
58
59 class PpdProviderImpl : public PpdProvider {
60 public:
61 PpdProviderImpl(
62 const string& api_key,
63 scoped_refptr<net::URLRequestContextGetter> url_context_getter,
64 unique_ptr<PpdCache> cache,
65 const PpdProvider::Options& options)
66 : api_key_(api_key),
67 forwarding_delegate_(this),
68 url_context_getter_(url_context_getter),
69 cache_(std::move(cache)),
70 options_(options) {
71 CHECK_GT(options_.max_ppd_contents_size_, 0U);
72 }
73 ~PpdProviderImpl() override {}
74
75 void Resolve(const Printer::PpdReference& ppd_reference,
76 PpdProvider::ResolveCallback cb) override {
77 CHECK(sequence_checker_.CalledOnValidSequence());
78 CHECK(base::SequencedTaskRunnerHandle::IsSet())
79 << "Resolve must be called from a SequencedTaskRunner context";
80
81 CHECK(fetcher_ == nullptr)
82 << "Can't have concurrent PpdProvider Resolve calls";
83
84 base::Optional<FilePath> tmp = cache_->Find(ppd_reference);
85 if (tmp) {
86 // Cache hit. Schedule the callback now and return.
87 base::SequencedTaskRunnerHandle::Get()->PostTask(
88 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value()));
89 return;
90 }
91
92 // We don't have a way to automatically resolve user-supplied PPDs yet. So
93 // if we have one specified, and it's not cached, we fail out rather than
94 // fall back to quirks-server based resolution. The reasoning here is that
95 // if the user has specified a PPD when a quirks-server one exists, it
96 // probably means the quirks server one doesn't work for some reason, so we
97 // shouldn't silently use it.
98 if (!ppd_reference.user_supplied_ppd_url.empty()) {
99 base::SequencedTaskRunnerHandle::Get()->PostTask(
100 FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath()));
101 return;
102 }
103
104 active_reference_ = ppd_reference;
105 done_callback_ = cb;
106
107 fetcher_ = URLFetcher::Create(GetQuirksServerLookupURL(ppd_reference),
108 URLFetcher::GET, &forwarding_delegate_);
109 fetcher_->SetRequestContext(url_context_getter_.get());
110 fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
111 net::LOAD_DO_NOT_SAVE_COOKIES |
112 net::LOAD_DO_NOT_SEND_COOKIES |
113 net::LOAD_DO_NOT_SEND_AUTH_DATA);
114 fetcher_->Start();
115 };
116
117 void AbortResolve() override {
118 // UrlFetcher guarantees that when the object has been destroyed, no further
119 // callbacks will occur.
120 fetcher_.reset();
121 }
122
123 bool CachePpd(const Printer::PpdReference& ppd_reference,
124 const base::FilePath& ppd_path) override {
125 string buf;
126 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf,
127 options_.max_ppd_contents_size_)) {
128 return false;
129 }
130 return static_cast<bool>(cache_->Store(ppd_reference, buf));
131 }
132
133 // Called on the same thread as Resolve() when the fetcher completes its
134 // fetch.
135 void OnURLFetchComplete() {
136 CHECK(sequence_checker_.CalledOnValidSequence());
137 // Scope the allocated |fetcher_| into this function so we clean it up when
138 // we're done here instead of leaving it around until the next Resolve call.
139 auto fetcher = std::move(fetcher_);
140 string contents;
141 if ((fetcher->GetStatus().status() != net::URLRequestStatus::SUCCESS) ||
142 (fetcher->GetResponseCode() != net::HTTP_OK) ||
143 !fetcher->GetResponseAsString(&contents)) {
144 // Something went wrong with the fetch.
145 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
146 return;
147 }
148
149 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents));
150 if (dict == nullptr) {
151 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
152 return;
153 }
154 string ppd_contents;
155 string last_updated_time_string;
156 uint64_t last_updated_time;
157 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) &&
158 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) &&
159 base::StringToUint64(last_updated_time_string, &last_updated_time))) {
160 // Malformed response. TODO(justincarlson) - LOG something here?
161 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
162 return;
163 }
164
165 if (ppd_contents.size() > options_.max_ppd_contents_size_) {
166 // PPD is too big.
167 //
168 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
169 // a user that's not from an explicitly trusted source, we should also
170 // check *uncompressed* size here to head off zip-bombs (e.g. let's
171 // compress 1GBs of zeros into a 900kb file and see what cups does when it
172 // tries to expand that...)
173 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
174 return;
175 }
176
177 auto ppd_file = cache_->Store(active_reference_, ppd_contents);
178 if (!ppd_file) {
179 // Failed to store.
180 done_callback_.Run(PpdProvider::INTERNAL_ERROR, FilePath());
181 return;
182 }
183 done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value());
184 }
185
186 private:
187 // Generate a url to look up a manufacturer/model from the quirks server
188 GURL GetQuirksServerLookupURL(
189 const Printer::PpdReference& ppd_reference) const {
190 return GURL(base::StringPrintf(
191 "https://%s/v2/printer/manufacturers/%s/models/%s?key=%s",
192 options_.quirks_server.c_str(),
193 ppd_reference.effective_manufacturer.c_str(),
194 ppd_reference.effective_model.c_str(), api_key_.c_str()));
195 }
196
197 // API key for accessing quirks server.
198 const string api_key_;
199
200 // Reference we're currently trying to resolve.
201 Printer::PpdReference active_reference_;
202
203 ForwardingURLFetcherDelegate forwarding_delegate_;
204 scoped_refptr<net::URLRequestContextGetter> url_context_getter_;
205 unique_ptr<PpdCache> cache_;
206
207 PpdProvider::ResolveCallback done_callback_;
208
209 // Check that Resolve() and its callback are sequenced appropriately.
210 base::SequenceChecker sequence_checker_;
211
212 // Fetcher for the current resolve call, if any.
213 unique_ptr<URLFetcher> fetcher_;
214
215 // Construction-time options, immutable.
216 const PpdProvider::Options options_;
217 };
218
219 void ForwardingURLFetcherDelegate::OnURLFetchComplete(
220 const URLFetcher* source) {
221 owner_->OnURLFetchComplete();
222 }
223
224 } // namespace
225
226 // static
227 unique_ptr<PpdProvider> PpdProvider::Create(
228 const string& api_key,
229 scoped_refptr<net::URLRequestContextGetter> url_context_getter,
230 unique_ptr<PpdCache> cache,
231 const PpdProvider::Options& options) {
232 return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter,
233 std::move(cache), options);
234 }
235
236 } // namespace printing
237 } // namespace chromeos
OLDNEW
« 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