privet_url_fetcher.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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 "base/bind.h" 8#include "base/json/json_reader.h" 9#include "base/message_loop/message_loop.h" 10#include "base/rand_util.h" 11#include "chrome/browser/browser_process.h" 12#include "chrome/browser/local_discovery/privet_constants.h" 13#include "net/http/http_status_code.h" 14#include "net/url_request/url_request_status.h" 15 16namespace local_discovery { 17 18namespace { 19const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: "; 20const char kXPrivetEmptyToken[] = "\"\""; 21const int kPrivetMaxRetries = 20; 22} 23 24void PrivetURLFetcher::Delegate::OnNeedPrivetToken( 25 PrivetURLFetcher* fetcher, 26 const TokenCallback& callback) { 27 OnError(fetcher, TOKEN_ERROR); 28} 29 30PrivetURLFetcher::PrivetURLFetcher( 31 const std::string& token, 32 const GURL& url, 33 net::URLFetcher::RequestType request_type, 34 net::URLRequestContextGetter* request_context, 35 PrivetURLFetcher::Delegate* delegate) 36 : privet_access_token_(token), url_(url), request_type_(request_type), 37 request_context_(request_context), delegate_(delegate), 38 do_not_retry_on_transient_error_(false), allow_empty_privet_token_(false), 39 tries_(0), weak_factory_(this) { 40} 41 42PrivetURLFetcher::~PrivetURLFetcher() { 43} 44 45void PrivetURLFetcher::DoNotRetryOnTransientError() { 46 do_not_retry_on_transient_error_ = true; 47} 48 49void PrivetURLFetcher::AllowEmptyPrivetToken() { 50 allow_empty_privet_token_ = true; 51} 52 53void PrivetURLFetcher::Try() { 54 tries_++; 55 if (tries_ < kPrivetMaxRetries) { 56 std::string token = privet_access_token_; 57 58 if (token.empty()) 59 token = kXPrivetEmptyToken; 60 61 url_fetcher_.reset(net::URLFetcher::Create(url_, request_type_, this)); 62 url_fetcher_->SetRequestContext(request_context_); 63 url_fetcher_->AddExtraRequestHeader(std::string(kXPrivetTokenHeaderPrefix) + 64 token); 65 66 // URLFetcher requires us to set upload data for POST requests. 67 if (request_type_ == net::URLFetcher::POST) 68 url_fetcher_->SetUploadData(upload_content_type_, upload_data_); 69 70 url_fetcher_->Start(); 71 } else { 72 delegate_->OnError(this, RETRY_ERROR); 73 } 74} 75 76void PrivetURLFetcher::Start() { 77 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet. 78 79 if (privet_access_token_.empty() && !allow_empty_privet_token_) { 80 RequestTokenRefresh(); 81 } else { 82 Try(); 83 } 84} 85 86void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type, 87 const std::string& upload_data) { 88 upload_content_type_ = upload_content_type; 89 upload_data_ = upload_data; 90} 91 92void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 93 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS) { 94 delegate_->OnError(this, URL_FETCH_ERROR); 95 return; 96 } 97 98 if (source->GetResponseCode() != net::HTTP_OK) { 99 delegate_->OnError(this, RESPONSE_CODE_ERROR); 100 return; 101 } 102 103 std::string response_str; 104 105 if (!source->GetResponseAsString(&response_str)) { 106 delegate_->OnError(this, URL_FETCH_ERROR); 107 return; 108 } 109 110 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS); 111 scoped_ptr<base::Value> value; 112 113 value.reset(json_reader.ReadToValue(response_str)); 114 115 if (!value) { 116 delegate_->OnError(this, JSON_PARSE_ERROR); 117 return; 118 } 119 120 const base::DictionaryValue* dictionary_value; 121 122 if (!value->GetAsDictionary(&dictionary_value)) { 123 delegate_->OnError(this, JSON_PARSE_ERROR); 124 return; 125 } 126 127 std::string error; 128 if (dictionary_value->GetString(kPrivetKeyError, &error)) { 129 if (error == kPrivetErrorInvalidXPrivetToken) { 130 RequestTokenRefresh(); 131 return; 132 } else if (PrivetErrorTransient(error) || 133 dictionary_value->HasKey(kPrivetKeyTimeout)) { 134 if (!do_not_retry_on_transient_error_) { 135 int timeout_seconds; 136 if (!dictionary_value->GetInteger(kPrivetKeyTimeout, 137 &timeout_seconds)) { 138 timeout_seconds = kPrivetDefaultTimeout; 139 } 140 141 ScheduleRetry(timeout_seconds); 142 return; 143 } 144 } 145 } 146 147 delegate_->OnParsedJson(this, dictionary_value, 148 dictionary_value->HasKey(kPrivetKeyError)); 149} 150 151void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) { 152 double random_scaling_factor = 153 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; 154 155 int timeout_seconds_randomized = 156 static_cast<int>(timeout_seconds * random_scaling_factor); 157 158 base::MessageLoop::current()->PostDelayedTask( 159 FROM_HERE, 160 base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()), 161 base::TimeDelta::FromSeconds(timeout_seconds_randomized)); 162} 163 164void PrivetURLFetcher::RequestTokenRefresh() { 165 delegate_->OnNeedPrivetToken( 166 this, 167 base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr())); 168} 169 170void PrivetURLFetcher::RefreshToken(const std::string& token) { 171 if (token.empty()) { 172 delegate_->OnError(this, TOKEN_ERROR); 173 } else { 174 privet_access_token_ = token; 175 Try(); 176 } 177} 178 179bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) { 180 return (error == kPrivetErrorDeviceBusy) || 181 (error == kPrivetErrorPendingUserAction); 182} 183 184PrivetURLFetcherFactory::PrivetURLFetcherFactory( 185 net::URLRequestContextGetter* request_context) 186 : request_context_(request_context) { 187} 188 189PrivetURLFetcherFactory::~PrivetURLFetcherFactory() { 190} 191 192scoped_ptr<PrivetURLFetcher> PrivetURLFetcherFactory::CreateURLFetcher( 193 const GURL& url, net::URLFetcher::RequestType request_type, 194 PrivetURLFetcher::Delegate* delegate) const { 195 return scoped_ptr<PrivetURLFetcher>( 196 new PrivetURLFetcher(token_, url, request_type, request_context_.get(), 197 delegate)); 198} 199 200} // namespace local_discovery 201