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

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

Powered by Google App Engine
This is Rietveld 408576698