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 "sync/test/accounts_client/test_accounts_client.h" 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "base/json/json_reader.h" 12 #include "base/json/json_writer.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/run_loop.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/time/time.h" 17 #include "base/values.h" 18 #include "net/base/url_util.h" 19 #include "net/http/http_status_code.h" 20 #include "net/url_request/url_fetcher.h" 21 #include "net/url_request/url_fetcher_delegate.h" 22 #include "net/url_request/url_request.h" 23 #include "net/url_request/url_request_context_getter.h" 24 #include "sync/test/accounts_client/url_request_context_getter.h" 25 26 using std::string; 27 using std::vector; 28 29 static const int kMaxSessionLifetimeSeconds = 30 * 60; 30 static const string kClaimPath = "claim"; 31 static const string kReleasePath = "release"; 32 static const base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10); 33 34 AccountSession::AccountSession() {} 35 AccountSession::~AccountSession() {} 36 37 class AccountsRequestDelegate : public net::URLFetcherDelegate { 38 public: 39 AccountsRequestDelegate(base::RunLoop* run_loop) : response_(""), 40 success_(false), run_loop_(run_loop) {} 41 42 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 43 string url = source->GetURL().spec(); 44 source->GetResponseAsString(&response_); 45 46 if (!source->GetStatus().is_success()) { 47 int error = source->GetStatus().error(); 48 DVLOG(0) << "The request failed with error code " << error << "." 49 << "\nRequested URL: " << url << "."; 50 } else if (source->GetResponseCode() != net::HTTP_OK) { 51 DVLOG(0) << "The request failed with response code " 52 << source->GetResponseCode() << "." 53 << "\nRequested URL: " << url 54 << "\nResponse body: \"" << response_ << "\""; 55 } else { 56 success_ = true; 57 } 58 59 run_loop_->Quit(); 60 } 61 62 string response() const { return response_; } 63 bool success() const { return success_; } 64 65 private: 66 string response_; 67 bool success_; 68 base::RunLoop* run_loop_; 69 }; 70 71 TestAccountsClient::TestAccountsClient(const string& server, 72 const string& account_space, 73 const vector<string>& usernames) 74 : server_(server), account_space_(account_space), usernames_(usernames) { 75 } 76 77 TestAccountsClient::~TestAccountsClient() {} 78 79 bool TestAccountsClient::ClaimAccount(AccountSession* session) { 80 GURL url = CreateGURLWithPath(kClaimPath); 81 url = net::AppendQueryParameter(url, "account_space", account_space_); 82 string max_lifetime_seconds = base::StringPrintf("%d", 83 kMaxSessionLifetimeSeconds); 84 url = net::AppendQueryParameter(url, "max_lifetime_seconds", 85 max_lifetime_seconds); 86 87 // TODO(pvalenzuela): Select N random usernames instead of all usernames. 88 for (vector<string>::iterator it = usernames_.begin(); 89 it != usernames_.end(); ++it) { 90 url = net::AppendQueryParameter(url, "username", *it); 91 } 92 93 string response; 94 if (!SendRequest(url, &response)) { 95 return false; 96 } 97 98 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); 99 base::DictionaryValue* dict_value; 100 if (value != NULL && value->GetAsDictionary(&dict_value) && 101 dict_value != NULL) { 102 dict_value->GetString("username", &session->username); 103 dict_value->GetString("account_space", &session->account_space); 104 dict_value->GetString("session_id", &session->session_id); 105 dict_value->GetString("expiration_time", &session->expiration_time); 106 } else { 107 return false; 108 } 109 110 return true; 111 } 112 113 void TestAccountsClient::ReleaseAccount(const AccountSession& session) { 114 // The expiration_time field is ignored since it isn't passed as part of the 115 // release request. 116 if (session.username.empty() || session.account_space.empty() || 117 account_space_.compare(session.account_space) != 0 || 118 session.session_id.empty()) { 119 return; 120 } 121 122 GURL url = CreateGURLWithPath(kReleasePath); 123 url = net::AppendQueryParameter(url, "account_space", session.account_space); 124 url = net::AppendQueryParameter(url, "username", session.username); 125 url = net::AppendQueryParameter(url, "session_id", session.session_id); 126 127 // This operation is best effort, so don't send any errors back to the caller. 128 string response; 129 SendRequest(url, &response); 130 } 131 132 GURL TestAccountsClient::CreateGURLWithPath(const string& path) { 133 return GURL(base::StringPrintf("%s/%s", server_.c_str(), path.c_str())); 134 } 135 136 137 bool TestAccountsClient::SendRequest(const GURL& url, string* response) { 138 base::MessageLoop* loop = base::MessageLoop::current(); 139 scoped_refptr<URLRequestContextGetter> context_getter( 140 new URLRequestContextGetter(loop->message_loop_proxy())); 141 142 base::RunLoop run_loop; 143 144 AccountsRequestDelegate delegate(&run_loop); 145 scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create( 146 url, net::URLFetcher::POST, &delegate)); 147 fetcher->SetRequestContext(context_getter.get()); 148 fetcher->SetUploadData("application/json", ""); 149 fetcher->Start(); 150 151 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, 152 run_loop.QuitClosure(), 153 kRequestTimeout); 154 run_loop.Run(); 155 156 if (delegate.success()) { 157 *response = delegate.response(); 158 } 159 160 return delegate.success(); 161 } 162