Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / sync / notifier / gcm_network_channel.cc
1 // Copyright 2014 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 "google_apis/gaia/google_service_auth_error.h"
6 #include "net/http/http_status_code.h"
7 #include "net/url_request/url_fetcher.h"
8 #include "net/url_request/url_request_status.h"
9 #include "sync/notifier/gcm_network_channel.h"
10 #include "sync/notifier/gcm_network_channel_delegate.h"
11
12 namespace syncer {
13
14 namespace {
15
16 // Register backoff policy.
17 const net::BackoffEntry::Policy kRegisterBackoffPolicy = {
18   // Number of initial errors (in sequence) to ignore before applying
19   // exponential back-off rules.
20   0,
21
22   // Initial delay for exponential back-off in ms.
23   2000, // 2 seconds.
24
25   // Factor by which the waiting time will be multiplied.
26   2,
27
28   // Fuzzing percentage. ex: 10% will spread requests randomly
29   // between 90%-100% of the calculated time.
30   0.2, // 20%.
31
32   // Maximum amount of time we are willing to delay our request in ms.
33   1000 * 3600 * 4, // 4 hours.
34
35   // Time to keep an entry from being discarded even when it
36   // has no significant state, -1 to never discard.
37   -1,
38
39   // Don't use initial delay unless the last request was an error.
40   false,
41 };
42
43 }  // namespace
44
45 GCMNetworkChannel::GCMNetworkChannel(
46     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
47     scoped_ptr<GCMNetworkChannelDelegate> delegate)
48     : request_context_getter_(request_context_getter),
49       delegate_(delegate.Pass()),
50       register_backoff_entry_(new net::BackoffEntry(&kRegisterBackoffPolicy)),
51       weak_factory_(this) {
52   Register();
53 }
54
55 GCMNetworkChannel::~GCMNetworkChannel() {
56 }
57
58 void GCMNetworkChannel::UpdateCredentials(
59     const std::string& email,
60     const std::string& token) {
61   // Do nothing. We get access token by requesting it for every message.
62 }
63
64 void GCMNetworkChannel::ResetRegisterBackoffEntryForTest(
65     const net::BackoffEntry::Policy* policy) {
66   register_backoff_entry_.reset(new net::BackoffEntry(policy));
67 }
68
69 void GCMNetworkChannel::Register() {
70   delegate_->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete,
71                                  weak_factory_.GetWeakPtr()));
72 }
73
74 void GCMNetworkChannel::OnRegisterComplete(
75     const std::string& registration_id,
76     gcm::GCMClient::Result result) {
77   DCHECK(CalledOnValidThread());
78   if (result == gcm::GCMClient::SUCCESS) {
79     DCHECK(!registration_id.empty());
80     DVLOG(2) << "Got registration_id";
81     register_backoff_entry_->Reset();
82     registration_id_ = registration_id;
83     if (!encoded_message_.empty())
84       RequestAccessToken();
85   } else {
86     DVLOG(2) << "Register failed: " << result;
87     // Retry in case of transient error.
88     switch (result) {
89       case gcm::GCMClient::NETWORK_ERROR:
90       case gcm::GCMClient::SERVER_ERROR:
91       case gcm::GCMClient::TTL_EXCEEDED:
92       case gcm::GCMClient::UNKNOWN_ERROR: {
93         register_backoff_entry_->InformOfRequest(false);
94         base::MessageLoop::current()->PostDelayedTask(
95             FROM_HERE,
96             base::Bind(&GCMNetworkChannel::Register,
97                        weak_factory_.GetWeakPtr()),
98             register_backoff_entry_->GetTimeUntilRelease());
99         break;
100       }
101       default:
102         break;
103     }
104   }
105 }
106
107 void GCMNetworkChannel::SendEncodedMessage(const std::string& encoded_message) {
108   DCHECK(CalledOnValidThread());
109   DCHECK(!encoded_message.empty());
110   DVLOG(2) << "SendEncodedMessage";
111   encoded_message_ = encoded_message;
112
113   if (!registration_id_.empty()) {
114     RequestAccessToken();
115   }
116 }
117
118 void GCMNetworkChannel::RequestAccessToken() {
119   DCHECK(CalledOnValidThread());
120   delegate_->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete,
121                                      weak_factory_.GetWeakPtr()));
122 }
123
124 void GCMNetworkChannel::OnGetTokenComplete(
125     const GoogleServiceAuthError& error,
126     const std::string& token) {
127   DCHECK(CalledOnValidThread());
128   if (encoded_message_.empty()) {
129     // Nothing to do.
130     return;
131   }
132
133   if (error.state() != GoogleServiceAuthError::NONE) {
134     // Requesting access token failed. Persistent errors will be reported by
135     // token service. Just drop this request, cacheinvalidations will retry
136     // sending message and at that time we'll retry requesting access token.
137     DVLOG(1) << "RequestAccessToken failed: " << error.ToString();
138     return;
139   }
140   DCHECK(!token.empty());
141   // Save access token in case POST fails and we need to invalidate it.
142   access_token_ = token;
143
144   DVLOG(2) << "Got access token, sending message";
145
146   fetcher_.reset(net::URLFetcher::Create(BuildUrl(), net::URLFetcher::POST,
147                                          this));
148   fetcher_->SetRequestContext(request_context_getter_);
149   const std::string auth_header("Authorization: Bearer " + access_token_);
150   fetcher_->AddExtraRequestHeader(auth_header);
151   fetcher_->SetUploadData("application/x-protobuffer", encoded_message_);
152   fetcher_->Start();
153   // Clear message to prevent accidentally resending it in the future.
154   encoded_message_.clear();
155 }
156
157 void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) {
158   DCHECK(CalledOnValidThread());
159   DCHECK_EQ(fetcher_, source);
160   // Free fetcher at the end of function.
161   scoped_ptr<net::URLFetcher> fetcher = fetcher_.Pass();
162
163   net::URLRequestStatus status = fetcher->GetStatus();
164   if (!status.is_success()) {
165     DVLOG(1) << "URLFetcher failure";
166     return;
167   }
168
169   if (fetcher->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
170     DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED";
171     delegate_->InvalidateToken(access_token_);
172     return;
173   }
174   DVLOG(2) << "URLFetcher success";
175 }
176
177 GURL GCMNetworkChannel::BuildUrl() {
178   DCHECK(!registration_id_.empty());
179   // Prepare NetworkEndpointId using registration_id
180   // Serialize NetworkEndpointId into byte array and base64 encode.
181   // Format url using encoded NetworkEndpointId.
182   // TODO(pavely): implement all of the above.
183   return GURL("http://invalid.url.com");
184 }
185
186 }  // namespace syncer