1 // Copyright (c) 2012 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/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/metrics/histogram.h" 14 #include "build/build_config.h" 15 #include "net/base/net_errors.h" 16 #include "net/http/http_status_code.h" 17 #include "sync/engine/net/url_translator.h" 18 #include "sync/engine/syncer.h" 19 #include "sync/internal_api/public/base/cancelation_signal.h" 20 #include "sync/protocol/sync.pb.h" 21 #include "sync/syncable/directory.h" 22 #include "url/gurl.h" 23 24 namespace syncer { 25 26 using std::ostream; 27 using std::string; 28 using std::vector; 29 30 static const char kSyncServerSyncPath[] = "/command/"; 31 32 HttpResponse::HttpResponse() 33 : response_code(kUnsetResponseCode), 34 content_length(kUnsetContentLength), 35 payload_length(kUnsetPayloadLength), 36 server_status(NONE) {} 37 38 #define ENUM_CASE(x) case x: return #x; break 39 40 const char* HttpResponse::GetServerConnectionCodeString( 41 ServerConnectionCode code) { 42 switch (code) { 43 ENUM_CASE(NONE); 44 ENUM_CASE(CONNECTION_UNAVAILABLE); 45 ENUM_CASE(IO_ERROR); 46 ENUM_CASE(SYNC_SERVER_ERROR); 47 ENUM_CASE(SYNC_AUTH_ERROR); 48 ENUM_CASE(SERVER_CONNECTION_OK); 49 ENUM_CASE(RETRY); 50 } 51 NOTREACHED(); 52 return ""; 53 } 54 55 #undef ENUM_CASE 56 57 // TODO(clamy): check if all errors are in the right category. 58 HttpResponse::ServerConnectionCode 59 HttpResponse::ServerConnectionCodeFromNetError(int error_code) { 60 switch (error_code) { 61 case net::ERR_ABORTED: 62 case net::ERR_SOCKET_NOT_CONNECTED: 63 case net::ERR_NETWORK_CHANGED: 64 case net::ERR_CONNECTION_FAILED: 65 case net::ERR_NAME_NOT_RESOLVED: 66 case net::ERR_INTERNET_DISCONNECTED: 67 case net::ERR_NETWORK_ACCESS_DENIED: 68 case net::ERR_NETWORK_IO_SUSPENDED: 69 return CONNECTION_UNAVAILABLE; 70 } 71 return IO_ERROR; 72 } 73 74 ServerConnectionManager::Connection::Connection( 75 ServerConnectionManager* scm) : scm_(scm) { 76 } 77 78 ServerConnectionManager::Connection::~Connection() { 79 } 80 81 bool ServerConnectionManager::Connection::ReadBufferResponse( 82 string* buffer_out, 83 HttpResponse* response, 84 bool require_response) { 85 if (net::HTTP_OK != response->response_code) { 86 response->server_status = HttpResponse::SYNC_SERVER_ERROR; 87 return false; 88 } 89 90 if (require_response && (1 > response->content_length)) 91 return false; 92 93 const int64 bytes_read = ReadResponse(buffer_out, 94 static_cast<int>(response->content_length)); 95 if (bytes_read != response->content_length) { 96 response->server_status = HttpResponse::IO_ERROR; 97 return false; 98 } 99 return true; 100 } 101 102 bool ServerConnectionManager::Connection::ReadDownloadResponse( 103 HttpResponse* response, 104 string* buffer_out) { 105 const int64 bytes_read = ReadResponse(buffer_out, 106 static_cast<int>(response->content_length)); 107 108 if (bytes_read != response->content_length) { 109 LOG(ERROR) << "Mismatched content lengths, server claimed " << 110 response->content_length << ", but sent " << bytes_read; 111 response->server_status = HttpResponse::IO_ERROR; 112 return false; 113 } 114 return true; 115 } 116 117 ServerConnectionManager::ScopedConnectionHelper::ScopedConnectionHelper( 118 ServerConnectionManager* manager, Connection* connection) 119 : manager_(manager), connection_(connection) {} 120 121 ServerConnectionManager::ScopedConnectionHelper::~ScopedConnectionHelper() { 122 if (connection_) 123 manager_->OnConnectionDestroyed(connection_.get()); 124 connection_.reset(); 125 } 126 127 ServerConnectionManager::Connection* 128 ServerConnectionManager::ScopedConnectionHelper::get() { 129 return connection_.get(); 130 } 131 132 namespace { 133 134 string StripTrailingSlash(const string& s) { 135 int stripped_end_pos = s.size(); 136 if (s.at(stripped_end_pos - 1) == '/') { 137 stripped_end_pos = stripped_end_pos - 1; 138 } 139 140 return s.substr(0, stripped_end_pos); 141 } 142 143 } // namespace 144 145 // TODO(chron): Use a GURL instead of string concatenation. 146 string ServerConnectionManager::Connection::MakeConnectionURL( 147 const string& sync_server, 148 const string& path, 149 bool use_ssl) const { 150 string connection_url = (use_ssl ? "https://" : "http://"); 151 connection_url += sync_server; 152 connection_url = StripTrailingSlash(connection_url); 153 connection_url += path; 154 155 return connection_url; 156 } 157 158 int ServerConnectionManager::Connection::ReadResponse(string* out_buffer, 159 int length) { 160 int bytes_read = buffer_.length(); 161 CHECK(length <= bytes_read); 162 out_buffer->assign(buffer_); 163 return bytes_read; 164 } 165 166 ScopedServerStatusWatcher::ScopedServerStatusWatcher( 167 ServerConnectionManager* conn_mgr, HttpResponse* response) 168 : conn_mgr_(conn_mgr), 169 response_(response) { 170 response->server_status = conn_mgr->server_status_; 171 } 172 173 ScopedServerStatusWatcher::~ScopedServerStatusWatcher() { 174 conn_mgr_->SetServerStatus(response_->server_status); 175 } 176 177 ServerConnectionManager::ServerConnectionManager( 178 const string& server, 179 int port, 180 bool use_ssl, 181 CancelationSignal* cancelation_signal) 182 : sync_server_(server), 183 sync_server_port_(port), 184 use_ssl_(use_ssl), 185 proto_sync_path_(kSyncServerSyncPath), 186 server_status_(HttpResponse::NONE), 187 terminated_(false), 188 active_connection_(NULL), 189 cancelation_signal_(cancelation_signal), 190 signal_handler_registered_(false) { 191 signal_handler_registered_ = cancelation_signal_->TryRegisterHandler(this); 192 if (!signal_handler_registered_) { 193 // Calling a virtual function from a constructor. We can get away with it 194 // here because ServerConnectionManager::OnSignalReceived() is the function 195 // we want to call. 196 OnSignalReceived(); 197 } 198 } 199 200 ServerConnectionManager::~ServerConnectionManager() { 201 if (signal_handler_registered_) { 202 cancelation_signal_->UnregisterHandler(this); 203 } 204 } 205 206 ServerConnectionManager::Connection* 207 ServerConnectionManager::MakeActiveConnection() { 208 base::AutoLock lock(terminate_connection_lock_); 209 DCHECK(!active_connection_); 210 if (terminated_) 211 return NULL; 212 213 active_connection_ = MakeConnection(); 214 return active_connection_; 215 } 216 217 void ServerConnectionManager::OnConnectionDestroyed(Connection* connection) { 218 DCHECK(connection); 219 base::AutoLock lock(terminate_connection_lock_); 220 // |active_connection_| can be NULL already if it was aborted. Also, 221 // it can legitimately be a different Connection object if a new Connection 222 // was created after a previous one was Aborted and destroyed. 223 if (active_connection_ != connection) 224 return; 225 226 active_connection_ = NULL; 227 } 228 229 bool ServerConnectionManager::SetAuthToken(const std::string& auth_token) { 230 DCHECK(thread_checker_.CalledOnValidThread()); 231 if (previously_invalidated_token != auth_token) { 232 auth_token_.assign(auth_token); 233 previously_invalidated_token = std::string(); 234 return true; 235 } 236 237 // This could happen in case like server outage/bug. E.g. token returned by 238 // first request is considered invalid by sync server and because 239 // of token server's caching policy, etc, same token is returned on second 240 // request. Need to notify sync frontend again to request new token, 241 // otherwise backend will stay in SYNC_AUTH_ERROR state while frontend thinks 242 // everything is fine and takes no actions. 243 SetServerStatus(HttpResponse::SYNC_AUTH_ERROR); 244 return false; 245 } 246 247 void ServerConnectionManager::OnInvalidationCredentialsRejected() { 248 InvalidateAndClearAuthToken(); 249 SetServerStatus(HttpResponse::SYNC_AUTH_ERROR); 250 } 251 252 void ServerConnectionManager::InvalidateAndClearAuthToken() { 253 DCHECK(thread_checker_.CalledOnValidThread()); 254 // Copy over the token to previous invalid token. 255 if (!auth_token_.empty()) { 256 previously_invalidated_token.assign(auth_token_); 257 auth_token_ = std::string(); 258 } 259 } 260 261 void ServerConnectionManager::SetServerStatus( 262 HttpResponse::ServerConnectionCode server_status) { 263 // SYNC_AUTH_ERROR is permanent error. Need to notify observer to take 264 // action externally to resolve. 265 if (server_status != HttpResponse::SYNC_AUTH_ERROR && 266 server_status_ == server_status) { 267 return; 268 } 269 server_status_ = server_status; 270 NotifyStatusChanged(); 271 } 272 273 void ServerConnectionManager::NotifyStatusChanged() { 274 DCHECK(thread_checker_.CalledOnValidThread()); 275 FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_, 276 OnServerConnectionEvent( 277 ServerConnectionEvent(server_status_))); 278 } 279 280 bool ServerConnectionManager::PostBufferWithCachedAuth( 281 PostBufferParams* params, ScopedServerStatusWatcher* watcher) { 282 DCHECK(thread_checker_.CalledOnValidThread()); 283 string path = 284 MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_)); 285 return PostBufferToPath(params, path, auth_token(), watcher); 286 } 287 288 bool ServerConnectionManager::PostBufferToPath(PostBufferParams* params, 289 const string& path, const string& auth_token, 290 ScopedServerStatusWatcher* watcher) { 291 DCHECK(thread_checker_.CalledOnValidThread()); 292 DCHECK(watcher != NULL); 293 294 // TODO(pavely): crbug.com/273096. Check for "credentials_lost" is added as 295 // workaround for M29 blocker to avoid sending RPC to sync with known invalid 296 // token but instead to trigger refreshing token in ProfileSyncService. Need 297 // to clean it. 298 if (auth_token.empty() || auth_token == "credentials_lost") { 299 params->response.server_status = HttpResponse::SYNC_AUTH_ERROR; 300 // Print a log to distinguish this "known failure" from others. 301 LOG(WARNING) << "ServerConnectionManager forcing SYNC_AUTH_ERROR"; 302 return false; 303 } 304 305 // When our connection object falls out of scope, it clears itself from 306 // active_connection_. 307 ScopedConnectionHelper post(this, MakeActiveConnection()); 308 if (!post.get()) { 309 params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE; 310 return false; 311 } 312 313 // Note that |post| may be aborted by now, which will just cause Init to fail 314 // with CONNECTION_UNAVAILABLE. 315 bool ok = post.get()->Init( 316 path.c_str(), auth_token, params->buffer_in, ¶ms->response); 317 318 if (params->response.server_status == HttpResponse::SYNC_AUTH_ERROR) { 319 InvalidateAndClearAuthToken(); 320 } 321 322 if (!ok || net::HTTP_OK != params->response.response_code) 323 return false; 324 325 if (post.get()->ReadBufferResponse( 326 ¶ms->buffer_out, ¶ms->response, true)) { 327 params->response.server_status = HttpResponse::SERVER_CONNECTION_OK; 328 return true; 329 } 330 return false; 331 } 332 333 // Returns the current server parameters in server_url and port. 334 void ServerConnectionManager::GetServerParameters(string* server_url, 335 int* port, 336 bool* use_ssl) const { 337 if (server_url != NULL) 338 *server_url = sync_server_; 339 if (port != NULL) 340 *port = sync_server_port_; 341 if (use_ssl != NULL) 342 *use_ssl = use_ssl_; 343 } 344 345 std::string ServerConnectionManager::GetServerHost() const { 346 string server_url; 347 int port; 348 bool use_ssl; 349 GetServerParameters(&server_url, &port, &use_ssl); 350 // For unit tests. 351 if (server_url.empty()) 352 return std::string(); 353 // We just want the hostname, so we don't need to switch on use_ssl. 354 server_url = "http://" + server_url; 355 GURL gurl(server_url); 356 DCHECK(gurl.is_valid()) << gurl; 357 return gurl.host(); 358 } 359 360 void ServerConnectionManager::AddListener( 361 ServerConnectionEventListener* listener) { 362 DCHECK(thread_checker_.CalledOnValidThread()); 363 listeners_.AddObserver(listener); 364 } 365 366 void ServerConnectionManager::RemoveListener( 367 ServerConnectionEventListener* listener) { 368 DCHECK(thread_checker_.CalledOnValidThread()); 369 listeners_.RemoveObserver(listener); 370 } 371 372 ServerConnectionManager::Connection* ServerConnectionManager::MakeConnection() 373 { 374 return NULL; // For testing. 375 } 376 377 void ServerConnectionManager::OnSignalReceived() { 378 base::AutoLock lock(terminate_connection_lock_); 379 terminated_ = true; 380 if (active_connection_) 381 active_connection_->Abort(); 382 383 // Sever our ties to this connection object. Note that it still may exist, 384 // since we don't own it, but it has been neutered. 385 active_connection_ = NULL; 386 } 387 388 std::ostream& operator << (std::ostream& s, const struct HttpResponse& hr) { 389 s << " Response Code (bogus on error): " << hr.response_code; 390 s << " Content-Length (bogus on error): " << hr.content_length; 391 s << " Server Status: " 392 << HttpResponse::GetServerConnectionCodeString(hr.server_status); 393 return s; 394 } 395 396 } // namespace syncer 397