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