Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/printing/ppd_provider.h" | 5 #include "chromeos/printing/ppd_provider.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 10 #include "base/json/json_parser.h" | 10 #include "base/json/json_parser.h" |
| 11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/sequence_checker.h" | 12 #include "base/sequence_checker.h" |
| 13 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "base/task_runner_util.h" | |
| 16 #include "base/threading/sequenced_task_runner_handle.h" | 17 #include "base/threading/sequenced_task_runner_handle.h" |
| 17 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 18 #include "base/values.h" | 19 #include "base/values.h" |
| 19 #include "chromeos/printing/ppd_cache.h" | 20 #include "chromeos/printing/ppd_cache.h" |
| 20 #include "net/base/load_flags.h" | 21 #include "net/base/load_flags.h" |
| 21 #include "net/http/http_status_code.h" | 22 #include "net/http/http_status_code.h" |
| 22 #include "net/url_request/url_fetcher.h" | 23 #include "net/url_request/url_fetcher.h" |
| 23 #include "net/url_request/url_fetcher_delegate.h" | 24 #include "net/url_request/url_fetcher_delegate.h" |
| 24 #include "net/url_request/url_request_context_getter.h" | 25 #include "net/url_request/url_request_context_getter.h" |
| 25 #include "url/gurl.h" | 26 #include "url/gurl.h" |
| 26 | 27 |
| 27 namespace chromeos { | 28 namespace chromeos { |
| 28 namespace printing { | 29 namespace printing { |
| 29 namespace { | 30 namespace { |
| 30 | 31 |
| 31 // Expected fields from the quirks server. | 32 // Expected fields from the quirks server. |
| 32 const char kJSONPPDKey[] = "compressedPpd"; | 33 const char kJSONPPDKey[] = "compressedPpd"; |
| 33 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; | 34 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; |
| 34 const char kJSONTopListKey[] = "manufacturers"; | 35 const char kJSONTopListKey[] = "manufacturers"; |
| 35 const char kJSONManufacturer[] = "manufacturer"; | 36 const char kJSONManufacturer[] = "manufacturer"; |
| 36 const char kJSONModelList[] = "models"; | 37 const char kJSONModelList[] = "models"; |
| 37 | 38 |
| 38 class PpdProviderImpl; | 39 class PpdProviderImpl; |
| 39 | 40 |
| 40 // URLFetcherDelegate that just forwards the complete callback back to | 41 // URLFetcherDelegate that just invokes a callback when the fetch is complete. |
| 41 // the PpdProvider that owns the delegate. | |
| 42 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate { | 42 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate { |
| 43 public: | 43 public: |
| 44 explicit ForwardingURLFetcherDelegate(PpdProviderImpl* owner) | 44 explicit ForwardingURLFetcherDelegate( |
| 45 : owner_(owner) {} | 45 const base::Callback<void()>& done_callback) |
| 46 : done_callback_(done_callback) {} | |
| 46 ~ForwardingURLFetcherDelegate() override {} | 47 ~ForwardingURLFetcherDelegate() override {} |
| 47 | 48 |
| 48 // URLFetcherDelegate API method. Defined below since we need the | 49 // URLFetcherDelegate API method. Defined below since we need the |
| 49 // PpdProviderImpl definition first. | 50 // PpdProviderImpl definition first. |
| 50 void OnURLFetchComplete(const net::URLFetcher* source) override; | 51 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 52 done_callback_.Run(); | |
| 53 } | |
| 51 | 54 |
| 52 private: | 55 private: |
| 53 // owner of this delegate. | 56 // Callback to be run on fetch complete. |
| 54 PpdProviderImpl* const owner_; | 57 base::Callback<void()> done_callback_; |
| 55 }; | 58 }; |
| 56 | 59 |
| 57 class PpdProviderImpl : public PpdProvider { | 60 class PpdProviderImpl : public PpdProvider { |
| 58 public: | 61 public: |
| 59 PpdProviderImpl( | 62 PpdProviderImpl( |
| 60 const std::string& api_key, | 63 const std::string& api_key, |
| 61 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 64 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 65 scoped_refptr<base::SequencedTaskRunner> io_task_runner, | |
| 62 std::unique_ptr<PpdCache> cache, | 66 std::unique_ptr<PpdCache> cache, |
| 63 const PpdProvider::Options& options) | 67 const PpdProvider::Options& options) |
| 64 : api_key_(api_key), | 68 : api_key_(api_key), |
| 65 forwarding_delegate_(this), | |
| 66 url_context_getter_(url_context_getter), | 69 url_context_getter_(url_context_getter), |
| 70 io_task_runner_(io_task_runner), | |
| 67 cache_(std::move(cache)), | 71 cache_(std::move(cache)), |
| 68 options_(options) { | 72 options_(options), |
| 73 weak_factory_(this) { | |
| 69 CHECK_GT(options_.max_ppd_contents_size_, 0U); | 74 CHECK_GT(options_.max_ppd_contents_size_, 0U); |
| 70 } | 75 } |
| 71 ~PpdProviderImpl() override {} | 76 ~PpdProviderImpl() override {} |
| 72 | 77 |
| 73 void Resolve(const Printer::PpdReference& ppd_reference, | 78 void Resolve(const Printer::PpdReference& ppd_reference, |
| 74 const PpdProvider::ResolveCallback& cb) override { | 79 const PpdProvider::ResolveCallback& cb) override { |
| 75 CHECK(!cb.is_null()); | 80 CHECK(!cb.is_null()); |
| 76 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); | 81 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); |
| 77 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | 82 CHECK(base::SequencedTaskRunnerHandle::IsSet()) |
| 78 << "Resolve must be called from a SequencedTaskRunner context"; | 83 << "Resolve must be called from a SequencedTaskRunner context"; |
| 79 CHECK(resolve_fetcher_ == nullptr) | 84 CHECK(!resolve_inflight_) |
| 80 << "Can't have concurrent PpdProvider Resolve calls"; | 85 << "Can't have concurrent PpdProvider Resolve calls"; |
| 86 resolve_inflight_ = true; | |
| 87 auto cache_result = base::MakeUnique<base::Optional<base::FilePath>>(); | |
| 88 CHECK(io_task_runner_->PostTaskAndReply( | |
|
stevenjb
2016/11/14 23:08:39
Don't wrap non test function calls with CHECK, ins
Carlson
2016/11/14 23:27:41
Done, but for my own edification, is this just you
stevenjb
2016/11/14 23:55:25
It's not explicitly in the guidelines, however thi
| |
| 89 FROM_HERE, base::Bind(&PpdProviderImpl::CacheFindWrapper, | |
|
stevenjb
2016/11/14 23:08:39
nit: This will be easier to follow if the method i
Carlson
2016/11/14 23:27:41
Done.
| |
| 90 weak_factory_.GetWeakPtr(), ppd_reference, | |
| 91 cache_result.get()), | |
| 92 base::Bind(&PpdProviderImpl::ResolveCacheLookupDone, | |
| 93 weak_factory_.GetWeakPtr(), ppd_reference, cb, | |
| 94 std::move(cache_result)))); | |
| 95 } | |
| 81 | 96 |
| 82 base::Optional<base::FilePath> tmp = cache_->Find(ppd_reference); | 97 void QueryAvailable(const QueryAvailableCallback& cb) override { |
| 83 if (tmp) { | 98 CHECK(!cb.is_null()); |
| 99 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | |
| 100 << "QueryAvailable() must be called from a SequencedTaskRunner context"; | |
| 101 auto cache_result = base::MakeUnique< | |
| 102 base::Optional<const PpdProvider::AvailablePrintersMap*>>(); | |
| 103 CHECK(!query_inflight_) | |
| 104 << "Can't have concurrent PpdProvider QueryAvailable calls"; | |
| 105 query_inflight_ = true; | |
| 106 CHECK(io_task_runner_->PostTaskAndReply( | |
| 107 FROM_HERE, base::Bind(&PpdProviderImpl::CacheFindAvailableWrapper, | |
|
stevenjb
2016/11/14 23:08:39
QueryAvailableDoCacheLookup
Carlson
2016/11/14 23:27:41
Done.
| |
| 108 weak_factory_.GetWeakPtr(), cache_result.get()), | |
| 109 base::Bind(&PpdProviderImpl::QueryCacheLookupDone, | |
|
stevenjb
2016/11/14 23:08:39
QueryAvailableCacheLookupDone
Carlson
2016/11/14 23:27:41
Done.
| |
| 110 weak_factory_.GetWeakPtr(), cb, std::move(cache_result)))); | |
| 111 } | |
| 112 | |
| 113 bool CachePpd(const Printer::PpdReference& ppd_reference, | |
| 114 const base::FilePath& ppd_path) override { | |
| 115 std::string buf; | |
| 116 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, | |
| 117 options_.max_ppd_contents_size_)) { | |
| 118 return false; | |
| 119 } | |
| 120 return static_cast<bool>(cache_->Store(ppd_reference, buf)); | |
| 121 } | |
| 122 | |
| 123 private: | |
| 124 // Trivial wrappers around PpdCache::Find() and | |
| 125 // PpdCache::FindAvailablePrinters(). We need these wrappers both because we | |
| 126 // use weak_ptrs to manage lifetime, and so so we need to bind callbacks to | |
|
stevenjb
2016/11/14 23:08:39
s/so so/because/
Carlson
2016/11/14 23:27:41
Done.
| |
| 127 // *this*, and because weak_ptr's preclude return values in posted tasks, so | |
| 128 // we have to use a parameter to return state. | |
|
stevenjb
2016/11/14 23:08:39
FWIW, this isn't that uncommon a pattern, you coul
Carlson
2016/11/14 23:27:41
From the perspective of someone new to this code b
stevenjb
2016/11/14 23:55:25
Acknowledged, but over-documentation is it's own p
| |
| 129 void CacheFindWrapper(const Printer::PpdReference& reference, | |
| 130 base::Optional<base::FilePath>* cache_result) const { | |
| 131 *cache_result = cache_->Find(reference); | |
| 132 } | |
|
stevenjb
2016/11/14 23:08:39
blank line
Carlson
2016/11/14 23:27:41
Done.
| |
| 133 void CacheFindAvailableWrapper( | |
| 134 base::Optional<const PpdProvider::AvailablePrintersMap*>* cache_result) | |
| 135 const { | |
| 136 auto tmp = cache_->FindAvailablePrinters(); | |
| 137 if (tmp != nullptr) { | |
| 138 *cache_result = tmp; | |
| 139 } else { | |
| 140 *cache_result = base::nullopt; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 // Callback that happens when the Resolve() cache lookup completes. If the | |
| 145 // cache satisfied the request, finish the Resolve, otherwise start a URL | |
| 146 // request to satisfy the request. This runs on the same thread as Resolve() | |
| 147 // was invoked on. | |
|
stevenjb
2016/11/14 23:08:39
Last sentence is unnecessary, we have a CHECK on l
Carlson
2016/11/14 23:27:41
Sort of, the CHECK below just guarantees sequence,
stevenjb
2016/11/14 23:55:25
Sure, but in practice this will only get called fr
| |
| 148 void ResolveCacheLookupDone( | |
| 149 const Printer::PpdReference& ppd_reference, | |
| 150 const PpdProvider::ResolveCallback& done_callback, | |
| 151 const std::unique_ptr<base::Optional<base::FilePath>>& cache_result) { | |
| 152 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); | |
| 153 if (*cache_result) { | |
| 84 // Cache hit. Schedule the callback now and return. | 154 // Cache hit. Schedule the callback now and return. |
| 85 base::SequencedTaskRunnerHandle::Get()->PostTask( | 155 resolve_inflight_ = false; |
| 86 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value())); | 156 done_callback.Run(PpdProvider::SUCCESS, cache_result->value()); |
| 87 return; | 157 return; |
| 88 } | 158 } |
| 89 | 159 |
| 90 // We don't have a way to automatically resolve user-supplied PPDs yet. So | 160 // 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 | 161 // 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 | 162 // 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 | 163 // 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 | 164 // probably means the quirks server one doesn't work for some reason, so we |
| 95 // shouldn't silently use it. | 165 // shouldn't silently use it. |
| 96 if (!ppd_reference.user_supplied_ppd_url.empty()) { | 166 if (!ppd_reference.user_supplied_ppd_url.empty()) { |
| 97 base::SequencedTaskRunnerHandle::Get()->PostTask( | 167 resolve_inflight_ = false; |
| 98 FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath())); | 168 done_callback.Run(PpdProvider::NOT_FOUND, base::FilePath()); |
| 99 return; | 169 return; |
| 100 } | 170 } |
| 101 | 171 |
| 102 resolve_reference_ = ppd_reference; | 172 // Missed in the cache, so start a URLRequest to resolve the request. |
| 103 resolve_done_callback_ = cb; | 173 resolve_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>( |
| 174 base::Bind(&PpdProviderImpl::OnResolveFetchComplete, | |
| 175 weak_factory_.GetWeakPtr(), ppd_reference, done_callback)); | |
| 104 | 176 |
| 105 resolve_fetcher_ = | 177 resolve_fetcher_ = net::URLFetcher::Create( |
| 106 net::URLFetcher::Create(GetQuirksServerPpdLookupURL(ppd_reference), | 178 GetQuirksServerPpdLookupURL(ppd_reference), net::URLFetcher::GET, |
| 107 net::URLFetcher::GET, &forwarding_delegate_); | 179 resolve_fetcher_delegate_.get()); |
| 180 | |
| 108 resolve_fetcher_->SetRequestContext(url_context_getter_.get()); | 181 resolve_fetcher_->SetRequestContext(url_context_getter_.get()); |
| 109 resolve_fetcher_->SetLoadFlags( | 182 resolve_fetcher_->SetLoadFlags( |
| 110 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | 183 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
| 111 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | 184 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | |
| 112 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 185 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 113 resolve_fetcher_->Start(); | 186 resolve_fetcher_->Start(); |
| 114 }; | |
| 115 | |
| 116 void AbortResolve() override { | |
| 117 // UrlFetcher guarantees that when the object has been destroyed, no further | |
| 118 // callbacks will occur. | |
| 119 resolve_fetcher_.reset(); | |
| 120 } | 187 } |
| 121 | 188 |
| 122 void QueryAvailable(const QueryAvailableCallback& cb) override { | |
| 123 CHECK(!cb.is_null()); | |
| 124 CHECK(query_sequence_checker_.CalledOnValidSequence()); | |
| 125 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | |
| 126 << "QueryAvailable() must be called from a SequencedTaskRunner context"; | |
| 127 CHECK(query_fetcher_ == nullptr) | |
| 128 << "Can't have concurrent PpdProvider QueryAvailable() calls"; | |
| 129 | |
| 130 const PpdProvider::AvailablePrintersMap* result = | |
| 131 cache_->FindAvailablePrinters(); | |
| 132 if (result != nullptr) { | |
| 133 // Satisfy from cache. | |
| 134 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 135 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, *result)); | |
| 136 return; | |
| 137 } | |
| 138 // Not in the cache, ask QuirksServer. | |
| 139 query_done_callback_ = cb; | |
| 140 | |
| 141 query_fetcher_ = | |
| 142 net::URLFetcher::Create(GetQuirksServerPpdListURL(), | |
| 143 net::URLFetcher::GET, &forwarding_delegate_); | |
| 144 query_fetcher_->SetRequestContext(url_context_getter_.get()); | |
| 145 query_fetcher_->SetLoadFlags( | |
| 146 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
| 147 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | |
| 148 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 149 query_fetcher_->Start(); | |
| 150 } | |
| 151 | |
| 152 void AbortQueryAvailable() override { | |
| 153 // UrlFetcher guarantees that when the object has been destroyed, no further | |
| 154 // callbacks will occur. | |
| 155 query_fetcher_.reset(); | |
| 156 } | |
| 157 | |
| 158 bool CachePpd(const Printer::PpdReference& ppd_reference, | |
| 159 const base::FilePath& ppd_path) override { | |
| 160 std::string buf; | |
| 161 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, | |
| 162 options_.max_ppd_contents_size_)) { | |
| 163 return false; | |
| 164 } | |
| 165 return static_cast<bool>(cache_->Store(ppd_reference, buf)); | |
| 166 } | |
| 167 | |
| 168 // Route to the proper fetch complete handler based on which fetcher caused | |
| 169 // it. | |
| 170 void OnURLFetchComplete(const net::URLFetcher* fetcher) { | |
| 171 if (fetcher == resolve_fetcher_.get()) { | |
| 172 OnResolveFetchComplete(); | |
| 173 } else if (fetcher == query_fetcher_.get()) { | |
| 174 OnQueryAvailableFetchComplete(); | |
| 175 } else { | |
| 176 NOTREACHED() << "Unknown fetcher completed."; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 private: | |
| 181 // Called on the same thread as Resolve() when the fetcher completes its | 189 // Called on the same thread as Resolve() when the fetcher completes its |
| 182 // fetch. | 190 // fetch. |
| 183 void OnResolveFetchComplete() { | 191 void OnResolveFetchComplete( |
| 192 const Printer::PpdReference& ppd_reference, | |
| 193 const PpdProvider::ResolveCallback& done_callback) { | |
| 184 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); | 194 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); |
| 185 // Scope the allocated |resolve_fetcher_| into this function so we clean it | 195 // Scope the allocated |resolve_fetcher_| and |resolve_fetcher_delegate_| |
| 186 // up when we're done here instead of leaving it around until the next | 196 // into this function so we clean it up when we're done here instead of |
| 187 // Resolve() call. | 197 // leaving it around until the next Resolve() call. |
| 188 auto fetcher = std::move(resolve_fetcher_); | 198 auto fetcher_delegate(std::move(resolve_fetcher_delegate_)); |
|
stevenjb
2016/11/14 23:08:39
nit: we don't appear to need fetcher_delegate belo
Carlson
2016/11/14 23:27:41
The delegate holds the callback *currently being i
stevenjb
2016/11/14 23:55:25
Once the function object is invoked, any bound par
| |
| 199 auto fetcher(std::move(resolve_fetcher_)); | |
| 200 resolve_inflight_ = false; | |
| 189 std::string contents; | 201 std::string contents; |
| 190 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { | 202 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { |
| 191 // Something went wrong with the fetch. | 203 // Something went wrong with the fetch. |
| 192 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 204 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 193 return; | 205 return; |
| 194 } | 206 } |
| 195 | 207 |
| 196 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); | 208 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); |
| 197 if (dict == nullptr) { | 209 if (dict == nullptr) { |
| 198 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 210 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 199 return; | 211 return; |
| 200 } | 212 } |
| 201 std::string ppd_contents; | 213 std::string ppd_contents; |
| 202 std::string last_updated_time_string; | 214 std::string last_updated_time_string; |
| 203 uint64_t last_updated_time; | 215 uint64_t last_updated_time; |
| 204 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) && | 216 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) && |
| 205 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) && | 217 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) && |
| 206 base::StringToUint64(last_updated_time_string, &last_updated_time))) { | 218 base::StringToUint64(last_updated_time_string, &last_updated_time))) { |
| 207 // Malformed response. TODO(justincarlson) - LOG something here? | 219 // Malformed response. TODO(justincarlson) - LOG something here? |
| 208 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 220 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 209 return; | 221 return; |
| 210 } | 222 } |
| 211 | 223 |
| 212 if (ppd_contents.size() > options_.max_ppd_contents_size_) { | 224 if (ppd_contents.size() > options_.max_ppd_contents_size_) { |
| 213 // PPD is too big. | 225 // PPD is too big. |
| 214 // | 226 // |
| 215 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to | 227 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to |
| 216 // a user that's not from an explicitly trusted source, we should also | 228 // a user that's not from an explicitly trusted source, we should also |
| 217 // check *uncompressed* size here to head off zip-bombs (e.g. let's | 229 // check *uncompressed* size here to head off zip-bombs (e.g. let's |
| 218 // compress 1GBs of zeros into a 900kb file and see what cups does when it | 230 // compress 1GBs of zeros into a 900kb file and see what cups does when it |
| 219 // tries to expand that...) | 231 // tries to expand that...) |
| 220 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 232 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 221 return; | 233 return; |
| 222 } | 234 } |
| 223 | 235 |
| 224 auto ppd_file = cache_->Store(resolve_reference_, ppd_contents); | 236 auto ppd_file = cache_->Store(ppd_reference, ppd_contents); |
| 225 if (!ppd_file) { | 237 if (!ppd_file) { |
| 226 // Failed to store. | 238 // Failed to store. |
| 227 resolve_done_callback_.Run(PpdProvider::INTERNAL_ERROR, base::FilePath()); | 239 done_callback.Run(PpdProvider::INTERNAL_ERROR, base::FilePath()); |
| 228 return; | 240 return; |
| 229 } | 241 } |
| 230 resolve_done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value()); | 242 done_callback.Run(PpdProvider::SUCCESS, ppd_file.value()); |
| 231 } | 243 } |
| 232 | 244 |
| 233 // Called on the same thread as QueryAvailable() when the fetcher completes | 245 // Called on the same thread as QueryAvailable() when the cache lookup is |
| 234 // its fetch. | 246 // done. If the cache satisfied the request, finish the Query, otherwise |
| 235 void OnQueryAvailableFetchComplete() { | 247 // start a URL request to satisfy the Query. This runs on the same thread as |
| 248 // QueryAvailable() was invoked on. | |
| 249 void QueryCacheLookupDone( | |
| 250 const PpdProvider::QueryAvailableCallback& done_callback, | |
| 251 const std::unique_ptr< | |
| 252 base::Optional<const PpdProvider::AvailablePrintersMap*>>& | |
| 253 cache_result) { | |
| 236 CHECK(query_sequence_checker_.CalledOnValidSequence()); | 254 CHECK(query_sequence_checker_.CalledOnValidSequence()); |
| 237 // Scope the object fetcher into this function so we clean it up when we're | 255 if (*cache_result) { |
| 238 // done here instead of leaving it around until the next QueryAvailable() | 256 query_inflight_ = false; |
| 239 // call. | 257 done_callback.Run(PpdProvider::SUCCESS, *cache_result->value()); |
| 240 auto fetcher = std::move(query_fetcher_); | 258 return; |
| 259 } | |
| 260 // Missed in the cache, start a query. | |
| 261 query_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>( | |
| 262 base::Bind(&PpdProviderImpl::OnQueryAvailableFetchComplete, | |
| 263 weak_factory_.GetWeakPtr(), done_callback)); | |
| 264 | |
| 265 query_fetcher_ = net::URLFetcher::Create(GetQuirksServerPpdListURL(), | |
| 266 net::URLFetcher::GET, | |
| 267 query_fetcher_delegate_.get()); | |
| 268 query_fetcher_->SetRequestContext(url_context_getter_.get()); | |
| 269 query_fetcher_->SetLoadFlags( | |
| 270 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
| 271 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | |
| 272 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 273 query_fetcher_->Start(); | |
| 274 } | |
| 275 | |
| 276 void OnQueryAvailableFetchComplete( | |
| 277 const PpdProvider::QueryAvailableCallback& done_callback) { | |
| 278 CHECK(query_sequence_checker_.CalledOnValidSequence()); | |
| 279 // Scope the object fetcher and task_runner into this function so we clean | |
| 280 // it up when we're done here instead of leaving it around until the next | |
| 281 // QueryAvailable() call. | |
| 282 auto fetcher_delegate(std::move(query_fetcher_delegate_)); | |
|
stevenjb
2016/11/14 23:08:39
Also unused, can use reset().
Carlson
2016/11/14 23:27:41
See above
| |
| 283 auto fetcher(std::move(query_fetcher_)); | |
| 284 query_inflight_ = false; | |
| 241 std::string contents; | 285 std::string contents; |
| 242 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { | 286 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { |
| 243 // Something went wrong with the fetch. | 287 // Something went wrong with the fetch. |
| 244 query_done_callback_.Run(PpdProvider::SERVER_ERROR, | 288 done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap()); |
| 245 AvailablePrintersMap()); | |
| 246 return; | 289 return; |
| 247 } | 290 } |
| 248 | 291 |
| 249 // The server gives us JSON in the form of a list of (manufacturer, list of | 292 // The server gives us JSON in the form of a list of (manufacturer, list of |
| 250 // models) tuples. | 293 // models) tuples. |
| 251 auto top_dict = | 294 auto top_dict = |
| 252 base::DictionaryValue::From(base::JSONReader::Read(contents)); | 295 base::DictionaryValue::From(base::JSONReader::Read(contents)); |
| 253 const base::ListValue* top_list; | 296 const base::ListValue* top_list; |
| 254 if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) { | 297 if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) { |
| 255 LOG(ERROR) << "Malformed response from quirks server"; | 298 LOG(ERROR) << "Malformed response from quirks server"; |
| 256 query_done_callback_.Run(PpdProvider::SERVER_ERROR, | 299 done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap()); |
| 257 PpdProvider::AvailablePrintersMap()); | |
| 258 return; | 300 return; |
| 259 } | 301 } |
| 260 | 302 |
| 261 auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>(); | 303 auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>(); |
| 262 for (const std::unique_ptr<base::Value>& entry : *top_list) { | 304 for (const std::unique_ptr<base::Value>& entry : *top_list) { |
| 263 base::DictionaryValue* dict; | 305 base::DictionaryValue* dict; |
| 264 std::string manufacturer; | 306 std::string manufacturer; |
| 265 std::string model; | 307 std::string model; |
| 266 base::ListValue* model_list; | 308 base::ListValue* model_list; |
| 267 if (!entry->GetAsDictionary(&dict) || | 309 if (!entry->GetAsDictionary(&dict) || |
| 268 !dict->GetString(kJSONManufacturer, &manufacturer) || | 310 !dict->GetString(kJSONManufacturer, &manufacturer) || |
| 269 !dict->GetList(kJSONModelList, &model_list)) { | 311 !dict->GetList(kJSONModelList, &model_list)) { |
| 270 LOG(ERROR) << "Unexpected contents in quirks server printer list."; | 312 LOG(ERROR) << "Unexpected contents in quirks server printer list."; |
| 271 // Just skip this entry instead of aborting the whole thing. | 313 // Just skip this entry instead of aborting the whole thing. |
| 272 continue; | 314 continue; |
| 273 } | 315 } |
| 274 | 316 |
| 275 std::vector<std::string>& dest = (*result)[manufacturer]; | 317 std::vector<std::string>& dest = (*result)[manufacturer]; |
| 276 for (const std::unique_ptr<base::Value>& model_value : *model_list) { | 318 for (const std::unique_ptr<base::Value>& model_value : *model_list) { |
| 277 if (model_value->GetAsString(&model)) { | 319 if (model_value->GetAsString(&model)) { |
| 278 dest.push_back(model); | 320 dest.push_back(model); |
| 279 } else { | 321 } else { |
| 280 LOG(ERROR) << "Skipping unknown model for manufacturer " | 322 LOG(ERROR) << "Skipping unknown model for manufacturer " |
| 281 << manufacturer; | 323 << manufacturer; |
| 282 } | 324 } |
| 283 } | 325 } |
| 284 } | 326 } |
| 285 query_done_callback_.Run(PpdProvider::SUCCESS, *result); | 327 done_callback.Run(PpdProvider::SUCCESS, *result); |
| 286 if (!result->empty()) { | 328 if (!result->empty()) { |
| 287 cache_->StoreAvailablePrinters(std::move(result)); | 329 cache_->StoreAvailablePrinters(std::move(result)); |
| 288 } else { | 330 } else { |
| 289 // An empty map means something is probably wrong; if we cache this map, | 331 // An empty map means something is probably wrong; if we cache this map, |
| 290 // we'll have an empty map until the cache expires. So complain and | 332 // we'll have an empty map until the cache expires. So complain and |
| 291 // refuse to cache. | 333 // refuse to cache. |
| 292 LOG(ERROR) << "Available printers map is unexpectedly empty. Refusing " | 334 LOG(ERROR) << "Available printers map is unexpectedly empty. Refusing " |
| 293 "to cache this."; | 335 "to cache this."; |
| 294 } | 336 } |
| 295 } | 337 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 316 // return true, otherwise return false. | 358 // return true, otherwise return false. |
| 317 bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, | 359 bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, |
| 318 std::string* contents) { | 360 std::string* contents) { |
| 319 return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && | 361 return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && |
| 320 (fetcher.GetResponseCode() == net::HTTP_OK) && | 362 (fetcher.GetResponseCode() == net::HTTP_OK) && |
| 321 fetcher.GetResponseAsString(contents)); | 363 fetcher.GetResponseAsString(contents)); |
| 322 } | 364 } |
| 323 | 365 |
| 324 // State held across a Resolve() call. | 366 // State held across a Resolve() call. |
| 325 | 367 |
| 326 // Reference we're currently trying to resolve. | |
| 327 Printer::PpdReference resolve_reference_; | |
| 328 | |
| 329 // Callback to invoke on completion. | |
| 330 PpdProvider::ResolveCallback resolve_done_callback_; | |
| 331 | |
| 332 // Check that Resolve() and its callback are sequenced appropriately. | 368 // Check that Resolve() and its callback are sequenced appropriately. |
| 333 base::SequenceChecker resolve_sequence_checker_; | 369 base::SequenceChecker resolve_sequence_checker_; |
| 334 | 370 |
| 335 // Fetcher for the current call, if any. | 371 // Fetcher and associated delegate for the current Resolve() call, if a fetch |
| 372 // is in progress. These are both null if no Resolve() is in flight. | |
| 336 std::unique_ptr<net::URLFetcher> resolve_fetcher_; | 373 std::unique_ptr<net::URLFetcher> resolve_fetcher_; |
| 374 std::unique_ptr<ForwardingURLFetcherDelegate> resolve_fetcher_delegate_; | |
| 375 // Is there a current Resolve() inflight? Used to help fail-fast in the case | |
| 376 // of inappropriate concurrent usage. | |
| 377 bool resolve_inflight_ = false; | |
| 337 | 378 |
| 338 // State held across a QueryAvailable() call. | 379 // State held across a QueryAvailable() call. |
| 339 | 380 |
| 340 // Callback to invoke on completion. | |
| 341 PpdProvider::QueryAvailableCallback query_done_callback_; | |
| 342 | |
| 343 // Check that QueryAvailable() and its callback are sequenced appropriately. | 381 // Check that QueryAvailable() and its callback are sequenced appropriately. |
| 344 base::SequenceChecker query_sequence_checker_; | 382 base::SequenceChecker query_sequence_checker_; |
| 345 | 383 |
| 346 // Fetcher for the current call, if any. | 384 // Fetcher and associated delegate for the current QueryAvailable() call, if a |
| 385 // fetch is in progress. These are both null if no QueryAvailable() is in | |
| 386 // flight. | |
| 347 std::unique_ptr<net::URLFetcher> query_fetcher_; | 387 std::unique_ptr<net::URLFetcher> query_fetcher_; |
| 388 std::unique_ptr<ForwardingURLFetcherDelegate> query_fetcher_delegate_; | |
| 389 // Is there a current QueryAvailable() inflight? Used to help fail-fast in | |
| 390 // the case of inappropriate concurrent usage. | |
| 391 bool query_inflight_ = false; | |
| 348 | 392 |
| 349 // Common state. | 393 // Common state. |
| 350 | 394 |
| 351 // API key for accessing quirks server. | 395 // API key for accessing quirks server. |
| 352 const std::string api_key_; | 396 const std::string api_key_; |
| 353 | 397 |
| 354 ForwardingURLFetcherDelegate forwarding_delegate_; | |
| 355 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; | 398 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; |
| 399 scoped_refptr<base::SequencedTaskRunner> io_task_runner_; | |
| 356 std::unique_ptr<PpdCache> cache_; | 400 std::unique_ptr<PpdCache> cache_; |
| 357 | 401 |
| 358 // Construction-time options, immutable. | 402 // Construction-time options, immutable. |
| 359 const PpdProvider::Options options_; | 403 const PpdProvider::Options options_; |
| 404 | |
| 405 base::WeakPtrFactory<PpdProviderImpl> weak_factory_; | |
| 360 }; | 406 }; |
| 361 | 407 |
| 362 void ForwardingURLFetcherDelegate::OnURLFetchComplete( | |
| 363 const net::URLFetcher* source) { | |
| 364 owner_->OnURLFetchComplete(source); | |
| 365 } | |
| 366 | |
| 367 } // namespace | 408 } // namespace |
| 368 | 409 |
| 369 // static | 410 // static |
| 370 std::unique_ptr<PpdProvider> PpdProvider::Create( | 411 std::unique_ptr<PpdProvider> PpdProvider::Create( |
| 371 const std::string& api_key, | 412 const std::string& api_key, |
| 372 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 413 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 414 scoped_refptr<base::SequencedTaskRunner> io_task_runner, | |
| 373 std::unique_ptr<PpdCache> cache, | 415 std::unique_ptr<PpdCache> cache, |
| 374 const PpdProvider::Options& options) { | 416 const PpdProvider::Options& options) { |
| 375 return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter, | 417 return base::MakeUnique<PpdProviderImpl>( |
| 376 std::move(cache), options); | 418 api_key, url_context_getter, io_task_runner, std::move(cache), options); |
| 377 } | 419 } |
| 378 | 420 |
| 379 } // namespace printing | 421 } // namespace printing |
| 380 } // namespace chromeos | 422 } // namespace chromeos |
| OLD | NEW |