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