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

Side by Side Diff: chrome/browser/local_discovery/privet_url_fetcher.cc

Issue 1553333002: Move cloud print specific files out of local_discovery (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@moveprn
Patch Set: Created 4 years, 11 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 2013 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 "chrome/browser/local_discovery/privet_url_fetcher.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <limits>
11
12 #include "base/bind.h"
13 #include "base/debug/dump_without_crashing.h"
14 #include "base/json/json_reader.h"
15 #include "base/location.h"
16 #include "base/memory/singleton.h"
17 #include "base/rand_util.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/local_discovery/privet_constants.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/base/load_flags.h"
25 #include "net/http/http_status_code.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_status.h"
28
29 namespace local_discovery {
30
31 namespace {
32
33 typedef std::map<std::string, std::string> TokenMap;
34
35 struct TokenMapHolder {
36 public:
37 static TokenMapHolder* GetInstance() {
38 return base::Singleton<TokenMapHolder>::get();
39 }
40
41 TokenMap map;
42 };
43
44 const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: ";
45 const char kRangeHeaderFormat[] = "Range: bytes=%d-%d";
46 const char kXPrivetEmptyToken[] = "\"\"";
47 const int kPrivetMaxRetries = 20;
48 const int kPrivetTimeoutOnError = 5;
49 const int kHTTPErrorCodeInvalidXPrivetToken = 418;
50
51 std::string MakeRangeHeader(int start, int end) {
52 DCHECK_GE(start, 0);
53 DCHECK_GT(end, 0);
54 DCHECK_GT(end, start);
55 return base::StringPrintf(kRangeHeaderFormat, start, end);
56 }
57
58 } // namespace
59
60 void PrivetURLFetcher::Delegate::OnNeedPrivetToken(
61 PrivetURLFetcher* fetcher,
62 const TokenCallback& callback) {
63 OnError(fetcher, TOKEN_ERROR);
64 }
65
66 bool PrivetURLFetcher::Delegate::OnRawData(PrivetURLFetcher* fetcher,
67 bool response_is_file,
68 const std::string& data_string,
69 const base::FilePath& data_file) {
70 return false;
71 }
72
73 PrivetURLFetcher::PrivetURLFetcher(
74 const GURL& url,
75 net::URLFetcher::RequestType request_type,
76 const scoped_refptr<net::URLRequestContextGetter>& context_getter,
77 PrivetURLFetcher::Delegate* delegate)
78 : url_(url),
79 request_type_(request_type),
80 context_getter_(context_getter),
81 delegate_(delegate),
82 max_retries_(kPrivetMaxRetries),
83 do_not_retry_on_transient_error_(false),
84 send_empty_privet_token_(false),
85 has_byte_range_(false),
86 make_response_file_(false),
87 byte_range_start_(0),
88 byte_range_end_(0),
89 tries_(0),
90 weak_factory_(this) {}
91
92 PrivetURLFetcher::~PrivetURLFetcher() {
93 }
94
95 // static
96 void PrivetURLFetcher::SetTokenForHost(const std::string& host,
97 const std::string& token) {
98 TokenMapHolder::GetInstance()->map[host] = token;
99 }
100
101 // static
102 void PrivetURLFetcher::ResetTokenMapForTests() {
103 TokenMapHolder::GetInstance()->map.clear();
104 }
105
106 void PrivetURLFetcher::SetMaxRetries(int max_retries) {
107 DCHECK_EQ(tries_, 0);
108 max_retries_ = max_retries;
109 }
110
111 void PrivetURLFetcher::DoNotRetryOnTransientError() {
112 DCHECK_EQ(tries_, 0);
113 do_not_retry_on_transient_error_ = true;
114 }
115
116 void PrivetURLFetcher::SendEmptyPrivetToken() {
117 DCHECK_EQ(tries_, 0);
118 send_empty_privet_token_ = true;
119 }
120
121 std::string PrivetURLFetcher::GetPrivetAccessToken() {
122 if (send_empty_privet_token_) {
123 return std::string();
124 }
125
126 TokenMapHolder* token_map_holder = TokenMapHolder::GetInstance();
127 TokenMap::iterator found = token_map_holder->map.find(GetHostString());
128 return found != token_map_holder->map.end() ? found->second : std::string();
129 }
130
131 std::string PrivetURLFetcher::GetHostString() {
132 return url_.GetOrigin().spec();
133 }
134
135 void PrivetURLFetcher::SaveResponseToFile() {
136 DCHECK_EQ(tries_, 0);
137 make_response_file_ = true;
138 }
139
140 void PrivetURLFetcher::SetByteRange(int start, int end) {
141 DCHECK_EQ(tries_, 0);
142 byte_range_start_ = start;
143 byte_range_end_ = end;
144 has_byte_range_ = true;
145 }
146
147 void PrivetURLFetcher::Try() {
148 tries_++;
149 if (tries_ <= max_retries_) {
150 DVLOG(1) << "Attempt: " << tries_;
151 url_fetcher_ = net::URLFetcher::Create(url_, request_type_, this);
152 // Privet requests are relevant to hosts on local network only.
153 url_fetcher_->SetLoadFlags(
154 url_fetcher_->GetLoadFlags() | net::LOAD_BYPASS_PROXY |
155 net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SEND_COOKIES);
156 url_fetcher_->SetRequestContext(context_getter_.get());
157
158 std::string token = GetPrivetAccessToken();
159
160 if (token.empty())
161 token = kXPrivetEmptyToken;
162
163 url_fetcher_->AddExtraRequestHeader(
164 std::string(kXPrivetTokenHeaderPrefix) + token);
165
166 if (has_byte_range_) {
167 url_fetcher_->AddExtraRequestHeader(
168 MakeRangeHeader(byte_range_start_, byte_range_end_));
169 }
170
171 if (make_response_file_) {
172 url_fetcher_->SaveResponseToTemporaryFile(
173 content::BrowserThread::GetMessageLoopProxyForThread(
174 content::BrowserThread::FILE));
175 }
176
177 // URLFetcher requires us to set upload data for POST requests.
178 if (request_type_ == net::URLFetcher::POST) {
179 if (!upload_file_path_.empty()) {
180 url_fetcher_->SetUploadFilePath(
181 upload_content_type_, upload_file_path_, 0 /*offset*/,
182 std::numeric_limits<uint64_t>::max() /*length*/,
183 content::BrowserThread::GetMessageLoopProxyForThread(
184 content::BrowserThread::FILE));
185 } else {
186 url_fetcher_->SetUploadData(upload_content_type_, upload_data_);
187 }
188 }
189
190 url_fetcher_->Start();
191 } else {
192 delegate_->OnError(this, UNKNOWN_ERROR);
193 }
194 }
195
196 void PrivetURLFetcher::Start() {
197 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet.
198
199 if (!url_.is_valid()) {
200 // Not yet clear why it's possible. crbug.com/513505
201 base::debug::DumpWithoutCrashing();
202 return delegate_->OnError(this, UNKNOWN_ERROR);
203 }
204
205 if (!send_empty_privet_token_) {
206 std::string privet_access_token;
207 privet_access_token = GetPrivetAccessToken();
208 if (privet_access_token.empty()) {
209 RequestTokenRefresh();
210 return;
211 }
212 }
213
214 Try();
215 }
216
217 void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type,
218 const std::string& upload_data) {
219 DCHECK(upload_file_path_.empty());
220 upload_content_type_ = upload_content_type;
221 upload_data_ = upload_data;
222 }
223
224 void PrivetURLFetcher::SetUploadFilePath(
225 const std::string& upload_content_type,
226 const base::FilePath& upload_file_path) {
227 DCHECK(upload_data_.empty());
228 upload_content_type_ = upload_content_type;
229 upload_file_path_ = upload_file_path;
230 }
231
232 void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
233 DVLOG(1) << "Status: " << source->GetStatus().status()
234 << ", ResponseCode: " << source->GetResponseCode();
235 if (source->GetStatus().status() != net::URLRequestStatus::CANCELED &&
236 (source->GetResponseCode() == net::HTTP_SERVICE_UNAVAILABLE ||
237 source->GetResponseCode() == net::URLFetcher::RESPONSE_CODE_INVALID)) {
238 ScheduleRetry(kPrivetTimeoutOnError);
239 return;
240 }
241
242 if (!OnURLFetchCompleteDoNotParseData(source)) {
243 // Byte ranges should only be used when we're not parsing the data
244 // as JSON.
245 DCHECK(!has_byte_range_);
246
247 // We should only be saving raw data to a file.
248 DCHECK(!make_response_file_);
249
250 OnURLFetchCompleteParseData(source);
251 }
252 }
253
254 // Note that this function returns "true" in error cases to indicate
255 // that it has fully handled the responses.
256 bool PrivetURLFetcher::OnURLFetchCompleteDoNotParseData(
257 const net::URLFetcher* source) {
258 if (source->GetStatus().status() == net::URLRequestStatus::CANCELED) {
259 delegate_->OnError(this, REQUEST_CANCELED);
260 return true;
261 }
262
263 if (source->GetResponseCode() == kHTTPErrorCodeInvalidXPrivetToken) {
264 RequestTokenRefresh();
265 return true;
266 }
267
268 if (source->GetResponseCode() != net::HTTP_OK &&
269 source->GetResponseCode() != net::HTTP_PARTIAL_CONTENT &&
270 source->GetResponseCode() != net::HTTP_BAD_REQUEST) {
271 delegate_->OnError(this, RESPONSE_CODE_ERROR);
272 return true;
273 }
274
275 if (make_response_file_) {
276 base::FilePath response_file_path;
277
278 if (!source->GetResponseAsFilePath(true, &response_file_path)) {
279 delegate_->OnError(this, UNKNOWN_ERROR);
280 return true;
281 }
282
283 return delegate_->OnRawData(this, true, std::string(), response_file_path);
284 } else {
285 std::string response_str;
286
287 if (!source->GetResponseAsString(&response_str)) {
288 delegate_->OnError(this, UNKNOWN_ERROR);
289 return true;
290 }
291
292 return delegate_->OnRawData(this, false, response_str, base::FilePath());
293 }
294 }
295
296 void PrivetURLFetcher::OnURLFetchCompleteParseData(
297 const net::URLFetcher* source) {
298 // Response contains error description.
299 bool is_error_response = false;
300 if (source->GetResponseCode() != net::HTTP_OK) {
301 delegate_->OnError(this, RESPONSE_CODE_ERROR);
302 return;
303 }
304
305 std::string response_str;
306 if (!source->GetResponseAsString(&response_str)) {
307 delegate_->OnError(this, UNKNOWN_ERROR);
308 return;
309 }
310
311 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS);
312 scoped_ptr<base::Value> value = json_reader.ReadToValue(response_str);
313 if (!value) {
314 delegate_->OnError(this, JSON_PARSE_ERROR);
315 return;
316 }
317
318 const base::DictionaryValue* dictionary_value = NULL;
319
320 if (!value->GetAsDictionary(&dictionary_value)) {
321 delegate_->OnError(this, JSON_PARSE_ERROR);
322 return;
323 }
324
325 std::string error;
326 if (dictionary_value->GetString(kPrivetKeyError, &error)) {
327 if (error == kPrivetErrorInvalidXPrivetToken) {
328 RequestTokenRefresh();
329 return;
330 } else if (PrivetErrorTransient(error)) {
331 if (!do_not_retry_on_transient_error_) {
332 int timeout_seconds;
333 if (!dictionary_value->GetInteger(kPrivetKeyTimeout,
334 &timeout_seconds)) {
335 timeout_seconds = kPrivetDefaultTimeout;
336 }
337
338 ScheduleRetry(timeout_seconds);
339 return;
340 }
341 }
342 is_error_response = true;
343 }
344
345 delegate_->OnParsedJson(this, *dictionary_value, is_error_response);
346 }
347
348 void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) {
349 double random_scaling_factor =
350 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
351
352 int timeout_seconds_randomized =
353 static_cast<int>(timeout_seconds * random_scaling_factor);
354
355 timeout_seconds_randomized =
356 std::max(timeout_seconds_randomized, kPrivetMinimumTimeout);
357
358 // Don't wait because only error callback is going to be called.
359 if (tries_ >= max_retries_)
360 timeout_seconds_randomized = 0;
361
362 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
363 FROM_HERE, base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()),
364 base::TimeDelta::FromSeconds(timeout_seconds_randomized));
365 }
366
367 void PrivetURLFetcher::RequestTokenRefresh() {
368 delegate_->OnNeedPrivetToken(
369 this,
370 base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr()));
371 }
372
373 void PrivetURLFetcher::RefreshToken(const std::string& token) {
374 if (token.empty()) {
375 delegate_->OnError(this, TOKEN_ERROR);
376 } else {
377 SetTokenForHost(GetHostString(), token);
378 Try();
379 }
380 }
381
382 bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) {
383 return (error == kPrivetErrorDeviceBusy) ||
384 (error == kPrivetV3ErrorDeviceBusy) ||
385 (error == kPrivetErrorPendingUserAction) ||
386 (error == kPrivetErrorPrinterBusy);
387 }
388
389 } // namespace local_discovery
OLDNEW
« no previous file with comments | « chrome/browser/local_discovery/privet_url_fetcher.h ('k') | chrome/browser/local_discovery/privet_url_fetcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698