summaryrefslogtreecommitdiffstats
path: root/sync/engine/net/server_connection_manager.h
blob: 6514290c9e943a4f128cf7f1c008dc797faee157 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// 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_