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.
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"
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.
22 // Initial delay for exponential back-off in ms.
25 // Factor by which the waiting time will be multiplied.
28 // Fuzzing percentage. ex: 10% will spread requests randomly
29 // between 90%-100% of the calculated time.
32 // Maximum amount of time we are willing to delay our request in ms.
33 1000 * 3600 * 4, // 4 hours.
35 // Time to keep an entry from being discarded even when it
36 // has no significant state, -1 to never discard.
39 // Don't use initial delay unless the last request was an error.
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
)),
55 GCMNetworkChannel::~GCMNetworkChannel() {
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.
64 void GCMNetworkChannel::ResetRegisterBackoffEntryForTest(
65 const net::BackoffEntry::Policy
* policy
) {
66 register_backoff_entry_
.reset(new net::BackoffEntry(policy
));
69 void GCMNetworkChannel::Register() {
70 delegate_
->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete
,
71 weak_factory_
.GetWeakPtr()));
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())
86 DVLOG(2) << "Register failed: " << result
;
87 // Retry in case of transient error.
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(
96 base::Bind(&GCMNetworkChannel::Register
,
97 weak_factory_
.GetWeakPtr()),
98 register_backoff_entry_
->GetTimeUntilRelease());
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
;
113 if (!registration_id_
.empty()) {
114 RequestAccessToken();
118 void GCMNetworkChannel::RequestAccessToken() {
119 DCHECK(CalledOnValidThread());
120 delegate_
->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete
,
121 weak_factory_
.GetWeakPtr()));
124 void GCMNetworkChannel::OnGetTokenComplete(
125 const GoogleServiceAuthError
& error
,
126 const std::string
& token
) {
127 DCHECK(CalledOnValidThread());
128 if (encoded_message_
.empty()) {
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();
140 DCHECK(!token
.empty());
141 // Save access token in case POST fails and we need to invalidate it.
142 access_token_
= token
;
144 DVLOG(2) << "Got access token, sending message";
146 fetcher_
.reset(net::URLFetcher::Create(BuildUrl(), net::URLFetcher::POST
,
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_
);
153 // Clear message to prevent accidentally resending it in the future.
154 encoded_message_
.clear();
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();
163 net::URLRequestStatus status
= fetcher
->GetStatus();
164 if (!status
.is_success()) {
165 DVLOG(1) << "URLFetcher failure";
169 if (fetcher
->GetResponseCode() == net::HTTP_UNAUTHORIZED
) {
170 DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED";
171 delegate_
->InvalidateToken(access_token_
);
174 DVLOG(2) << "URLFetcher success";
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");
186 } // namespace syncer