// 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_