Home | History | Annotate | Download | only in local_discovery
      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 
     14 namespace local_discovery {
     15 
     16 namespace {
     17 // First format argument (string) is the host, second format argument (int) is
     18 // the port.
     19 const 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.
     23 const char kPrivetRegisterURLFormat[] =
     24     "http://%s:%d/privet/register?action=%s&user=%s";
     25 }  // namespace
     26 
     27 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
     28     PrivetHTTPClientImpl* privet_client,
     29     PrivetInfoOperation::Delegate* delegate)
     30     : privet_client_(privet_client), delegate_(delegate) {
     31 }
     32 
     33 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
     34 }
     35 
     36 void 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 
     48 void 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 
     57 void 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 
     65 PrivetRegisterOperationImpl::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 
     73 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
     74 }
     75 
     76 void 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 
     89 void PrivetRegisterOperationImpl::Cancel() {
     90   url_fetcher_.reset();
     91   // TODO(noamsml): Proper cancelation.
     92 }
     93 
     94 void PrivetRegisterOperationImpl::CompleteRegistration() {
     95   next_response_handler_ =
     96       base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
     97                  base::Unretained(this));
     98   SendRequest(kPrivetActionComplete);
     99 }
    100 
    101 void 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 
    120 void 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 
    168 void 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 
    182 void 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 
    191 void 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 
    207 void 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 
    215 void 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 
    253 void PrivetRegisterOperationImpl::StartInfoOperation() {
    254   info_operation_ = privet_client_->CreateInfoOperation(this);
    255   info_operation_->Start();
    256 }
    257 
    258 bool PrivetRegisterOperationImpl::PrivetErrorTransient(
    259     const std::string& error) {
    260   return (error == kPrivetErrorDeviceBusy) ||
    261          (error == kPrivetErrorPendingUserAction);
    262 }
    263 
    264 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
    265     const net::HostPortPair& host_port,
    266     net::URLRequestContextGetter* request_context)
    267     : fetcher_factory_(request_context),
    268       host_port_(host_port) {
    269 }
    270 
    271 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
    272 }
    273 
    274 const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
    275   return cached_info_.get();
    276 }
    277 
    278 scoped_ptr<PrivetRegisterOperation>
    279 PrivetHTTPClientImpl::CreateRegisterOperation(
    280     const std::string& user,
    281     PrivetRegisterOperation::Delegate* delegate) {
    282   return scoped_ptr<PrivetRegisterOperation>(
    283       new PrivetRegisterOperationImpl(this, user, delegate));
    284 }
    285 
    286 scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
    287     PrivetInfoOperation::Delegate* delegate) {
    288   return scoped_ptr<PrivetInfoOperation>(
    289       new PrivetInfoOperationImpl(this, delegate));
    290 }
    291 
    292 void 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