- add sources.
[platform/framework/web/crosswalk.git] / src / sync / engine / net / server_connection_manager.cc
1 // Copyright (c) 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 #include "sync/engine/net/server_connection_manager.h"
6
7 #include <errno.h>
8
9 #include <ostream>
10 #include <string>
11 #include <vector>
12
13 #include "base/metrics/histogram.h"
14 #include "build/build_config.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/http_status_code.h"
17 #include "sync/engine/net/url_translator.h"
18 #include "sync/engine/syncer.h"
19 #include "sync/internal_api/public/base/cancelation_signal.h"
20 #include "sync/protocol/sync.pb.h"
21 #include "sync/syncable/directory.h"
22 #include "url/gurl.h"
23
24 namespace syncer {
25
26 using std::ostream;
27 using std::string;
28 using std::vector;
29
30 static const char kSyncServerSyncPath[] = "/command/";
31
32 HttpResponse::HttpResponse()
33     : response_code(kUnsetResponseCode),
34       content_length(kUnsetContentLength),
35       payload_length(kUnsetPayloadLength),
36       server_status(NONE) {}
37
38 #define ENUM_CASE(x) case x: return #x; break
39
40 const char* HttpResponse::GetServerConnectionCodeString(
41     ServerConnectionCode code) {
42   switch (code) {
43     ENUM_CASE(NONE);
44     ENUM_CASE(CONNECTION_UNAVAILABLE);
45     ENUM_CASE(IO_ERROR);
46     ENUM_CASE(SYNC_SERVER_ERROR);
47     ENUM_CASE(SYNC_AUTH_ERROR);
48     ENUM_CASE(SERVER_CONNECTION_OK);
49     ENUM_CASE(RETRY);
50   }
51   NOTREACHED();
52   return "";
53 }
54
55 #undef ENUM_CASE
56
57 // TODO(clamy): check if all errors are in the right category.
58 HttpResponse::ServerConnectionCode
59 HttpResponse::ServerConnectionCodeFromNetError(int error_code) {
60   switch (error_code) {
61     case net::ERR_ABORTED:
62     case net::ERR_SOCKET_NOT_CONNECTED:
63     case net::ERR_NETWORK_CHANGED:
64     case net::ERR_CONNECTION_FAILED:
65     case net::ERR_NAME_NOT_RESOLVED:
66     case net::ERR_INTERNET_DISCONNECTED:
67     case net::ERR_NETWORK_ACCESS_DENIED:
68     case net::ERR_NETWORK_IO_SUSPENDED:
69       return CONNECTION_UNAVAILABLE;
70   }
71   return IO_ERROR;
72 }
73
74 ServerConnectionManager::Connection::Connection(
75     ServerConnectionManager* scm) : scm_(scm) {
76 }
77
78 ServerConnectionManager::Connection::~Connection() {
79 }
80
81 bool ServerConnectionManager::Connection::ReadBufferResponse(
82     string* buffer_out,
83     HttpResponse* response,
84     bool require_response) {
85   if (net::HTTP_OK != response->response_code) {
86     response->server_status = HttpResponse::SYNC_SERVER_ERROR;
87     return false;
88   }
89
90   if (require_response && (1 > response->content_length))
91     return false;
92
93   const int64 bytes_read = ReadResponse(buffer_out,
94       static_cast<int>(response->content_length));
95   if (bytes_read != response->content_length) {
96     response->server_status = HttpResponse::IO_ERROR;
97     return false;
98   }
99   return true;
100 }
101
102 bool ServerConnectionManager::Connection::ReadDownloadResponse(
103     HttpResponse* response,
104     string* buffer_out) {
105   const int64 bytes_read = ReadResponse(buffer_out,
106       static_cast<int>(response->content_length));
107
108   if (bytes_read != response->content_length) {
109     LOG(ERROR) << "Mismatched content lengths, server claimed " <<
110         response->content_length << ", but sent " << bytes_read;
111     response->server_status = HttpResponse::IO_ERROR;
112     return false;
113   }
114   return true;
115 }
116
117 ServerConnectionManager::ScopedConnectionHelper::ScopedConnectionHelper(
118     ServerConnectionManager* manager, Connection* connection)
119     : manager_(manager), connection_(connection) {}
120
121 ServerConnectionManager::ScopedConnectionHelper::~ScopedConnectionHelper() {
122   if (connection_)
123     manager_->OnConnectionDestroyed(connection_.get());
124   connection_.reset();
125 }
126
127 ServerConnectionManager::Connection*
128 ServerConnectionManager::ScopedConnectionHelper::get() {
129   return connection_.get();
130 }
131
132 namespace {
133
134 string StripTrailingSlash(const string& s) {
135   int stripped_end_pos = s.size();
136   if (s.at(stripped_end_pos - 1) == '/') {
137     stripped_end_pos = stripped_end_pos - 1;
138   }
139
140   return s.substr(0, stripped_end_pos);
141 }
142
143 }  // namespace
144
145 // TODO(chron): Use a GURL instead of string concatenation.
146 string ServerConnectionManager::Connection::MakeConnectionURL(
147     const string& sync_server,
148     const string& path,
149     bool use_ssl) const {
150   string connection_url = (use_ssl ? "https://" : "http://");
151   connection_url += sync_server;
152   connection_url = StripTrailingSlash(connection_url);
153   connection_url += path;
154
155   return connection_url;
156 }
157
158 int ServerConnectionManager::Connection::ReadResponse(string* out_buffer,
159                                                       int length) {
160   int bytes_read = buffer_.length();
161   CHECK(length <= bytes_read);
162   out_buffer->assign(buffer_);
163   return bytes_read;
164 }
165
166 ScopedServerStatusWatcher::ScopedServerStatusWatcher(
167     ServerConnectionManager* conn_mgr, HttpResponse* response)
168     : conn_mgr_(conn_mgr),
169       response_(response) {
170   response->server_status = conn_mgr->server_status_;
171 }
172
173 ScopedServerStatusWatcher::~ScopedServerStatusWatcher() {
174   conn_mgr_->SetServerStatus(response_->server_status);
175 }
176
177 ServerConnectionManager::ServerConnectionManager(
178     const string& server,
179     int port,
180     bool use_ssl,
181     CancelationSignal* cancelation_signal)
182     : sync_server_(server),
183       sync_server_port_(port),
184       use_ssl_(use_ssl),
185       proto_sync_path_(kSyncServerSyncPath),
186       server_status_(HttpResponse::NONE),
187       terminated_(false),
188       active_connection_(NULL),
189       cancelation_signal_(cancelation_signal),
190       signal_handler_registered_(false) {
191   signal_handler_registered_ = cancelation_signal_->TryRegisterHandler(this);
192   if (!signal_handler_registered_) {
193     // Calling a virtual function from a constructor.  We can get away with it
194     // here because ServerConnectionManager::OnSignalReceived() is the function
195     // we want to call.
196     OnSignalReceived();
197   }
198 }
199
200 ServerConnectionManager::~ServerConnectionManager() {
201   if (signal_handler_registered_) {
202     cancelation_signal_->UnregisterHandler(this);
203   }
204 }
205
206 ServerConnectionManager::Connection*
207 ServerConnectionManager::MakeActiveConnection() {
208   base::AutoLock lock(terminate_connection_lock_);
209   DCHECK(!active_connection_);
210   if (terminated_)
211     return NULL;
212
213   active_connection_ = MakeConnection();
214   return active_connection_;
215 }
216
217 void ServerConnectionManager::OnConnectionDestroyed(Connection* connection) {
218   DCHECK(connection);
219   base::AutoLock lock(terminate_connection_lock_);
220   // |active_connection_| can be NULL already if it was aborted. Also,
221   // it can legitimately be a different Connection object if a new Connection
222   // was created after a previous one was Aborted and destroyed.
223   if (active_connection_ != connection)
224     return;
225
226   active_connection_ = NULL;
227 }
228
229 bool ServerConnectionManager::SetAuthToken(const std::string& auth_token) {
230   DCHECK(thread_checker_.CalledOnValidThread());
231   if (previously_invalidated_token != auth_token) {
232     auth_token_.assign(auth_token);
233     previously_invalidated_token = std::string();
234     return true;
235   }
236   return false;
237 }
238
239 void ServerConnectionManager::OnInvalidationCredentialsRejected() {
240   InvalidateAndClearAuthToken();
241   SetServerStatus(HttpResponse::SYNC_AUTH_ERROR);
242 }
243
244 void ServerConnectionManager::InvalidateAndClearAuthToken() {
245   DCHECK(thread_checker_.CalledOnValidThread());
246   // Copy over the token to previous invalid token.
247   if (!auth_token_.empty()) {
248     previously_invalidated_token.assign(auth_token_);
249     auth_token_ = std::string();
250   }
251 }
252
253 void ServerConnectionManager::SetServerStatus(
254     HttpResponse::ServerConnectionCode server_status) {
255   if (server_status_ == server_status)
256     return;
257   server_status_ = server_status;
258   NotifyStatusChanged();
259 }
260
261 void ServerConnectionManager::NotifyStatusChanged() {
262   DCHECK(thread_checker_.CalledOnValidThread());
263   FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_,
264      OnServerConnectionEvent(
265          ServerConnectionEvent(server_status_)));
266 }
267
268 bool ServerConnectionManager::PostBufferWithCachedAuth(
269     PostBufferParams* params, ScopedServerStatusWatcher* watcher) {
270   DCHECK(thread_checker_.CalledOnValidThread());
271   string path =
272       MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_));
273   return PostBufferToPath(params, path, auth_token(), watcher);
274 }
275
276 bool ServerConnectionManager::PostBufferToPath(PostBufferParams* params,
277     const string& path, const string& auth_token,
278     ScopedServerStatusWatcher* watcher) {
279   DCHECK(thread_checker_.CalledOnValidThread());
280   DCHECK(watcher != NULL);
281
282   // TODO(pavely): crbug.com/273096. Check for "credentials_lost" is added as
283   // workaround for M29 blocker to avoid sending RPC to sync with known invalid
284   // token but instead to trigger refreshing token in ProfileSyncService. Need
285   // to clean it.
286   if (auth_token.empty() || auth_token == "credentials_lost") {
287     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
288     return false;
289   }
290
291   // When our connection object falls out of scope, it clears itself from
292   // active_connection_.
293   ScopedConnectionHelper post(this, MakeActiveConnection());
294   if (!post.get()) {
295     params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE;
296     return false;
297   }
298
299   // Note that |post| may be aborted by now, which will just cause Init to fail
300   // with CONNECTION_UNAVAILABLE.
301   bool ok = post.get()->Init(
302       path.c_str(), auth_token, params->buffer_in, &params->response);
303
304   if (params->response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
305     InvalidateAndClearAuthToken();
306   }
307
308   if (!ok || net::HTTP_OK != params->response.response_code)
309     return false;
310
311   if (post.get()->ReadBufferResponse(
312       &params->buffer_out, &params->response, true)) {
313     params->response.server_status = HttpResponse::SERVER_CONNECTION_OK;
314     return true;
315   }
316   return false;
317 }
318
319 // Returns the current server parameters in server_url and port.
320 void ServerConnectionManager::GetServerParameters(string* server_url,
321                                                   int* port,
322                                                   bool* use_ssl) const {
323   if (server_url != NULL)
324     *server_url = sync_server_;
325   if (port != NULL)
326     *port = sync_server_port_;
327   if (use_ssl != NULL)
328     *use_ssl = use_ssl_;
329 }
330
331 std::string ServerConnectionManager::GetServerHost() const {
332   string server_url;
333   int port;
334   bool use_ssl;
335   GetServerParameters(&server_url, &port, &use_ssl);
336   // For unit tests.
337   if (server_url.empty())
338     return std::string();
339   // We just want the hostname, so we don't need to switch on use_ssl.
340   server_url = "http://" + server_url;
341   GURL gurl(server_url);
342   DCHECK(gurl.is_valid()) << gurl;
343   return gurl.host();
344 }
345
346 void ServerConnectionManager::AddListener(
347     ServerConnectionEventListener* listener) {
348   DCHECK(thread_checker_.CalledOnValidThread());
349   listeners_.AddObserver(listener);
350 }
351
352 void ServerConnectionManager::RemoveListener(
353     ServerConnectionEventListener* listener) {
354   DCHECK(thread_checker_.CalledOnValidThread());
355   listeners_.RemoveObserver(listener);
356 }
357
358 ServerConnectionManager::Connection* ServerConnectionManager::MakeConnection()
359 {
360   return NULL;  // For testing.
361 }
362
363 void ServerConnectionManager::OnSignalReceived() {
364   base::AutoLock lock(terminate_connection_lock_);
365   terminated_ = true;
366   if (active_connection_)
367     active_connection_->Abort();
368
369   // Sever our ties to this connection object. Note that it still may exist,
370   // since we don't own it, but it has been neutered.
371   active_connection_ = NULL;
372 }
373
374 std::ostream& operator << (std::ostream& s, const struct HttpResponse& hr) {
375   s << " Response Code (bogus on error): " << hr.response_code;
376   s << " Content-Length (bogus on error): " << hr.content_length;
377   s << " Server Status: "
378     << HttpResponse::GetServerConnectionCodeString(hr.server_status);
379   return s;
380 }
381
382 }  // namespace syncer