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