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