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
AccountSession()34 AccountSession::AccountSession() {}
~AccountSession()35 AccountSession::~AccountSession() {}
36
37 class AccountsRequestDelegate : public net::URLFetcherDelegate {
38 public:
AccountsRequestDelegate(base::RunLoop * run_loop)39 AccountsRequestDelegate(base::RunLoop* run_loop) : response_(""),
40 success_(false), run_loop_(run_loop) {}
41
OnURLFetchComplete(const net::URLFetcher * source)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
response() const62 string response() const { return response_; }
success() const63 bool success() const { return success_; }
64
65 private:
66 string response_;
67 bool success_;
68 base::RunLoop* run_loop_;
69 };
70
TestAccountsClient(const string & server,const string & account_space,const vector<string> & usernames)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
~TestAccountsClient()77 TestAccountsClient::~TestAccountsClient() {}
78
ClaimAccount(AccountSession * session)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<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
ReleaseAccount(const AccountSession & session)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
CreateGURLWithPath(const string & path)132 GURL TestAccountsClient::CreateGURLWithPath(const string& path) {
133 return GURL(base::StringPrintf("%s/%s", server_.c_str(), path.c_str()));
134 }
135
136
SendRequest(const GURL & url,string * response)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