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 <algorithm> 8 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" 18 19 namespace local_discovery { 20 21 namespace { 22 const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: "; 23 const char kXPrivetEmptyToken[] = "\"\""; 24 const int kPrivetMaxRetries = 20; 25 const int kPrivetTimeoutOnError = 5; 26 } 27 28 void PrivetURLFetcher::Delegate::OnNeedPrivetToken( 29 PrivetURLFetcher* fetcher, 30 const TokenCallback& callback) { 31 OnError(fetcher, TOKEN_ERROR); 32 } 33 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) { 44 } 45 46 PrivetURLFetcher::~PrivetURLFetcher() { 47 } 48 49 void PrivetURLFetcher::DoNotRetryOnTransientError() { 50 do_not_retry_on_transient_error_ = true; 51 } 52 53 void PrivetURLFetcher::AllowEmptyPrivetToken() { 54 allow_empty_privet_token_ = true; 55 } 56 57 void PrivetURLFetcher::Try() { 58 tries_++; 59 if (tries_ < kPrivetMaxRetries) { 60 std::string token = privet_access_token_; 61 62 if (token.empty()) 63 token = kXPrivetEmptyToken; 64 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); 69 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_); 82 } 83 } 84 85 url_fetcher_->Start(); 86 } else { 87 delegate_->OnError(this, RETRY_ERROR); 88 } 89 } 90 91 void PrivetURLFetcher::Start() { 92 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet. 93 94 if (privet_access_token_.empty() && !allow_empty_privet_token_) { 95 RequestTokenRefresh(); 96 } else { 97 Try(); 98 } 99 } 100 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; 106 } 107 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; 114 } 115 116 void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 117 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS || 118 source->GetResponseCode() == net::HTTP_SERVICE_UNAVAILABLE) { 119 ScheduleRetry(kPrivetTimeoutOnError); 120 return; 121 } 122 123 if (source->GetResponseCode() != net::HTTP_OK) { 124 delegate_->OnError(this, RESPONSE_CODE_ERROR); 125 return; 126 } 127 128 std::string response_str; 129 130 if (!source->GetResponseAsString(&response_str)) { 131 delegate_->OnError(this, URL_FETCH_ERROR); 132 return; 133 } 134 135 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS); 136 scoped_ptr<base::Value> value; 137 138 value.reset(json_reader.ReadToValue(response_str)); 139 140 if (!value) { 141 delegate_->OnError(this, JSON_PARSE_ERROR); 142 return; 143 } 144 145 const base::DictionaryValue* dictionary_value; 146 147 if (!value->GetAsDictionary(&dictionary_value)) { 148 delegate_->OnError(this, JSON_PARSE_ERROR); 149 return; 150 } 151 152 std::string error; 153 if (dictionary_value->GetString(kPrivetKeyError, &error)) { 154 if (error == kPrivetErrorInvalidXPrivetToken) { 155 RequestTokenRefresh(); 156 return; 157 } else if (PrivetErrorTransient(error)) { 158 if (!do_not_retry_on_transient_error_) { 159 int timeout_seconds; 160 if (!dictionary_value->GetInteger(kPrivetKeyTimeout, 161 &timeout_seconds)) { 162 timeout_seconds = kPrivetDefaultTimeout; 163 } 164 165 ScheduleRetry(timeout_seconds); 166 return; 167 } 168 } 169 } 170 171 delegate_->OnParsedJson(this, dictionary_value, 172 dictionary_value->HasKey(kPrivetKeyError)); 173 } 174 175 void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) { 176 double random_scaling_factor = 177 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; 178 179 int timeout_seconds_randomized = 180 static_cast<int>(timeout_seconds * random_scaling_factor); 181 182 timeout_seconds_randomized = 183 std::max(timeout_seconds_randomized, kPrivetMinimumTimeout); 184 185 base::MessageLoop::current()->PostDelayedTask( 186 FROM_HERE, 187 base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()), 188 base::TimeDelta::FromSeconds(timeout_seconds_randomized)); 189 } 190 191 void PrivetURLFetcher::RequestTokenRefresh() { 192 delegate_->OnNeedPrivetToken( 193 this, 194 base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr())); 195 } 196 197 void PrivetURLFetcher::RefreshToken(const std::string& token) { 198 if (token.empty()) { 199 delegate_->OnError(this, TOKEN_ERROR); 200 } else { 201 privet_access_token_ = token; 202 Try(); 203 } 204 } 205 206 bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) { 207 return (error == kPrivetErrorDeviceBusy) || 208 (error == kPrivetErrorPendingUserAction) || 209 (error == kPrivetErrorPrinterBusy); 210 } 211 212 PrivetURLFetcherFactory::PrivetURLFetcherFactory( 213 net::URLRequestContextGetter* request_context) 214 : request_context_(request_context) { 215 } 216 217 PrivetURLFetcherFactory::~PrivetURLFetcherFactory() { 218 } 219 220 scoped_ptr<PrivetURLFetcher> PrivetURLFetcherFactory::CreateURLFetcher( 221 const GURL& url, net::URLFetcher::RequestType request_type, 222 PrivetURLFetcher::Delegate* delegate) const { 223 return scoped_ptr<PrivetURLFetcher>( 224 new PrivetURLFetcher(token_, url, request_type, request_context_.get(), 225 delegate)); 226 } 227 228 } // namespace local_discovery 229