// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_ #define CHROME_BROWSER_SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_ #pragma once #include <iosfwd> #include <string> #include "base/atomicops.h" #include "base/observer_list_threadsafe.h" #include "base/string_util.h" #include "base/synchronization/lock.h" #include "chrome/browser/sync/syncable/syncable_id.h" #include "chrome/common/deprecated/event_sys.h" #include "chrome/common/deprecated/event_sys-inl.h" #include "chrome/common/net/http_return.h" namespace syncable { class WriteTransaction; class DirectoryManager; } namespace sync_pb { class ClientToServerMessage; } struct RequestTimingInfo; namespace browser_sync { class ClientToServerMessage; // How many connection errors are accepted before network handles are closed // and reopened. static const int32 kMaxConnectionErrorsBeforeReset = 10; static const int32 kUnsetResponseCode = -1; static const int32 kUnsetContentLength = -1; static const int32 kUnsetPayloadLength = -1; // HttpResponse gathers the relevant output properties of an HTTP request. // Depending on the value of the server_status code, response_code, and // content_length may not be valid. struct HttpResponse { enum ServerConnectionCode { // For uninitialized state. NONE, // CONNECTION_UNAVAILABLE is returned when InternetConnect() fails. CONNECTION_UNAVAILABLE, // IO_ERROR is returned when reading/writing to a buffer has failed. IO_ERROR, // SYNC_SERVER_ERROR is returned when the HTTP status code indicates that // a non-auth error has occured. SYNC_SERVER_ERROR, // SYNC_AUTH_ERROR is returned when the HTTP status code indicates that an // auth error has occured (i.e. a 401 or sync-specific AUTH_INVALID // response) // TODO(tim): Caring about AUTH_INVALID is a layering violation. But // this app-specific logic is being added as a stable branch hotfix so // minimal changes prevail for the moment. Fix this! Bug 35060. SYNC_AUTH_ERROR, // All the following connection codes are valid responses from the server. // Means the server is up. If you update this list, be sure to also update // IsGoodReplyFromServer(). // SERVER_CONNECTION_OK is returned when request was handled correctly. SERVER_CONNECTION_OK, // RETRY is returned when a Commit request fails with a RETRY response from // the server. // // TODO(idana): the server no longer returns RETRY so we should remove this // value. RETRY, }; // The HTTP Status code. int64 response_code; // The value of the Content-length header. int64 content_length; // The size of a download request's payload. int64 payload_length; // Value of the Update-Client-Auth header. std::string update_client_auth_header; // Identifies the type of failure, if any. ServerConnectionCode server_status; HttpResponse() : response_code(kUnsetResponseCode), content_length(kUnsetContentLength), payload_length(kUnsetPayloadLength), server_status(NONE) {} }; inline bool IsGoodReplyFromServer(HttpResponse::ServerConnectionCode code) { return code >= HttpResponse::SERVER_CONNECTION_OK; } // TODO(tim): Deprecated. struct ServerConnectionEvent { // Traits. typedef ServerConnectionEvent EventType; enum WhatHappened { SHUTDOWN, STATUS_CHANGED }; static inline bool IsChannelShutdownEvent(const EventType& event) { return SHUTDOWN == event.what_happened; } WhatHappened what_happened; HttpResponse::ServerConnectionCode connection_code; bool server_reachable; }; struct ServerConnectionEvent2 { HttpResponse::ServerConnectionCode connection_code; bool server_reachable; ServerConnectionEvent2(HttpResponse::ServerConnectionCode code, bool server_reachable) : connection_code(code), server_reachable(server_reachable) {} }; class ServerConnectionEventListener { public: virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event) = 0; protected: virtual ~ServerConnectionEventListener() {} }; class ServerConnectionManager; // A helper class that automatically notifies when the status changes. // TODO(tim): This class shouldn't be exposed outside of the implementation, // bug 35060. class ScopedServerStatusWatcher { public: ScopedServerStatusWatcher(ServerConnectionManager* conn_mgr, HttpResponse* response); ~ScopedServerStatusWatcher(); private: ServerConnectionManager* const conn_mgr_; HttpResponse* const response_; // TODO(tim): Should this be Barrier:AtomicIncrement? base::subtle::AtomicWord reset_count_; bool server_reachable_; DISALLOW_COPY_AND_ASSIGN(ScopedServerStatusWatcher); }; // Use this class to interact with the sync server. // The ServerConnectionManager currently supports POSTing protocol buffers. // // *** This class is thread safe. In fact, you should consider creating only // one instance for every server that you need to talk to. class ServerConnectionManager { public: typedef EventChannel<ServerConnectionEvent, base::Lock> Channel; // buffer_in - will be POSTed // buffer_out - string will be overwritten with response struct PostBufferParams { const std::string& buffer_in; std::string* buffer_out; HttpResponse* response; RequestTimingInfo* timing_info; }; // Abstract class providing network-layer functionality to the // ServerConnectionManager. Subclasses implement this using an HTTP stack of // their choice. class Post { public: explicit Post(ServerConnectionManager* scm) : scm_(scm), timing_info_(0) { } virtual ~Post() { } // Called to initialize and perform an HTTP POST. virtual bool Init(const char* path, const std::string& auth_token, const std::string& payload, HttpResponse* response) = 0; bool ReadBufferResponse(std::string* buffer_out, HttpResponse* response, bool require_response); bool ReadDownloadResponse(HttpResponse* response, std::string* buffer_out); void set_timing_info(RequestTimingInfo* timing_info) { timing_info_ = timing_info; } RequestTimingInfo* timing_info() { return timing_info_; } protected: std::string MakeConnectionURL(const std::string& sync_server, const std::string& path, bool use_ssl) const; void GetServerParams(std::string* server, int* server_port, bool* use_ssl) const { base::AutoLock lock(scm_->server_parameters_mutex_); server->assign(scm_->sync_server_); *server_port = scm_->sync_server_port_; *use_ssl = scm_->use_ssl_; } std::string buffer_; ServerConnectionManager* scm_; private: int ReadResponse(void* buffer, int length); int ReadResponse(std::string* buffer, int length); RequestTimingInfo* timing_info_; }; ServerConnectionManager(const std::string& server, int port, bool use_ssl, const std::string& user_agent); virtual ~ServerConnectionManager(); // POSTS buffer_in and reads a response into buffer_out. Uses our currently // set auth token in our headers. // // Returns true if executed successfully. virtual bool PostBufferWithCachedAuth(const PostBufferParams* params, ScopedServerStatusWatcher* watcher); // Checks the time on the server. Returns false if the request failed. |time| // is an out parameter that stores the value returned from the server. virtual bool CheckTime(int32* out_time); // Returns true if sync_server_ is reachable. This method verifies that the // server is pingable and that traffic can be sent to and from it. virtual bool IsServerReachable(); // Returns true if user has been successfully authenticated. virtual bool IsUserAuthenticated(); // Updates status and broadcasts events on change. bool CheckServerReachable(); // Signal the shutdown event to notify listeners. virtual void kill(); inline Channel* channel() const { return channel_; } void AddListener(ServerConnectionEventListener* listener); void RemoveListener(ServerConnectionEventListener* listener); inline std::string user_agent() const { return user_agent_; } inline HttpResponse::ServerConnectionCode server_status() const { return server_status_; } inline bool server_reachable() const { return server_reachable_; } const std::string client_id() const { return client_id_; } // This changes the server info used by the connection manager. This allows // a single client instance to talk to different backing servers. This is // typically called during / after authentication so that the server url // can be a function of the user's login id. A side effect of this call is // that ResetConnection is called. void SetServerParameters(const std::string& server_url, int port, bool use_ssl); // Returns the current server parameters in server_url, port and use_ssl. void GetServerParameters(std::string* server_url, int* port, bool* use_ssl) const; std::string GetServerHost() const; bool terminate_all_io() const { base::AutoLock lock(terminate_all_io_mutex_); return terminate_all_io_; } // Factory method to create a Post object we can use for communication with // the server. virtual Post* MakePost(); void set_client_id(const std::string& client_id) { DCHECK(client_id_.empty()); client_id_.assign(client_id); } void set_auth_token(const std::string& auth_token) { // TODO(chron): Consider adding a message loop check here. base::AutoLock lock(auth_token_mutex_); auth_token_.assign(auth_token); } const std::string auth_token() const { base::AutoLock lock(auth_token_mutex_); return auth_token_; } protected: inline std::string proto_sync_path() const { base::AutoLock lock(path_mutex_); return proto_sync_path_; } std::string get_time_path() const { base::AutoLock lock(path_mutex_); return get_time_path_; } // Called wherever a failure should be taken as an indication that we may // be experiencing connection difficulties. virtual bool IncrementErrorCount(); // NOTE: Tests rely on this protected function being virtual. // // Internal PostBuffer base function. virtual bool PostBufferToPath(const PostBufferParams*, const std::string& path, const std::string& auth_token, ScopedServerStatusWatcher* watcher); // Protects access to sync_server_, sync_server_port_ and use_ssl_: mutable base::Lock server_parameters_mutex_; // The sync_server_ is the server that requests will be made to. std::string sync_server_; // The sync_server_port_ is the port that HTTP requests will be made on. int sync_server_port_; // The unique id of the user's client. std::string client_id_; // The user-agent string for HTTP. std::string user_agent_; // Indicates whether or not requests should be made using HTTPS. bool use_ssl_; // The paths we post to. mutable base::Lock path_mutex_; std::string proto_sync_path_; std::string get_time_path_; mutable base::Lock auth_token_mutex_; // The auth token to use in authenticated requests. Set by the AuthWatcher. std::string auth_token_; base::Lock error_count_mutex_; // Protects error_count_ int error_count_; // Tracks the number of connection errors. // TODO(tim): Deprecated. Channel* const channel_; scoped_refptr<ObserverListThreadSafe<ServerConnectionEventListener> > listeners_; // Volatile so various threads can call server_status() without // synchronization. volatile HttpResponse::ServerConnectionCode server_status_; bool server_reachable_; // A counter that is incremented everytime ResetAuthStatus() is called. volatile base::subtle::AtomicWord reset_count_; private: friend class Post; friend class ScopedServerStatusWatcher; void NotifyStatusChanged(); void ResetConnection(); mutable base::Lock terminate_all_io_mutex_; bool terminate_all_io_; // When set to true, terminate all connections asap. DISALLOW_COPY_AND_ASSIGN(ServerConnectionManager); }; // Fills a ClientToServerMessage with the appropriate share and birthday // settings. bool FillMessageWithShareDetails(sync_pb::ClientToServerMessage* csm, syncable::DirectoryManager* manager, const std::string& share); std::ostream& operator<<(std::ostream& s, const struct HttpResponse& hr); } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_