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

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, 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 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 #include "chromeos/printing/ppd_provider.h"
Lei Zhang 2016/10/19 01:25:10 blank line above.
Carlson 2016/10/19 16:38:27 Done. Am I misrunning lint somehow? I would expe
Lei Zhang 2016/10/19 23:15:46 Indeed, rather strange.
Carlson 2016/10/19 23:38:56 Acknowledged.
5
6 #include <utility>
7
8 #include "base/files/file_util.h"
9 #include "base/json/json_parser.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chromeos/printing/ppd_cache.h"
16 #include "net/base/load_flags.h"
17 #include "net/http/http_status_code.h"
18 #include "net/url_request/url_fetcher.h"
19 #include "net/url_request/url_fetcher_delegate.h"
20 #include "net/url_request/url_request_context_getter.h"
21 #include "url/gurl.h"
22
23 using ::base::DictionaryValue;
24 using ::base::FilePath;
25 using ::base::Optional;
26 using ::base::Value;
27 using ::net::URLFetcher;
28 using ::net::URLFetcherDelegate;
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 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* 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_, static_cast<size_t>(0));
Lei Zhang 2016/10/19 01:25:10 You may be able to just write "0U" and avoid the s
Carlson 2016/10/19 16:38:26 Done.
72 }
73 ~PpdProviderImpl() override {}
74
75 void Resolve(const Printer::PpdReference& ppd_reference,
76 PpdProvider::ResolveCallback cb) override {
77 if (fetcher_ != nullptr) {
Lei Zhang 2016/10/19 01:25:10 fop != nullptr -> foo bar == nullptr -> !bar
Carlson 2016/10/19 16:38:26 Acknowledged.
78 LOG(DFATAL) << "Can't have concurrent PpdProvider Resolve calls";
79 // Even though the caller has done something wrong, try to do something
Lei Zhang 2016/10/19 01:25:10 See the "CHECK(), DCHECK(), and NOTREACHED()" phil
Carlson 2016/10/19 16:38:26 Thanks for the link, it's good to know the consens
Lei Zhang 2016/10/19 23:15:46 "A consequence of this is that you should not hand
Carlson 2016/10/19 23:38:56 Done.
80 // at least semi-graceful if we're not running in debug.
81 url_context_getter_->GetNetworkTaskRunner()->PostTask(
82 FROM_HERE, base::Bind(cb, PpdProvider::INTERNAL_ERROR, FilePath()));
83 return;
84 }
85 Optional<FilePath> tmp = cache_->Find(ppd_reference);
86 if (tmp) {
87 // Cache hit. Schedule the callback right now.
88 url_context_getter_->GetNetworkTaskRunner()->PostTask(
89 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value()));
90 return;
91 }
92
93 // We don't have a way to automatically resolve user-supplied ppds yet. So
94 // if we have one specified, and it's not cached, we fail out rather than
95 // fall back to quirks-server based resolution. The reasoning here is that
96 // if the user has specified a ppd when a quirks-server one exists, it
97 // probably means the quirks server one doesn't work for some reason, so we
98 // shouldn't silently use it.
99 if (!ppd_reference.user_supplied_ppd_url.empty()) {
100 // Cache hit. Schedule the callback right now.
101 url_context_getter_->GetNetworkTaskRunner()->PostTask(
102 FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath()));
103 return;
104 }
105
106 active_reference_ = ppd_reference;
107 done_callback_ = cb;
108
109 fetcher_ = net::URLFetcher::Create(GetQuirksServerLookupURL(ppd_reference),
110 URLFetcher::GET, &forwarding_delegate_);
111 fetcher_->SetRequestContext(url_context_getter_.get());
112 fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
113 net::LOAD_DO_NOT_SAVE_COOKIES |
114 net::LOAD_DO_NOT_SEND_COOKIES |
115 net::LOAD_DO_NOT_SEND_AUTH_DATA);
116 fetcher_->Start();
117 };
118
119 void AbortResolve() override {
120 // UrlFetcher guarantees that when the object has been destroyed, no further
121 // callbacks will occur.
122 fetcher_.reset();
123 }
124
125 bool CachePpd(const Printer::PpdReference& ppd_reference,
126 const base::FilePath& ppd_path) override {
127 string buf;
128 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf,
129 options_.max_ppd_contents_size_)) {
130 return false;
131 }
132 return static_cast<bool>(cache_->Store(ppd_reference, buf));
133 }
134
135 // Called on the network thread when the fetcher completes its fetch.
136 void OnURLFetchComplete() {
137 // Scope the allocated fetcher_ into this function so we clean it up when
Lei Zhang 2016/10/19 01:25:10 |fetcher_|
Carlson 2016/10/19 16:38:27 Done.
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 parsed_json = ::base::JSONReader::Read(contents);
Lei Zhang 2016/10/19 01:25:10 You can take advantage of base::DictionaryValue::F
Carlson 2016/10/19 16:38:27 Thanks for the pointer. Done, but I don't feel en
Lei Zhang 2016/10/19 23:15:46 Apparently I approved the CL that added that comme
Carlson 2016/10/19 23:38:56 Ironically, I think this is a case of special-valu
Lei Zhang 2016/10/19 23:51:54 Given it was added to deal with all the places whe
150 DictionaryValue* dict;
151 if (parsed_json == nullptr || //
Lei Zhang 2016/10/19 01:25:10 I also have not seen anyone else do this. It's an
Carlson 2016/10/19 16:38:27 Sorry, not clear on what 'this' refers to here. C
Lei Zhang 2016/10/19 23:15:46 Putting "//" at the end to avoid formatting. It, u
Carlson 2016/10/19 23:38:56 Acknowledged.
152 !parsed_json->GetAsDictionary(&dict)) {
153 // Malformed response. TODO(justincarlson) - LOG something here?
Lei Zhang 2016/10/19 01:25:10 Or let the caller handle it since it needs to chec
Carlson 2016/10/19 16:38:27 Good point, removed TODO
154 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
155 return;
156 }
157 string ppd_contents;
158 string last_updated_time_string;
159 uint64_t last_updated_time;
160 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) &&
161 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) &&
162 ::base::StringToUint64(last_updated_time_string,
163 &last_updated_time))) {
164 // Malformed response. TODO(justincarlson) - LOG something here?
165 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
166 return;
167 }
168
169 if (ppd_contents.size() > options_.max_ppd_contents_size_) {
170 // PPD is too big.
171 //
172 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
173 // a user that's not from an explicitly trusted source, we should also
174 // check *uncompressed* size here to head off zip-bombs (e.g. let's
175 // compress 1GBs of zeros into a 900kb file and see what cups does when it
176 // tries to expand that...)
177 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath());
178 return;
179 }
180
181 auto ppd_file = cache_->Store(active_reference_, ppd_contents);
182 if (!ppd_file) {
183 // Failed to store.
184 done_callback_.Run(PpdProvider::INTERNAL_ERROR, FilePath());
185 return;
186 }
187 done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value());
188 }
189
190 private:
191 // Generate a url to look up a manufacturer/model from the quirks server
192 GURL GetQuirksServerLookupURL(
193 const Printer::PpdReference& ppd_reference) const {
194 return GURL(::base::JoinString(
195 {
196 "https://", //
Lei Zhang 2016/10/19 01:25:10 Again, I don't struggle with clang-format, so I do
Carlson 2016/10/19 16:38:26 (isn't clang-format what's underneath 'git cl form
197 options_.quirks_server, //
198 "/v2/printer/manufacturers/", //
199 ppd_reference.effective_manufacturer, //
200 "/models/", //
201 ppd_reference.effective_model, //
202 "?key=", //
203 api_key_ //
204 },
205 ""));
206 }
207
208 // API key for accessing quirks server.
209 const string api_key_;
210
211 // Reference we're currently trying to resolve.
212 Printer::PpdReference active_reference_;
213
214 ForwardingURLFetcherDelegate forwarding_delegate_;
215 scoped_refptr<net::URLRequestContextGetter> url_context_getter_;
216 unique_ptr<PpdCache> cache_;
217
218 PpdProvider::ResolveCallback done_callback_;
219
220 // Fetcher for the current resolve call, if any.
221 unique_ptr<::net::URLFetcher> fetcher_;
222
223 // Construction-time options, immutable.
224 const PpdProvider::Options options_;
225 };
226
227 void ForwardingURLFetcherDelegate::OnURLFetchComplete(
Lei Zhang 2016/10/19 01:25:10 Since you implemented PpdProviderImpl methods inli
Carlson 2016/10/19 16:38:26 I wish I could, but I can't because of recursive d
228 const URLFetcher* source) {
229 owner_->OnURLFetchComplete();
230 }
231
232 } // namespace
233
234 // static
235 unique_ptr<PpdProvider> PpdProvider::Create(
236 const string& api_key,
237 scoped_refptr<net::URLRequestContextGetter> url_context_getter,
238 unique_ptr<PpdCache> cache,
239 const PpdProvider::Options& options) {
240 return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter,
241 std::move(cache), options);
242 }
243
244 } // namespace printing
245 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698