privet_http_impl.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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
26const int kPrivetCancelationTimeoutSeconds = 3;
27}  // namespace
28
29PrivetInfoOperationImpl::PrivetInfoOperationImpl(
30    PrivetHTTPClientImpl* privet_client,
31    PrivetInfoOperation::Delegate* delegate)
32    : privet_client_(privet_client), delegate_(delegate) {
33}
34
35PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
36}
37
38void PrivetInfoOperationImpl::Start() {
39  std::string url = base::StringPrintf(
40      kPrivetInfoURLFormat,
41      privet_client_->host_port().host().c_str(),
42      privet_client_->host_port().port());
43
44  url_fetcher_ = privet_client_->fetcher_factory().CreateURLFetcher(
45      GURL(url), net::URLFetcher::GET, this);
46
47  url_fetcher_->Start();
48}
49
50PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
51  return privet_client_;
52}
53
54void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
55                                      PrivetURLFetcher::ErrorType error) {
56  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
57    delegate_->OnPrivetInfoDone(this, fetcher->response_code(), NULL);
58  } else {
59    delegate_->OnPrivetInfoDone(this, kPrivetHTTPCodeInternalFailure, NULL);
60  }
61}
62
63void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
64                                           const base::DictionaryValue* value,
65                                           bool has_error) {
66  if (!has_error)
67    privet_client_->CacheInfo(value);
68  delegate_->OnPrivetInfoDone(this, fetcher->response_code(), value);
69}
70
71PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
72    PrivetHTTPClientImpl* privet_client,
73    const std::string& user,
74    PrivetRegisterOperation::Delegate* delegate)
75    : user_(user), delegate_(delegate), privet_client_(privet_client),
76      ongoing_(false) {
77}
78
79PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
80}
81
82void PrivetRegisterOperationImpl::Start() {
83  if (privet_client_->fetcher_factory().get_token() == "") {
84    StartInfoOperation();
85    return;
86  }
87
88  ongoing_ = true;
89  next_response_handler_ =
90      base::Bind(&PrivetRegisterOperationImpl::StartResponse,
91                 base::Unretained(this));
92  SendRequest(kPrivetActionStart);
93}
94
95void PrivetRegisterOperationImpl::Cancel() {
96  url_fetcher_.reset();
97
98  if (ongoing_) {
99    // Owned by the message loop.
100    Cancelation* cancelation = new Cancelation(privet_client_, user_);
101
102    base::MessageLoop::current()->PostDelayedTask(
103        FROM_HERE,
104        base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
105                   base::Owned(cancelation)),
106        base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));
107
108    ongoing_ = false;
109  }
110}
111
112void PrivetRegisterOperationImpl::CompleteRegistration() {
113  next_response_handler_ =
114      base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
115                 base::Unretained(this));
116  SendRequest(kPrivetActionComplete);
117}
118
119PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
120  return privet_client_;
121}
122
123void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
124                                          PrivetURLFetcher::ErrorType error) {
125  ongoing_ = false;
126  int visible_http_code = -1;
127  FailureReason reason = FAILURE_NETWORK;
128
129  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
130    visible_http_code = fetcher->response_code();
131    reason = FAILURE_HTTP_ERROR;
132  } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
133    reason = FAILURE_MALFORMED_RESPONSE;
134  }
135
136  delegate_->OnPrivetRegisterError(this,
137                                   current_action_,
138                                   reason,
139                                   visible_http_code,
140                                   NULL);
141}
142
143void PrivetRegisterOperationImpl::OnParsedJson(
144    PrivetURLFetcher* fetcher,
145    const base::DictionaryValue* value,
146    bool has_error) {
147  if (has_error) {
148    std::string error;
149    value->GetString(kPrivetKeyError, &error);
150
151    if (error == kPrivetErrorInvalidXPrivetToken) {
152      StartInfoOperation();
153
154      // Use a list of transient error names, but also detect if a "timeout"
155      // key is present as a fallback.
156    } else if (PrivetErrorTransient(error) ||
157               value->HasKey(kPrivetKeyTimeout)) {
158      int timeout_seconds;
159      double random_scaling_factor =
160          1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
161
162      if (!value->GetInteger(kPrivetKeyTimeout, &timeout_seconds)) {
163        timeout_seconds = kPrivetDefaultTimeout;
164      }
165
166      int timeout_seconds_randomized =
167          static_cast<int>(timeout_seconds * random_scaling_factor);
168
169      base::MessageLoop::current()->PostDelayedTask(
170          FROM_HERE,
171          base::Bind(&PrivetRegisterOperationImpl::SendRequest,
172                     AsWeakPtr(), current_action_),
173                     base::TimeDelta::FromSeconds(timeout_seconds_randomized));
174    } else  {
175      ongoing_ = false;
176      delegate_->OnPrivetRegisterError(this,
177                                       current_action_,
178                                       FAILURE_JSON_ERROR,
179                                       fetcher->response_code(),
180                                       value);
181    }
182
183    return;
184  }
185
186  // TODO(noamsml): Match the user&action with the user&action in the object,
187  // and fail if different.
188
189  next_response_handler_.Run(*value);
190}
191
192void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
193  GURL url = GetURLForActionAndUser(privet_client_, action, user_);
194
195  current_action_ = action;
196  url_fetcher_ = privet_client_->fetcher_factory().CreateURLFetcher(
197      url, net::URLFetcher::POST, this);
198  url_fetcher_->Start();
199}
200
201void PrivetRegisterOperationImpl::StartResponse(
202    const base::DictionaryValue& value) {
203  next_response_handler_ =
204      base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
205                 base::Unretained(this));
206
207  SendRequest(kPrivetActionGetClaimToken);
208}
209
210void PrivetRegisterOperationImpl::GetClaimTokenResponse(
211    const base::DictionaryValue& value) {
212  std::string claimUrl;
213  std::string claimToken;
214  bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
215  bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
216  if (got_url || got_token) {
217    delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl));
218  } else {
219    delegate_->OnPrivetRegisterError(this,
220                                     current_action_,
221                                     FAILURE_MALFORMED_RESPONSE,
222                                     -1,
223                                     NULL);
224  }
225}
226
227void PrivetRegisterOperationImpl::CompleteResponse(
228    const base::DictionaryValue& value) {
229  std::string id;
230  value.GetString(kPrivetKeyDeviceID, &id);
231  ongoing_ = false;
232  delegate_->OnPrivetRegisterDone(this, id);
233}
234
235void PrivetRegisterOperationImpl::OnPrivetInfoDone(
236    PrivetInfoOperation* operation,
237    int http_code,
238    const base::DictionaryValue* value) {
239  // TODO(noamsml): Distinguish between network errors and unparsable JSON in
240  // this case.
241  if (!value) {
242    delegate_->OnPrivetRegisterError(this,
243                                     kPrivetActionNameInfo,
244                                     FAILURE_NETWORK,
245                                     -1,
246                                     NULL);
247    return;
248  }
249
250  // If there is a key in the info response, the InfoOperation
251  // has stored it in the client.
252  if (!value->HasKey(kPrivetInfoKeyToken)) {
253    if (value->HasKey(kPrivetKeyError)) {
254      delegate_->OnPrivetRegisterError(this,
255                                       kPrivetActionNameInfo,
256                                       FAILURE_JSON_ERROR,
257                                       http_code,
258                                       value);
259    } else {
260      delegate_->OnPrivetRegisterError(this,
261                                       kPrivetActionNameInfo,
262                                       FAILURE_MALFORMED_RESPONSE,
263                                       -1,
264                                       NULL);
265    }
266
267    return;
268  }
269
270  if (!ongoing_) {
271    Start();
272  } else {
273    SendRequest(current_action_);
274  }
275}
276
277void PrivetRegisterOperationImpl::StartInfoOperation() {
278  info_operation_ = privet_client_->CreateInfoOperation(this);
279  info_operation_->Start();
280}
281
282bool PrivetRegisterOperationImpl::PrivetErrorTransient(
283    const std::string& error) {
284  return (error == kPrivetErrorDeviceBusy) ||
285         (error == kPrivetErrorPendingUserAction);
286}
287
288// static
289GURL PrivetRegisterOperationImpl::GetURLForActionAndUser(
290    PrivetHTTPClientImpl* privet_client,
291    const std::string& action,
292    const std::string& user) {
293  return GURL(base::StringPrintf(kPrivetRegisterURLFormat,
294                                 privet_client->host_port().host().c_str(),
295                                 privet_client->host_port().port(),
296                                 action.c_str(),
297                                 user.c_str()));
298}
299
300PrivetRegisterOperationImpl::Cancelation::Cancelation(
301    PrivetHTTPClientImpl* privet_client,
302    const std::string& user) {
303  GURL url = GetURLForActionAndUser(privet_client,
304                                    kPrivetActionCancel,
305                                    user);
306  url_fetcher_ = privet_client->fetcher_factory().CreateURLFetcher(
307      url, net::URLFetcher::POST, this);
308  url_fetcher_->Start();
309}
310
311PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
312}
313
314void PrivetRegisterOperationImpl::Cancelation::OnError(
315    PrivetURLFetcher* fetcher,
316    PrivetURLFetcher::ErrorType error) {
317}
318
319void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
320    PrivetURLFetcher* fetcher,
321    const base::DictionaryValue* value,
322    bool has_error) {
323}
324
325void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
326  // Nothing needs to be done, as base::Owned will delete this object,
327  // this callback is just here to pass ownership of the Cancelation to
328  // the message loop.
329}
330
331PrivetHTTPClientImpl::PrivetHTTPClientImpl(
332    const std::string& name,
333    const net::HostPortPair& host_port,
334    net::URLRequestContextGetter* request_context)
335    : name_(name),
336      fetcher_factory_(request_context),
337      host_port_(host_port) {
338}
339
340PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
341}
342
343const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
344  return cached_info_.get();
345}
346
347scoped_ptr<PrivetRegisterOperation>
348PrivetHTTPClientImpl::CreateRegisterOperation(
349    const std::string& user,
350    PrivetRegisterOperation::Delegate* delegate) {
351  return scoped_ptr<PrivetRegisterOperation>(
352      new PrivetRegisterOperationImpl(this, user, delegate));
353}
354
355scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
356    PrivetInfoOperation::Delegate* delegate) {
357  return scoped_ptr<PrivetInfoOperation>(
358      new PrivetInfoOperationImpl(this, delegate));
359}
360
361const std::string& PrivetHTTPClientImpl::GetName() {
362  return name_;
363}
364
365void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) {
366  cached_info_.reset(cached_info->DeepCopy());
367  std::string token;
368  if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) {
369    fetcher_factory_.set_token(token);
370  }
371}
372
373}  // namespace local_discovery
374