blob: 6514290c9e943a4f128cf7f1c008dc797faee157 [file] [log] [blame]
// Copyright 2012 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 SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_
#define SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_
#include <iosfwd>
#include <string>
#include "base/atomicops.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread_checker.h"
#include "sync/base/sync_export.h"
#include "sync/internal_api/public/base/cancelation_observer.h"
#include "sync/syncable/syncable_id.h"
namespace sync_pb {
class ClientToServerMessage;
}
namespace syncer {
class CancelationSignal;
namespace syncable {
class Directory;
}
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 SYNC_EXPORT_PRIVATE 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,
// 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;
// Identifies the type of failure, if any.
ServerConnectionCode server_status;
HttpResponse();
static const char* GetServerConnectionCodeString(
ServerConnectionCode code);
static ServerConnectionCode ServerConnectionCodeFromNetError(
int error_code);
};
struct ServerConnectionEvent {
HttpResponse::ServerConnectionCode connection_code;
explicit ServerConnectionEvent(HttpResponse::ServerConnectionCode code) :
connection_code(code) {}
};
class SYNC_EXPORT_PRIVATE ServerConnectionEventListener {
public:
virtual void OnServerConnectionEvent(const ServerConnectionEvent& 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 SYNC_EXPORT_PRIVATE ScopedServerStatusWatcher
: public base::NonThreadSafe {
public:
ScopedServerStatusWatcher(ServerConnectionManager* conn_mgr,
HttpResponse* response);
virtual ~ScopedServerStatusWatcher();
private:
ServerConnectionManager* const conn_mgr_;
HttpResponse* const response_;
DISALLOW_COPY_AND_ASSIGN(ScopedServerStatusWatcher);
};
// Use this class to interact with the sync server.
// The ServerConnectionManager currently supports POSTing protocol buffers.
//
class SYNC_EXPORT_PRIVATE ServerConnectionManager : public CancelationObserver {
public:
// buffer_in - will be POSTed
// buffer_out - string will be overwritten with response
struct PostBufferParams {
std::string buffer_in;
std::string buffer_out;
HttpResponse response;
};
// Abstract class providing network-layer functionality to the
// ServerConnectionManager. Subclasses implement this using an HTTP stack of
// their choice.
class Connection {
public:
explicit Connection(ServerConnectionManager* scm);
virtual ~Connection();
// 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;
// Immediately abandons a pending HTTP POST request and unblocks caller
// in Init.
virtual void Abort() = 0;
bool ReadBufferResponse(std::string* buffer_out, HttpResponse* response,
bool require_response);
bool ReadDownloadResponse(HttpResponse* response, std::string* buffer_out);
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 {
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);
};
ServerConnectionManager(const std::string& server,
int port,
bool use_ssl,
CancelationSignal* cancelation_signal);
~ServerConnectionManager() override;
// 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(PostBufferParams* params,
ScopedServerStatusWatcher* watcher);
void AddListener(ServerConnectionEventListener* listener);
void RemoveListener(ServerConnectionEventListener* listener);
inline HttpResponse::ServerConnectionCode server_status() const {
DCHECK(thread_checker_.CalledOnValidThread());
return server_status_;
}
const std::string client_id() const { return client_id_; }
// 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;
// Factory method to create an Connection object we can use for
// communication with the server.
virtual Connection* MakeConnection();
// Closes any active network connections to the sync server.
// We expect this to get called on a different thread than the valid
// ThreadChecker thread, as we want to kill any pending http traffic without
// having to wait for the request to complete.
void OnSignalReceived() final;
void set_client_id(const std::string& client_id) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(client_id_.empty());
client_id_.assign(client_id);
}
// Sets a new auth token and time.
bool SetAuthToken(const std::string& auth_token);
bool HasInvalidAuthToken() {
return auth_token_.empty();
}
const std::string auth_token() const {
DCHECK(thread_checker_.CalledOnValidThread());
return auth_token_;
}
protected:
inline std::string proto_sync_path() const {
return proto_sync_path_;
}
// Updates server_status_ and notifies listeners if server_status_ changed
void SetServerStatus(HttpResponse::ServerConnectionCode server_status);
// NOTE: Tests rely on this protected function being virtual.
//
// Internal PostBuffer base function.
virtual bool PostBufferToPath(PostBufferParams*,
const std::string& path,
const std::string& auth_token,
ScopedServerStatusWatcher* watcher);
// An internal helper to clear our auth_token_ and cache the old version
// in |previously_invalidated_token_| to shelter us from retrying with a
// known bad token.
void InvalidateAndClearAuthToken();
// Helper to check terminated flags and build a Connection object, installing
// it as the |active_connection_|. If this ServerConnectionManager has been
// terminated, this will return NULL.
Connection* MakeActiveConnection();
// Called by Connection objects as they are destroyed to allow the
// ServerConnectionManager to cleanup active connections.
void OnConnectionDestroyed(Connection* connection);
// 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_;
// Indicates whether or not requests should be made using HTTPS.
bool use_ssl_;
// The paths we post to.
std::string proto_sync_path_;
// The auth token to use in authenticated requests.
std::string auth_token_;
// The previous auth token that is invalid now.
std::string previously_invalidated_token;
base::ObserverList<ServerConnectionEventListener> listeners_;
HttpResponse::ServerConnectionCode server_status_;
base::ThreadChecker thread_checker_;
// Protects all variables below to allow bailing out of active connections.
base::Lock terminate_connection_lock_;
// If true, we've been told to terminate IO and expect to be destroyed
// shortly. No future network requests will be made.
bool terminated_;
// A non-owning pointer to any active http connection, so that we can abort
// it if necessary.
Connection* active_connection_;
private:
friend class Connection;
friend class ScopedServerStatusWatcher;
// A class to help deal with cleaning up active Connection objects when (for
// ex) multiple early-exits are present in some scope. ScopedConnectionHelper
// informs the ServerConnectionManager before the Connection object it takes
// ownership of is destroyed.
class ScopedConnectionHelper {
public:
// |manager| must outlive this. Takes ownership of |connection|.
ScopedConnectionHelper(ServerConnectionManager* manager,
Connection* connection);
~ScopedConnectionHelper();
Connection* get();
private:
ServerConnectionManager* manager_;
scoped_ptr<Connection> connection_;
DISALLOW_COPY_AND_ASSIGN(ScopedConnectionHelper);
};
void NotifyStatusChanged();
CancelationSignal* const cancelation_signal_;
bool signal_handler_registered_;
DISALLOW_COPY_AND_ASSIGN(ServerConnectionManager);
};
std::ostream& operator<<(std::ostream& s, const struct HttpResponse& hr);
} // namespace syncer
#endif // SYNC_ENGINE_NET_SERVER_CONNECTION_MANAGER_H_