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