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

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

Powered by Google App Engine
This is Rietveld 408576698