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