privet_url_fetcher.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 24PrivetURLFetcher::PrivetURLFetcher( 25 const std::string& token, 26 const GURL& url, 27 net::URLFetcher::RequestType request_type, 28 net::URLRequestContextGetter* request_context, 29 PrivetURLFetcher::Delegate* delegate) 30 : privet_access_token_(token), url_(url), request_type_(request_type), 31 request_context_(request_context), delegate_(delegate), tries_(0), 32 weak_factory_(this) { 33 if (privet_access_token_.empty()) 34 privet_access_token_ = kXPrivetEmptyToken; 35} 36 37PrivetURLFetcher::~PrivetURLFetcher() { 38} 39 40void PrivetURLFetcher::Try() { 41 tries_++; 42 if (tries_ < kPrivetMaxRetries) { 43 url_fetcher_.reset(net::URLFetcher::Create(url_, request_type_, this)); 44 url_fetcher_->SetRequestContext(request_context_); 45 url_fetcher_->AddExtraRequestHeader(std::string(kXPrivetTokenHeaderPrefix) + 46 privet_access_token_); 47 48 // URLFetcher requires us to set upload data for POST requests. 49 if (request_type_ == net::URLFetcher::POST) 50 url_fetcher_->SetUploadData(upload_content_type_, upload_data_); 51 52 url_fetcher_->Start(); 53 } else { 54 delegate_->OnError(this, RETRY_ERROR); 55 } 56} 57 58void PrivetURLFetcher::Start() { 59 DCHECK_EQ(tries_, 0); // We haven't called |Start()| yet. 60 Try(); 61} 62 63void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type, 64 const std::string& upload_data) { 65 upload_content_type_ = upload_content_type; 66 upload_data_ = upload_data; 67} 68 69void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 70 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS) { 71 delegate_->OnError(this, URL_FETCH_ERROR); 72 return; 73 } 74 75 if (source->GetResponseCode() != net::HTTP_OK) { 76 delegate_->OnError(this, RESPONSE_CODE_ERROR); 77 return; 78 } 79 80 std::string response_str; 81 82 if (!source->GetResponseAsString(&response_str)) { 83 delegate_->OnError(this, URL_FETCH_ERROR); 84 return; 85 } 86 87 base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS); 88 scoped_ptr<base::Value> value; 89 90 value.reset(json_reader.ReadToValue(response_str)); 91 92 if (!value) { 93 delegate_->OnError(this, JSON_PARSE_ERROR); 94 return; 95 } 96 97 const base::DictionaryValue* dictionary_value; 98 99 if (!value->GetAsDictionary(&dictionary_value)) { 100 delegate_->OnError(this, JSON_PARSE_ERROR); 101 return; 102 } 103 104 std::string error; 105 if (dictionary_value->GetString(kPrivetKeyError, &error)) { 106 if (PrivetErrorTransient(error) || 107 dictionary_value->HasKey(kPrivetKeyTimeout)) { 108 int timeout_seconds; 109 if (!dictionary_value->GetInteger(kPrivetKeyTimeout, &timeout_seconds)) { 110 timeout_seconds = kPrivetDefaultTimeout; 111 } 112 113 ScheduleRetry(timeout_seconds); 114 return; 115 } 116 } 117 118 delegate_->OnParsedJson(this, dictionary_value, 119 dictionary_value->HasKey(kPrivetKeyError)); 120} 121 122void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) { 123 double random_scaling_factor = 124 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; 125 126 int timeout_seconds_randomized = 127 static_cast<int>(timeout_seconds * random_scaling_factor); 128 129 base::MessageLoop::current()->PostDelayedTask( 130 FROM_HERE, 131 base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()), 132 base::TimeDelta::FromSeconds(timeout_seconds_randomized)); 133} 134 135bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) { 136 return (error == kPrivetErrorDeviceBusy) || 137 (error == kPrivetErrorPendingUserAction); 138} 139 140PrivetURLFetcherFactory::PrivetURLFetcherFactory( 141 net::URLRequestContextGetter* request_context) 142 : request_context_(request_context) { 143} 144 145PrivetURLFetcherFactory::~PrivetURLFetcherFactory() { 146} 147 148scoped_ptr<PrivetURLFetcher> PrivetURLFetcherFactory::CreateURLFetcher( 149 const GURL& url, net::URLFetcher::RequestType request_type, 150 PrivetURLFetcher::Delegate* delegate) const { 151 return scoped_ptr<PrivetURLFetcher>( 152 new PrivetURLFetcher(token_, url, request_type, 153 request_context_.get(), delegate)); 154} 155 156} // namespace local_discovery 157