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