Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: chrome/browser/sync/engine/net/server_connection_manager.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/sync/engine/net/server_connection_manager.h"
6
7 #include <errno.h>
8
9 #include <ostream>
10 #include <string>
11 #include <vector>
12
13 #include "chrome/browser/sync/engine/net/http_return.h"
14 #include "chrome/browser/sync/engine/net/url_translator.h"
15 #include "chrome/browser/sync/engine/syncapi.h"
16 #include "chrome/browser/sync/engine/syncer.h"
17 #include "chrome/browser/sync/engine/syncproto.h"
18 #include "chrome/browser/sync/protocol/sync.pb.h"
19 #include "chrome/browser/sync/syncable/directory_manager.h"
20 #include "chrome/browser/sync/util/character_set_converters.h"
21 #include "chrome/browser/sync/util/event_sys-inl.h"
22
23 namespace browser_sync {
24
25 using std::ostream;
26 using std::string;
27 using std::vector;
28
29 static const char kSyncServerSyncPath[] = "/command/";
30
31 // At the /time/ path of the sync server, we expect to find a very simple
32 // time of day service that we can use to synchronize the local clock with
33 // server time.
34 static const char kSyncServerGetTimePath[] = "/time";
35
36 static const ServerConnectionEvent shutdown_event =
37 { ServerConnectionEvent::SHUTDOWN, HttpResponse::CONNECTION_UNAVAILABLE,
38 false };
39
40 typedef PThreadScopedLock<PThreadMutex> MutexLock;
41
42 struct ServerConnectionManager::PlatformMembers {
43 explicit PlatformMembers(const string& user_agent) { }
44 void Kill() { }
45 void Reset() { }
46 void Reset(MutexLock*) { }
47 };
48
49 bool ServerConnectionManager::Post::ReadBufferResponse(
50 string* buffer_out, HttpResponse* response, bool require_response) {
51 if (RC_REQUEST_OK != response->response_code) {
52 response->server_status = HttpResponse::SYNC_SERVER_ERROR;
53 return false;
54 }
55
56 if (require_response && (1 > response->content_length))
57 return false;
58
59 const int64 bytes_read = ReadResponse(buffer_out, response->content_length);
60 if (bytes_read != response->content_length) {
61 response->server_status = HttpResponse::IO_ERROR;
62 return false;
63 }
64 return true;
65 }
66
67 bool ServerConnectionManager::Post::ReadDownloadResponse(
68 HttpResponse* response, string* buffer_out) {
69 const int64 bytes_read = ReadResponse(buffer_out, response->content_length);
70
71 if (bytes_read != response->content_length) {
72 LOG(ERROR) << "Mismatched content lengths, server claimed " <<
73 response->content_length << ", but sent " << bytes_read;
74 response->server_status = HttpResponse::IO_ERROR;
75 return false;
76 }
77 return true;
78 }
79
80 namespace {
81 string StripTrailingSlash(const string& s) {
82 int stripped_end_pos = s.size();
83 if (s.at(stripped_end_pos - 1) == '/') {
84 stripped_end_pos = stripped_end_pos - 1;
85 }
86
87 return s.substr(0, stripped_end_pos);
88 }
89 } // namespace
90
91 // TODO(chron): Use a GURL instead of string concatenation.
92 string ServerConnectionManager::Post::MakeConnectionURL(
idana 2009/09/10 05:44:37 Indentation
93 const string& sync_server, const string& path,
94 bool use_ssl) const {
95 string connection_url = (use_ssl ? "https://" : "http://");
96 connection_url += sync_server;
97 connection_url = StripTrailingSlash(connection_url);
98 connection_url += path;
99
100 return connection_url;
101 }
102
103 int ServerConnectionManager::Post::ReadResponse(string* out_buffer,
104 int length) {
105 int bytes_read = buffer_.length();
106 CHECK(length <= bytes_read);
107 out_buffer->assign(buffer_);
108 return bytes_read;
109 }
110
111 // A helper class that automatically notifies when the status changes:
112 struct WatchServerStatus {
113 WatchServerStatus(ServerConnectionManager* conn_mgr, HttpResponse* response)
114 : conn_mgr_(conn_mgr), response_(response),
115 reset_count_(conn_mgr->reset_count_),
116 server_reachable_(conn_mgr->server_reachable_) {
117 response->server_status = conn_mgr->server_status_;
118 }
119 ~WatchServerStatus() {
120 // Don't update the status of the connection if it has been reset.
121 // TODO(timsteele): Do we need this? Is this used by multiple threads?
122 if (reset_count_ != conn_mgr_->reset_count_)
123 return;
124 if (conn_mgr_->server_status_ != response_->server_status) {
125 conn_mgr_->server_status_ = response_->server_status;
126 conn_mgr_->NotifyStatusChanged();
127 return;
128 }
129 // Notify if we've gone on or offline.
130 if (server_reachable_ != conn_mgr_->server_reachable_)
131 conn_mgr_->NotifyStatusChanged();
132 }
133 ServerConnectionManager* const conn_mgr_;
134 HttpResponse* const response_;
135 // TODO(timsteele): Should this be Barrier:AtomicIncrement?
136 base::subtle::AtomicWord reset_count_;
137 bool server_reachable_;
138 };
139
140 ServerConnectionManager::ServerConnectionManager(
141 const string& server, int port, bool use_ssl, const string& user_agent,
142 const string& client_id)
143 : sync_server_(server), sync_server_port_(port),
144 channel_(new Channel(shutdown_event)),
145 server_status_(HttpResponse::NONE), server_reachable_(false),
146 client_id_(client_id), use_ssl_(use_ssl),
147 user_agent_(user_agent),
148 platform_(new PlatformMembers(user_agent)),
149 reset_count_(0), error_count_(0),
150 terminate_all_io_(false),
151 proto_sync_path_(kSyncServerSyncPath),
152 get_time_path_(kSyncServerGetTimePath) {
153 }
154
155 ServerConnectionManager::~ServerConnectionManager() {
156 delete channel_;
157 delete platform_;
158 shutdown_event_mutex_.Lock();
159 int result = pthread_cond_broadcast(&shutdown_event_condition_.condvar_);
160 shutdown_event_mutex_.Unlock();
161 if (result) {
162 LOG(ERROR) << "Error signaling shutdown_event_condition_ last error = "
163 << result;
164 }
165 }
166
167 void ServerConnectionManager::NotifyStatusChanged() {
168 ServerConnectionEvent event = { ServerConnectionEvent::STATUS_CHANGED,
169 server_status_,
170 server_reachable_ };
171 channel_->NotifyListeners(event);
172 }
173
174 // Uses currently set auth token. Set by AuthWatcher.
175 bool ServerConnectionManager::PostBufferWithCachedAuth(
176 const PostBufferParams* params) {
177 string path =
178 MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_));
179 return PostBufferToPath(params, path, auth_token_);
180 }
181
182 bool ServerConnectionManager::PostBufferWithAuth(const PostBufferParams* params,
183 const string& auth_token) {
184 string path = MakeSyncServerPath(proto_sync_path(),
185 MakeSyncQueryString(client_id_));
186
187 return PostBufferToPath(params, path, auth_token);
188 }
189
190 bool ServerConnectionManager::PostBufferToPath(const PostBufferParams* params,
191 const string& path,
192 const string& auth_token) {
193 WatchServerStatus watcher(this, params->response);
194 scoped_ptr<Post> post(MakePost());
195 post->set_timing_info(params->timing_info);
196 bool ok = post->Init(path.c_str(), auth_token, params->buffer_in,
197 params->response);
198
199 if (!ok || RC_REQUEST_OK != params->response->response_code) {
200 IncrementErrorCount();
201 return false;
202 }
203
204 if (post->ReadBufferResponse(params->buffer_out, params->response, true)) {
205 params->response->server_status = HttpResponse::SERVER_CONNECTION_OK;
206 server_reachable_ = true;
207 return true;
208 }
209 return false;
210 }
211
212 bool ServerConnectionManager::CheckTime(int32* out_time) {
213 // Verify that the server really is reachable by checking the time. We need
214 // to do this because of wifi interstitials that intercept messages from the
215 // client and return HTTP OK instead of a redirect.
216 HttpResponse response;
217 WatchServerStatus watcher(this, &response);
218 string post_body = "command=get_time";
219
220 // We only retry the CheckTime call if we were reset during the CheckTime
221 // attempt. We only try 3 times in case we're in a reset loop elsewhere.
222 base::subtle::AtomicWord start_reset_count = reset_count_ - 1;
223 for (int i = 0 ; i < 3 && start_reset_count != reset_count_ ; i++) {
224 start_reset_count = reset_count_;
225 scoped_ptr<Post> post(MakePost());
226
227 // Note that the server's get_time path doesn't require authentication.
228 string get_time_path =
229 MakeSyncServerPath(kSyncServerGetTimePath, post_body);
230 LOG(INFO) << "Requesting get_time from:" << get_time_path;
231
232 string blank_post_body;
233 bool ok = post->Init(get_time_path.c_str(), blank_post_body,
234 blank_post_body, &response);
235 if (!ok) {
236 LOG(INFO) << "Unable to check the time";
237 continue;
238 }
239 string time_response;
240 time_response.resize(response.content_length);
241 ok = post->ReadDownloadResponse(&response, &time_response);
242 if (!ok || string::npos !=
243 time_response.find_first_not_of("0123456789")) {
244 LOG(ERROR) << "unable to read a non-numeric response from get_time:"
245 << time_response;
246 continue;
247 }
248 *out_time = atoi(time_response.c_str());
249 LOG(INFO) << "Server was reachable.";
250 return true;
251 }
252 IncrementErrorCount();
253 return false;
254 }
255
256 bool ServerConnectionManager::IsServerReachable() {
257 int32 time;
258 return CheckTime(&time);
259 }
260
261 bool ServerConnectionManager::IsUserAuthenticated() {
262 return IsGoodReplyFromServer(server_status_);
263 }
264
265 bool ServerConnectionManager::CheckServerReachable() {
266 const bool server_is_reachable = IsServerReachable();
267 if (server_reachable_ != server_is_reachable) {
268 server_reachable_ = server_is_reachable;
269 NotifyStatusChanged();
270 }
271 return server_is_reachable;
272 }
273
274 void ServerConnectionManager::kill() {
275 {
276 MutexLock lock(&terminate_all_io_mutex_);
277 terminate_all_io_ = true;
278 }
279 platform_->Kill();
280 shutdown_event_mutex_.Lock();
281 int result = pthread_cond_broadcast(&shutdown_event_condition_.condvar_);
282 shutdown_event_mutex_.Unlock();
283 if (result) {
284 LOG(ERROR) << "Error signaling shutdown_event_condition_ last error = "
285 << result;
286 }
287 }
288
289 void ServerConnectionManager::ResetAuthStatus() {
290 ResetConnection();
291 server_status_ = HttpResponse::NONE;
292 NotifyStatusChanged();
293 }
294
295 void ServerConnectionManager::ResetConnection() {
296 base::subtle::NoBarrier_AtomicIncrement(&reset_count_, 1);
297 platform_->Reset();
298 }
299
300 bool ServerConnectionManager::IncrementErrorCount() {
301 #ifdef OS_WINDOWS
302 error_count_mutex_.Lock();
303 error_count_++;
304
305 if (error_count_ > kMaxConnectionErrorsBeforeReset) {
306 error_count_ = 0;
307
308 // Be careful with this mutex because calling out to other methods can
309 // result in being called back. Unlock it here to prevent any potential
310 // double-acquisitions.
311 error_count_mutex_.Unlock();
312
313 if (!IsServerReachable()) {
314 LOG(WARNING) << "Too many connection failures, server is not reachable. "
315 << "Resetting connections.";
316 ResetConnection();
317 } else {
318 LOG(WARNING) << "Multiple connection failures while server is reachable.";
319 }
320 return false;
321 }
322
323 error_count_mutex_.Unlock();
324 return true;
325 #endif
326 return true;
327 }
328
329 void ServerConnectionManager::SetServerParameters(const string& server_url,
330 int port, bool use_ssl) {
331 {
332 ParametersLock lock(&server_parameters_mutex_);
333 sync_server_ = server_url;
334 sync_server_port_ = port;
335 use_ssl_ = use_ssl;
336 }
337 platform_->Reset();
338 }
339
340 // Returns the current server parameters in server_url and port.
341 void ServerConnectionManager::GetServerParameters(string* server_url,
342 int* port, bool* use_ssl) {
343 ParametersLock lock(&server_parameters_mutex_);
344 if (server_url != NULL)
345 *server_url = sync_server_;
346 if (port != NULL)
347 *port = sync_server_port_;
348 if (use_ssl != NULL)
349 *use_ssl = use_ssl_;
350 }
351
352 bool FillMessageWithShareDetails(sync_pb::ClientToServerMessage* csm,
353 syncable::DirectoryManager* manager,
354 const PathString &share) {
355 syncable::ScopedDirLookup dir(manager, share);
356 if (!dir.good()) {
357 LOG(INFO) << "Dir lookup failed";
358 return false;
359 }
360 string birthday = dir->store_birthday();
361 if (!birthday.empty())
362 csm->set_store_birthday(birthday);
363 csm->set_share(ToUTF8(share).get_string());
364 return true;
365 }
366
367 } // namespace browser_sync
368
369 std::ostream& operator << (std::ostream& s,
370 const struct browser_sync::HttpResponse& hr) {
371 s << " Response Code (bogus on error): " << hr.response_code;
372 s << " Content-Length (bogus on error): " << hr.content_length;
373 s << " Server Status: " << hr.server_status;
374 return s;
375 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698