Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / local_discovery / privet_url_fetcher.cc
blob349555086c6c7c569d022307267fdb998d41b69a
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.
5 #include "chrome/browser/local_discovery/privet_url_fetcher.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/json/json_reader.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/rand_util.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/local_discovery/privet_constants.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/http/http_status_code.h"
17 #include "net/url_request/url_request_status.h"
19 namespace local_discovery {
21 namespace {
22 const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: ";
23 const char kXPrivetEmptyToken[] = "\"\"";
24 const int kPrivetMaxRetries = 20;
25 const int kPrivetTimeoutOnError = 5;
28 void PrivetURLFetcher::Delegate::OnNeedPrivetToken(
29 PrivetURLFetcher* fetcher,
30 const TokenCallback& callback) {
31 OnError(fetcher, TOKEN_ERROR);
34 PrivetURLFetcher::PrivetURLFetcher(
35 const std::string& token,
36 const GURL& url,
37 net::URLFetcher::RequestType request_type,
38 net::URLRequestContextGetter* request_context,
39 PrivetURLFetcher::Delegate* delegate)
40 : privet_access_token_(token), url_(url), request_type_(request_type),
41 request_context_(request_context), delegate_(delegate),
42 do_not_retry_on_transient_error_(false), allow_empty_privet_token_(false),
43 tries_(0), weak_factory_(this) {
46 PrivetURLFetcher::~PrivetURLFetcher() {
49 void PrivetURLFetcher::DoNotRetryOnTransientError() {
50 do_not_retry_on_transient_error_ = true;
53 void PrivetURLFetcher::AllowEmptyPrivetToken() {
54 allow_empty_privet_token_ = true;
57 void PrivetURLFetcher::Try() {
58 tries_++;
59 if (tries_ < kPrivetMaxRetries) {
60 std::string token = privet_access_token_;
62 if (token.empty())
63 token = kXPrivetEmptyToken;
65 url_fetcher_.reset(net::URLFetcher::Create(url_, request_type_, this));
66 url_fetcher_->SetRequestContext(request_context_);
67 url_fetcher_->AddExtraRequestHeader(std::string(kXPrivetTokenHeaderPrefix) +
68 token);
70 // URLFetcher requires us to set upload data for POST requests.
71 if (request_type_ == net::URLFetcher::POST) {
72 if (!upload_file_path_.empty()) {
73 url_fetcher_->SetUploadFilePath(
74 upload_content_type_,
75 upload_file_path_,
76 0 /*offset*/,
77 kuint64max /*length*/,
78 content::BrowserThread::GetMessageLoopProxyForThread(
79 content::BrowserThread::FILE));
80 } else {
81 url_fetcher_->SetUploadData(upload_content_type_, upload_data_);
85 url_fetcher_->Start();
86 } else {
87 delegate_->OnError(this, RETRY_ERROR);
91 void PrivetURLFetcher::Start() {
92 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet.
94 if (privet_access_token_.empty() && !allow_empty_privet_token_) {
95 RequestTokenRefresh();
96 } else {
97 Try();
101 void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type,
102 const std::string& upload_data) {
103 DCHECK(upload_file_path_.empty());
104 upload_content_type_ = upload_content_type;
105 upload_data_ = upload_data;
108 void PrivetURLFetcher::SetUploadFilePath(
109 const std::string& upload_content_type,
110 const base::FilePath& upload_file_path) {
111 DCHECK(upload_data_.empty());
112 upload_content_type_ = upload_content_type;
113 upload_file_path_ = upload_file_path;
116 void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
117 if (source->GetResponseCode() == net::HTTP_SERVICE_UNAVAILABLE) {
118 ScheduleRetry(kPrivetTimeoutOnError);
119 return;
122 if (source->GetResponseCode() != net::HTTP_OK) {
123 delegate_->OnError(this, RESPONSE_CODE_ERROR);
124 return;
127 std::string response_str;
129 if (!source->GetResponseAsString(&response_str)) {
130 delegate_->OnError(this, URL_FETCH_ERROR);
131 return;
134 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS);
135 scoped_ptr<base::Value> value;
137 value.reset(json_reader.ReadToValue(response_str));
139 if (!value) {
140 delegate_->OnError(this, JSON_PARSE_ERROR);
141 return;
144 const base::DictionaryValue* dictionary_value;
146 if (!value->GetAsDictionary(&dictionary_value)) {
147 delegate_->OnError(this, JSON_PARSE_ERROR);
148 return;
151 std::string error;
152 if (dictionary_value->GetString(kPrivetKeyError, &error)) {
153 if (error == kPrivetErrorInvalidXPrivetToken) {
154 RequestTokenRefresh();
155 return;
156 } else if (PrivetErrorTransient(error)) {
157 if (!do_not_retry_on_transient_error_) {
158 int timeout_seconds;
159 if (!dictionary_value->GetInteger(kPrivetKeyTimeout,
160 &timeout_seconds)) {
161 timeout_seconds = kPrivetDefaultTimeout;
164 ScheduleRetry(timeout_seconds);
165 return;
170 delegate_->OnParsedJson(this, dictionary_value,
171 dictionary_value->HasKey(kPrivetKeyError));
174 void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) {
175 double random_scaling_factor =
176 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
178 int timeout_seconds_randomized =
179 static_cast<int>(timeout_seconds * random_scaling_factor);
181 timeout_seconds_randomized =
182 std::max(timeout_seconds_randomized, kPrivetMinimumTimeout);
184 base::MessageLoop::current()->PostDelayedTask(
185 FROM_HERE,
186 base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()),
187 base::TimeDelta::FromSeconds(timeout_seconds_randomized));
190 void PrivetURLFetcher::RequestTokenRefresh() {
191 delegate_->OnNeedPrivetToken(
192 this,
193 base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr()));
196 void PrivetURLFetcher::RefreshToken(const std::string& token) {
197 if (token.empty()) {
198 delegate_->OnError(this, TOKEN_ERROR);
199 } else {
200 privet_access_token_ = token;
201 Try();
205 bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) {
206 return (error == kPrivetErrorDeviceBusy) ||
207 (error == kPrivetErrorPendingUserAction) ||
208 (error == kPrivetErrorPrinterBusy);
211 PrivetURLFetcherFactory::PrivetURLFetcherFactory(
212 net::URLRequestContextGetter* request_context)
213 : request_context_(request_context) {
216 PrivetURLFetcherFactory::~PrivetURLFetcherFactory() {
219 scoped_ptr<PrivetURLFetcher> PrivetURLFetcherFactory::CreateURLFetcher(
220 const GURL& url, net::URLFetcher::RequestType request_type,
221 PrivetURLFetcher::Delegate* delegate) const {
222 return scoped_ptr<PrivetURLFetcher>(
223 new PrivetURLFetcher(token_, url, request_type, request_context_.get(),
224 delegate));
227 } // namespace local_discovery