privet_http_impl.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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_http_impl.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/rand_util.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/browser/local_discovery/privet_constants.h"
12#include "url/gurl.h"
13
14namespace local_discovery {
15
16namespace {
17// First format argument (string) is the host, second format argument (int) is
18// the port.
19const char kPrivetInfoURLFormat[] = "http://%s:%d/privet/info";
20// First format argument (string) is the host, second format argument (int) is
21// the port, third argument (string) is the action name, fourth argument
22// (string) is the user name.
23const char kPrivetRegisterURLFormat[] =
24    "http://%s:%d/privet/register?action=%s&user=%s";
25}  // namespace
26
27PrivetInfoOperationImpl::PrivetInfoOperationImpl(
28    PrivetHTTPClientImpl* privet_client,
29    PrivetInfoOperation::Delegate* delegate)
30    : privet_client_(privet_client), delegate_(delegate) {
31}
32
33PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
34}
35
36void PrivetInfoOperationImpl::Start() {
37  std::string url = base::StringPrintf(
38      kPrivetInfoURLFormat,
39      privet_client_->host_port().host().c_str(),
40      privet_client_->host_port().port());
41
42  url_fetcher_ = privet_client_->fetcher_factory().CreateURLFetcher(
43      GURL(url), net::URLFetcher::GET, this);
44
45  url_fetcher_->Start();
46}
47
48void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
49                                      PrivetURLFetcher::ErrorType error) {
50  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
51    delegate_->OnPrivetInfoDone(fetcher->response_code(), NULL);
52  } else {
53    delegate_->OnPrivetInfoDone(kPrivetHTTPCodeInternalFailure, NULL);
54  }
55}
56
57void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
58                                           const base::DictionaryValue* value,
59                                           bool has_error) {
60  if (!has_error)
61    privet_client_->CacheInfo(value);
62  delegate_->OnPrivetInfoDone(fetcher->response_code(), value);
63}
64
65PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
66    PrivetHTTPClientImpl* privet_client,
67    const std::string& user,
68    PrivetRegisterOperation::Delegate* delegate)
69    : user_(user), delegate_(delegate), privet_client_(privet_client),
70      ongoing_(false) {
71}
72
73PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
74}
75
76void PrivetRegisterOperationImpl::Start() {
77  if (privet_client_->fetcher_factory().get_token() == "") {
78    StartInfoOperation();
79    return;
80  }
81
82  ongoing_ = true;
83  next_response_handler_ =
84      base::Bind(&PrivetRegisterOperationImpl::StartResponse,
85                 base::Unretained(this));
86  SendRequest(kPrivetActionStart);
87}
88
89void PrivetRegisterOperationImpl::Cancel() {
90  url_fetcher_.reset();
91  // TODO(noamsml): Proper cancelation.
92}
93
94void PrivetRegisterOperationImpl::CompleteRegistration() {
95  next_response_handler_ =
96      base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
97                 base::Unretained(this));
98  SendRequest(kPrivetActionComplete);
99}
100
101void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
102                                          PrivetURLFetcher::ErrorType error) {
103  ongoing_ = false;
104  int visible_http_code = -1;
105  FailureReason reason = FAILURE_NETWORK;
106
107  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
108    visible_http_code = fetcher->response_code();
109    reason = FAILURE_HTTP_ERROR;
110  } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
111    reason = FAILURE_MALFORMED_RESPONSE;
112  }
113
114  delegate_->OnPrivetRegisterError(current_action_,
115                                   reason,
116                                   visible_http_code,
117                                   NULL);
118}
119
120void PrivetRegisterOperationImpl::OnParsedJson(
121    PrivetURLFetcher* fetcher,
122    const base::DictionaryValue* value,
123    bool has_error) {
124  if (has_error) {
125    std::string error;
126    value->GetString(kPrivetKeyError, &error);
127
128    if (error == kPrivetErrorInvalidXPrivetToken) {
129      StartInfoOperation();
130
131      // Use a list of transient error names, but also detect if a "timeout"
132      // key is present as a fallback.
133    } else if (PrivetErrorTransient(error) ||
134               value->HasKey(kPrivetKeyTimeout)) {
135      int timeout_seconds;
136      double random_scaling_factor =
137          1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
138
139      if (!value->GetInteger(kPrivetKeyTimeout, &timeout_seconds)) {
140        timeout_seconds = kPrivetDefaultTimeout;
141      }
142
143      int timeout_seconds_randomized =
144          static_cast<int>(timeout_seconds * random_scaling_factor);
145
146      base::MessageLoop::current()->PostDelayedTask(
147          FROM_HERE,
148          base::Bind(&PrivetRegisterOperationImpl::SendRequest,
149                     AsWeakPtr(), current_action_),
150                     base::TimeDelta::FromSeconds(timeout_seconds_randomized));
151    } else  {
152      ongoing_ = false;
153      delegate_->OnPrivetRegisterError(current_action_,
154                                       FAILURE_JSON_ERROR,
155                                       fetcher->response_code(),
156                                       value);
157    }
158
159    return;
160  }
161
162  // TODO(noamsml): Match the user&action with the user&action in the object,
163  // and fail if different.
164
165  next_response_handler_.Run(*value);
166}
167
168void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
169  std::string url = base::StringPrintf(
170      kPrivetRegisterURLFormat,
171      privet_client_->host_port().host().c_str(),
172      privet_client_->host_port().port(),
173      action.c_str(),
174      user_.c_str());
175
176  current_action_ = action;
177  url_fetcher_ = privet_client_->fetcher_factory().CreateURLFetcher(
178      GURL(url), net::URLFetcher::POST, this);
179  url_fetcher_->Start();
180}
181
182void PrivetRegisterOperationImpl::StartResponse(
183    const base::DictionaryValue& value) {
184  next_response_handler_ =
185      base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
186                 base::Unretained(this));
187
188  SendRequest(kPrivetActionGetClaimToken);
189}
190
191void PrivetRegisterOperationImpl::GetClaimTokenResponse(
192    const base::DictionaryValue& value) {
193  std::string claimUrl;
194  std::string claimToken;
195  bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
196  bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
197  if (got_url || got_token) {
198    delegate_->OnPrivetRegisterClaimToken(claimToken, GURL(claimUrl));
199  } else {
200    delegate_->OnPrivetRegisterError(current_action_,
201                                     FAILURE_MALFORMED_RESPONSE,
202                                     -1,
203                                     NULL);
204  }
205}
206
207void PrivetRegisterOperationImpl::CompleteResponse(
208    const base::DictionaryValue& value) {
209  std::string id;
210  value.GetString(kPrivetKeyDeviceID, &id);
211  ongoing_ = false;
212  delegate_->OnPrivetRegisterDone(id);
213}
214
215void PrivetRegisterOperationImpl::OnPrivetInfoDone(
216    int http_code,
217    const base::DictionaryValue* value) {
218  // TODO(noamsml): Distinguish between network errors and unparsable JSON in
219  // this case.
220  if (!value) {
221    delegate_->OnPrivetRegisterError(kPrivetActionNameInfo,
222                                     FAILURE_NETWORK,
223                                     -1,
224                                     NULL);
225    return;
226  }
227
228  // If there is a key in the info response, the InfoOperation
229  // has stored it in the client.
230  if (!value->HasKey(kPrivetInfoKeyToken)) {
231    if (value->HasKey(kPrivetKeyError)) {
232      delegate_->OnPrivetRegisterError(kPrivetActionNameInfo,
233                                       FAILURE_JSON_ERROR,
234                                       http_code,
235                                       value);
236    } else {
237      delegate_->OnPrivetRegisterError(kPrivetActionNameInfo,
238                                       FAILURE_MALFORMED_RESPONSE,
239                                       -1,
240                                       NULL);
241    }
242
243    return;
244  }
245
246  if (!ongoing_) {
247    Start();
248  } else {
249    SendRequest(current_action_);
250  }
251}
252
253void PrivetRegisterOperationImpl::StartInfoOperation() {
254  info_operation_ = privet_client_->CreateInfoOperation(this);
255  info_operation_->Start();
256}
257
258bool PrivetRegisterOperationImpl::PrivetErrorTransient(
259    const std::string& error) {
260  return (error == kPrivetErrorDeviceBusy) ||
261         (error == kPrivetErrorPendingUserAction);
262}
263
264PrivetHTTPClientImpl::PrivetHTTPClientImpl(
265    const net::HostPortPair& host_port,
266    net::URLRequestContextGetter* request_context)
267    : fetcher_factory_(request_context),
268      host_port_(host_port) {
269}
270
271PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
272}
273
274const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
275  return cached_info_.get();
276}
277
278scoped_ptr<PrivetRegisterOperation>
279PrivetHTTPClientImpl::CreateRegisterOperation(
280    const std::string& user,
281    PrivetRegisterOperation::Delegate* delegate) {
282  return scoped_ptr<PrivetRegisterOperation>(
283      new PrivetRegisterOperationImpl(this, user, delegate));
284}
285
286scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
287    PrivetInfoOperation::Delegate* delegate) {
288  return scoped_ptr<PrivetInfoOperation>(
289      new PrivetInfoOperationImpl(this, delegate));
290}
291
292void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) {
293  cached_info_.reset(cached_info->DeepCopy());
294  std::string token;
295  if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) {
296    fetcher_factory_.set_token(token);
297  }
298}
299
300}  // namespace local_discovery
301