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/memory/singleton.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/rand_util.h" 14 #include "base/strings/stringprintf.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/local_discovery/privet_constants.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "net/http/http_status_code.h" 19 #include "net/url_request/url_request_status.h" 20 21 namespace local_discovery { 22 23 namespace { 24 25 typedef std::map<std::string, std::string> TokenMap; 26 27 struct TokenMapHolder { 28 public: 29 static TokenMapHolder* GetInstance() { 30 return Singleton<TokenMapHolder>::get(); 31 } 32 33 TokenMap map; 34 }; 35 36 const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: "; 37 const char kRangeHeaderFormat[] = "Range: bytes=%d-%d"; 38 const char kXPrivetEmptyToken[] = "\"\""; 39 const int kPrivetMaxRetries = 20; 40 const int kPrivetTimeoutOnError = 5; 41 const int kHTTPErrorCodeInvalidXPrivetToken = 418; 42 43 std::string MakeRangeHeader(int start, int end) { 44 DCHECK_GE(start, 0); 45 DCHECK_GT(end, 0); 46 DCHECK_GT(end, start); 47 return base::StringPrintf(kRangeHeaderFormat, start, end); 48 } 49 50 } // namespace 51 52 void PrivetURLFetcher::Delegate::OnNeedPrivetToken( 53 PrivetURLFetcher* fetcher, 54 const TokenCallback& callback) { 55 OnError(fetcher, TOKEN_ERROR); 56 } 57 58 bool PrivetURLFetcher::Delegate::OnRawData(PrivetURLFetcher* fetcher, 59 bool response_is_file, 60 const std::string& data_string, 61 const base::FilePath& data_file) { 62 return false; 63 } 64 65 PrivetURLFetcher::PrivetURLFetcher( 66 const GURL& url, 67 net::URLFetcher::RequestType request_type, 68 net::URLRequestContextGetter* request_context, 69 PrivetURLFetcher::Delegate* delegate) 70 : url_(url), 71 request_type_(request_type), 72 request_context_(request_context), 73 delegate_(delegate), 74 do_not_retry_on_transient_error_(false), 75 send_empty_privet_token_(false), 76 has_byte_range_(false), 77 make_response_file_(false), 78 byte_range_start_(0), 79 byte_range_end_(0), 80 tries_(0), 81 weak_factory_(this) {} 82 83 PrivetURLFetcher::~PrivetURLFetcher() { 84 } 85 86 // static 87 void PrivetURLFetcher::SetTokenForHost(const std::string& host, 88 const std::string& token) { 89 TokenMapHolder::GetInstance()->map[host] = token; 90 } 91 92 // static 93 void PrivetURLFetcher::ResetTokenMapForTests() { 94 TokenMapHolder::GetInstance()->map.clear(); 95 } 96 97 void PrivetURLFetcher::DoNotRetryOnTransientError() { 98 DCHECK(tries_ == 0); 99 do_not_retry_on_transient_error_ = true; 100 } 101 102 void PrivetURLFetcher::SendEmptyPrivetToken() { 103 DCHECK(tries_ == 0); 104 send_empty_privet_token_ = true; 105 } 106 107 std::string PrivetURLFetcher::GetPrivetAccessToken() { 108 if (send_empty_privet_token_) { 109 return std::string(); 110 } 111 112 TokenMapHolder* token_map_holder = TokenMapHolder::GetInstance(); 113 TokenMap::iterator found = token_map_holder->map.find(GetHostString()); 114 return found != token_map_holder->map.end() ? found->second : std::string(); 115 } 116 117 std::string PrivetURLFetcher::GetHostString() { 118 return url_.GetOrigin().spec(); 119 } 120 121 void PrivetURLFetcher::SaveResponseToFile() { 122 DCHECK(tries_ == 0); 123 make_response_file_ = true; 124 } 125 126 void PrivetURLFetcher::SetByteRange(int start, int end) { 127 DCHECK(tries_ == 0); 128 byte_range_start_ = start; 129 byte_range_end_ = end; 130 has_byte_range_ = true; 131 } 132 133 void PrivetURLFetcher::Try() { 134 tries_++; 135 if (tries_ < kPrivetMaxRetries) { 136 std::string token = GetPrivetAccessToken(); 137 138 if (token.empty()) 139 token = kXPrivetEmptyToken; 140 141 url_fetcher_.reset(net::URLFetcher::Create(url_, request_type_, this)); 142 url_fetcher_->SetRequestContext(request_context_); 143 url_fetcher_->AddExtraRequestHeader(std::string(kXPrivetTokenHeaderPrefix) + 144 token); 145 if (has_byte_range_) { 146 url_fetcher_->AddExtraRequestHeader( 147 MakeRangeHeader(byte_range_start_, byte_range_end_)); 148 } 149 150 if (make_response_file_) { 151 url_fetcher_->SaveResponseToTemporaryFile( 152 content::BrowserThread::GetMessageLoopProxyForThread( 153 content::BrowserThread::FILE)); 154 } 155 156 // URLFetcher requires us to set upload data for POST requests. 157 if (request_type_ == net::URLFetcher::POST) { 158 if (!upload_file_path_.empty()) { 159 url_fetcher_->SetUploadFilePath( 160 upload_content_type_, 161 upload_file_path_, 162 0 /*offset*/, 163 kuint64max /*length*/, 164 content::BrowserThread::GetMessageLoopProxyForThread( 165 content::BrowserThread::FILE)); 166 } else { 167 url_fetcher_->SetUploadData(upload_content_type_, upload_data_); 168 } 169 } 170 171 url_fetcher_->Start(); 172 } else { 173 delegate_->OnError(this, RETRY_ERROR); 174 } 175 } 176 177 void PrivetURLFetcher::Start() { 178 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet. 179 180 if (!send_empty_privet_token_) { 181 std::string privet_access_token; 182 privet_access_token = GetPrivetAccessToken(); 183 if (privet_access_token.empty()) { 184 RequestTokenRefresh(); 185 return; 186 } 187 } 188 189 Try(); 190 } 191 192 void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type, 193 const std::string& upload_data) { 194 DCHECK(upload_file_path_.empty()); 195 upload_content_type_ = upload_content_type; 196 upload_data_ = upload_data; 197 } 198 199 void PrivetURLFetcher::SetUploadFilePath( 200 const std::string& upload_content_type, 201 const base::FilePath& upload_file_path) { 202 DCHECK(upload_data_.empty()); 203 upload_content_type_ = upload_content_type; 204 upload_file_path_ = upload_file_path; 205 } 206 207 void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 208 if (source->GetResponseCode() == net::HTTP_SERVICE_UNAVAILABLE) { 209 ScheduleRetry(kPrivetTimeoutOnError); 210 return; 211 } 212 213 if (!OnURLFetchCompleteDoNotParseData(source)) { 214 // Byte ranges should only be used when we're not parsing the data 215 // as JSON. 216 DCHECK(!has_byte_range_); 217 218 // We should only be saving raw data to a file. 219 DCHECK(!make_response_file_); 220 221 OnURLFetchCompleteParseData(source); 222 } 223 } 224 225 // Note that this function returns "true" in error cases to indicate 226 // that it has fully handled the responses. 227 bool PrivetURLFetcher::OnURLFetchCompleteDoNotParseData( 228 const net::URLFetcher* source) { 229 if (source->GetResponseCode() == kHTTPErrorCodeInvalidXPrivetToken) { 230 RequestTokenRefresh(); 231 return true; 232 } 233 234 if (source->GetResponseCode() != net::HTTP_OK && 235 source->GetResponseCode() != net::HTTP_PARTIAL_CONTENT) { 236 delegate_->OnError(this, RESPONSE_CODE_ERROR); 237 return true; 238 } 239 240 if (make_response_file_) { 241 base::FilePath response_file_path; 242 243 if (!source->GetResponseAsFilePath(true, &response_file_path)) { 244 delegate_->OnError(this, URL_FETCH_ERROR); 245 return true; 246 } 247 248 return delegate_->OnRawData(this, true, std::string(), response_file_path); 249 } else { 250 std::string response_str; 251 252 if (!source->GetResponseAsString(&response_str)) { 253 delegate_->OnError(this, URL_FETCH_ERROR); 254 return true; 255 } 256 257 return delegate_->OnRawData(this, false, response_str, base::FilePath()); 258 } 259 } 260 261 void PrivetURLFetcher::OnURLFetchCompleteParseData( 262 const net::URLFetcher* source) { 263 if (source->GetResponseCode() != net::HTTP_OK) { 264 delegate_->OnError(this, RESPONSE_CODE_ERROR); 265 return; 266 } 267 268 std::string response_str; 269 270 if (!source->GetResponseAsString(&response_str)) { 271 delegate_->OnError(this, URL_FETCH_ERROR); 272 return; 273 } 274 275 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS); 276 scoped_ptr<base::Value> value; 277 278 value.reset(json_reader.ReadToValue(response_str)); 279 280 if (!value) { 281 delegate_->OnError(this, JSON_PARSE_ERROR); 282 return; 283 } 284 285 const base::DictionaryValue* dictionary_value; 286 287 if (!value->GetAsDictionary(&dictionary_value)) { 288 delegate_->OnError(this, JSON_PARSE_ERROR); 289 return; 290 } 291 292 std::string error; 293 if (dictionary_value->GetString(kPrivetKeyError, &error)) { 294 if (error == kPrivetErrorInvalidXPrivetToken) { 295 RequestTokenRefresh(); 296 return; 297 } else if (PrivetErrorTransient(error)) { 298 if (!do_not_retry_on_transient_error_) { 299 int timeout_seconds; 300 if (!dictionary_value->GetInteger(kPrivetKeyTimeout, 301 &timeout_seconds)) { 302 timeout_seconds = kPrivetDefaultTimeout; 303 } 304 305 ScheduleRetry(timeout_seconds); 306 return; 307 } 308 } 309 } 310 311 delegate_->OnParsedJson(this, dictionary_value, 312 dictionary_value->HasKey(kPrivetKeyError)); 313 } 314 315 void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) { 316 double random_scaling_factor = 317 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; 318 319 int timeout_seconds_randomized = 320 static_cast<int>(timeout_seconds * random_scaling_factor); 321 322 timeout_seconds_randomized = 323 std::max(timeout_seconds_randomized, kPrivetMinimumTimeout); 324 325 base::MessageLoop::current()->PostDelayedTask( 326 FROM_HERE, 327 base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()), 328 base::TimeDelta::FromSeconds(timeout_seconds_randomized)); 329 } 330 331 void PrivetURLFetcher::RequestTokenRefresh() { 332 delegate_->OnNeedPrivetToken( 333 this, 334 base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr())); 335 } 336 337 void PrivetURLFetcher::RefreshToken(const std::string& token) { 338 if (token.empty()) { 339 delegate_->OnError(this, TOKEN_ERROR); 340 } else { 341 SetTokenForHost(GetHostString(), token); 342 Try(); 343 } 344 } 345 346 bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) { 347 return (error == kPrivetErrorDeviceBusy) || 348 (error == kPrivetErrorPendingUserAction) || 349 (error == kPrivetErrorPrinterBusy); 350 } 351 352 } // namespace local_discovery 353