1 // Copyright 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 #ifndef SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_ 6 #define SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_ 7 8 #include <iosfwd> 9 #include <string> 10 11 #include "base/atomicops.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/observer_list.h" 14 #include "base/strings/string_util.h" 15 #include "base/synchronization/lock.h" 16 #include "base/threading/non_thread_safe.h" 17 #include "base/threading/thread_checker.h" 18 #include "sync/base/sync_export.h" 19 #include "sync/syncable/syncable_id.h" 20 21 namespace sync_pb { 22 class ClientToServerMessage; 23 } 24 25 namespace syncer { 26 27 namespace syncable { 28 class Directory; 29 } 30 31 static const int32 kUnsetResponseCode = -1; 32 static const int32 kUnsetContentLength = -1; 33 static const int32 kUnsetPayloadLength = -1; 34 35 // HttpResponse gathers the relevant output properties of an HTTP request. 36 // Depending on the value of the server_status code, response_code, and 37 // content_length may not be valid. 38 struct SYNC_EXPORT_PRIVATE HttpResponse { 39 enum ServerConnectionCode { 40 // For uninitialized state. 41 NONE, 42 43 // CONNECTION_UNAVAILABLE is returned when InternetConnect() fails. 44 CONNECTION_UNAVAILABLE, 45 46 // IO_ERROR is returned when reading/writing to a buffer has failed. 47 IO_ERROR, 48 49 // SYNC_SERVER_ERROR is returned when the HTTP status code indicates that 50 // a non-auth error has occured. 51 SYNC_SERVER_ERROR, 52 53 // SYNC_AUTH_ERROR is returned when the HTTP status code indicates that an 54 // auth error has occured (i.e. a 401 or sync-specific AUTH_INVALID 55 // response) 56 // TODO(tim): Caring about AUTH_INVALID is a layering violation. But 57 // this app-specific logic is being added as a stable branch hotfix so 58 // minimal changes prevail for the moment. Fix this! Bug 35060. 59 SYNC_AUTH_ERROR, 60 61 // SERVER_CONNECTION_OK is returned when request was handled correctly. 62 SERVER_CONNECTION_OK, 63 64 // RETRY is returned when a Commit request fails with a RETRY response from 65 // the server. 66 // 67 // TODO(idana): the server no longer returns RETRY so we should remove this 68 // value. 69 RETRY, 70 }; 71 72 // The HTTP Status code. 73 int64 response_code; 74 75 // The value of the Content-length header. 76 int64 content_length; 77 78 // The size of a download request's payload. 79 int64 payload_length; 80 81 // Value of the Update-Client-Auth header. 82 std::string update_client_auth_header; 83 84 // Identifies the type of failure, if any. 85 ServerConnectionCode server_status; 86 87 HttpResponse(); 88 89 static const char* GetServerConnectionCodeString( 90 ServerConnectionCode code); 91 92 static ServerConnectionCode ServerConnectionCodeFromNetError( 93 int error_code); 94 }; 95 96 struct ServerConnectionEvent { 97 HttpResponse::ServerConnectionCode connection_code; 98 explicit ServerConnectionEvent(HttpResponse::ServerConnectionCode code) : 99 connection_code(code) {} 100 }; 101 102 class SYNC_EXPORT_PRIVATE ServerConnectionEventListener { 103 public: 104 virtual void OnServerConnectionEvent(const ServerConnectionEvent& event) = 0; 105 protected: 106 virtual ~ServerConnectionEventListener() {} 107 }; 108 109 class ServerConnectionManager; 110 // A helper class that automatically notifies when the status changes. 111 // TODO(tim): This class shouldn't be exposed outside of the implementation, 112 // bug 35060. 113 class SYNC_EXPORT_PRIVATE ScopedServerStatusWatcher 114 : public base::NonThreadSafe { 115 public: 116 ScopedServerStatusWatcher(ServerConnectionManager* conn_mgr, 117 HttpResponse* response); 118 virtual ~ScopedServerStatusWatcher(); 119 private: 120 ServerConnectionManager* const conn_mgr_; 121 HttpResponse* const response_; 122 DISALLOW_COPY_AND_ASSIGN(ScopedServerStatusWatcher); 123 }; 124 125 // Use this class to interact with the sync server. 126 // The ServerConnectionManager currently supports POSTing protocol buffers. 127 // 128 class SYNC_EXPORT_PRIVATE ServerConnectionManager { 129 public: 130 // buffer_in - will be POSTed 131 // buffer_out - string will be overwritten with response 132 struct PostBufferParams { 133 std::string buffer_in; 134 std::string buffer_out; 135 HttpResponse response; 136 }; 137 138 // Abstract class providing network-layer functionality to the 139 // ServerConnectionManager. Subclasses implement this using an HTTP stack of 140 // their choice. 141 class Connection { 142 public: 143 explicit Connection(ServerConnectionManager* scm); 144 virtual ~Connection(); 145 146 // Called to initialize and perform an HTTP POST. 147 virtual bool Init(const char* path, 148 const std::string& auth_token, 149 const std::string& payload, 150 HttpResponse* response) = 0; 151 152 // Immediately abandons a pending HTTP POST request and unblocks caller 153 // in Init. 154 virtual void Abort() = 0; 155 156 bool ReadBufferResponse(std::string* buffer_out, HttpResponse* response, 157 bool require_response); 158 bool ReadDownloadResponse(HttpResponse* response, std::string* buffer_out); 159 160 protected: 161 std::string MakeConnectionURL(const std::string& sync_server, 162 const std::string& path, 163 bool use_ssl) const; 164 165 void GetServerParams(std::string* server, 166 int* server_port, 167 bool* use_ssl, 168 bool* use_oauth2_token) const { 169 server->assign(scm_->sync_server_); 170 *server_port = scm_->sync_server_port_; 171 *use_ssl = scm_->use_ssl_; 172 *use_oauth2_token = scm_->use_oauth2_token_; 173 } 174 175 std::string buffer_; 176 ServerConnectionManager* scm_; 177 178 private: 179 int ReadResponse(void* buffer, int length); 180 int ReadResponse(std::string* buffer, int length); 181 }; 182 183 ServerConnectionManager(const std::string& server, 184 int port, 185 bool use_ssl, 186 bool use_oauth2_token); 187 188 virtual ~ServerConnectionManager(); 189 190 // POSTS buffer_in and reads a response into buffer_out. Uses our currently 191 // set auth token in our headers. 192 // 193 // Returns true if executed successfully. 194 virtual bool PostBufferWithCachedAuth(PostBufferParams* params, 195 ScopedServerStatusWatcher* watcher); 196 197 void AddListener(ServerConnectionEventListener* listener); 198 void RemoveListener(ServerConnectionEventListener* listener); 199 200 inline HttpResponse::ServerConnectionCode server_status() const { 201 DCHECK(thread_checker_.CalledOnValidThread()); 202 return server_status_; 203 } 204 205 const std::string client_id() const { return client_id_; } 206 207 // Returns the current server parameters in server_url, port and use_ssl. 208 void GetServerParameters(std::string* server_url, 209 int* port, 210 bool* use_ssl) const; 211 212 std::string GetServerHost() const; 213 214 // Factory method to create an Connection object we can use for 215 // communication with the server. 216 virtual Connection* MakeConnection(); 217 218 // Aborts any active HTTP POST request. 219 // We expect this to get called on a different thread than the valid 220 // ThreadChecker thread, as we want to kill any pending http traffic without 221 // having to wait for the request to complete. 222 void TerminateAllIO(); 223 224 void set_client_id(const std::string& client_id) { 225 DCHECK(thread_checker_.CalledOnValidThread()); 226 DCHECK(client_id_.empty()); 227 client_id_.assign(client_id); 228 } 229 230 // Sets a new auth token and time. 231 bool SetAuthToken(const std::string& auth_token); 232 233 // Our out-of-band invalidations channel can encounter auth errors, 234 // and when it does so it tells us via this method to prevent making more 235 // requests with known-bad tokens. This will put the 236 // ServerConnectionManager in an auth error state as if it received an 237 // HTTP 401 from sync servers. 238 void OnInvalidationCredentialsRejected(); 239 240 bool HasInvalidAuthToken() { 241 return auth_token_.empty(); 242 } 243 244 const std::string auth_token() const { 245 DCHECK(thread_checker_.CalledOnValidThread()); 246 return auth_token_; 247 } 248 249 protected: 250 inline std::string proto_sync_path() const { 251 return proto_sync_path_; 252 } 253 254 // Updates server_status_ and notifies listeners if server_status_ changed 255 void SetServerStatus(HttpResponse::ServerConnectionCode server_status); 256 257 // NOTE: Tests rely on this protected function being virtual. 258 // 259 // Internal PostBuffer base function. 260 virtual bool PostBufferToPath(PostBufferParams*, 261 const std::string& path, 262 const std::string& auth_token, 263 ScopedServerStatusWatcher* watcher); 264 265 // An internal helper to clear our auth_token_ and cache the old version 266 // in |previously_invalidated_token_| to shelter us from retrying with a 267 // known bad token. 268 void InvalidateAndClearAuthToken(); 269 270 // Helper to check terminated flags and build a Connection object, installing 271 // it as the |active_connection_|. If this ServerConnectionManager has been 272 // terminated, this will return NULL. 273 Connection* MakeActiveConnection(); 274 275 // Called by Connection objects as they are destroyed to allow the 276 // ServerConnectionManager to cleanup active connections. 277 void OnConnectionDestroyed(Connection* connection); 278 279 // The sync_server_ is the server that requests will be made to. 280 std::string sync_server_; 281 282 // The sync_server_port_ is the port that HTTP requests will be made on. 283 int sync_server_port_; 284 285 // The unique id of the user's client. 286 std::string client_id_; 287 288 // Indicates whether or not requests should be made using HTTPS. 289 bool use_ssl_; 290 291 // Indicates if token should be handled as OAuth2 token. Connection should set 292 // auth header appropriately. 293 // TODO(pavely): Remove once sync on android switches to oauth2 tokens. 294 bool use_oauth2_token_; 295 296 // The paths we post to. 297 std::string proto_sync_path_; 298 299 // The auth token to use in authenticated requests. 300 std::string auth_token_; 301 302 // The previous auth token that is invalid now. 303 std::string previously_invalidated_token; 304 305 ObserverList<ServerConnectionEventListener> listeners_; 306 307 HttpResponse::ServerConnectionCode server_status_; 308 309 base::ThreadChecker thread_checker_; 310 311 // Protects all variables below to allow bailing out of active connections. 312 base::Lock terminate_connection_lock_; 313 314 // If true, we've been told to terminate IO and expect to be destroyed 315 // shortly. No future network requests will be made. 316 bool terminated_; 317 318 // A non-owning pointer to any active http connection, so that we can abort 319 // it if necessary. 320 Connection* active_connection_; 321 322 private: 323 friend class Connection; 324 friend class ScopedServerStatusWatcher; 325 326 // A class to help deal with cleaning up active Connection objects when (for 327 // ex) multiple early-exits are present in some scope. ScopedConnectionHelper 328 // informs the ServerConnectionManager before the Connection object it takes 329 // ownership of is destroyed. 330 class ScopedConnectionHelper { 331 public: 332 // |manager| must outlive this. Takes ownership of |connection|. 333 ScopedConnectionHelper(ServerConnectionManager* manager, 334 Connection* connection); 335 ~ScopedConnectionHelper(); 336 Connection* get(); 337 private: 338 ServerConnectionManager* manager_; 339 scoped_ptr<Connection> connection_; 340 DISALLOW_COPY_AND_ASSIGN(ScopedConnectionHelper); 341 }; 342 343 void NotifyStatusChanged(); 344 345 DISALLOW_COPY_AND_ASSIGN(ServerConnectionManager); 346 }; 347 348 std::ostream& operator<<(std::ostream& s, const struct HttpResponse& hr); 349 350 } // namespace syncer 351 352 #endif // SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_ 353