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 "components/gcm_driver/gcm_channel_status_request.h"
8 #include "base/message_loop/message_loop.h"
9 #include "components/gcm_driver/gcm_backoff_policy.h"
10 #include "net/base/escape.h"
11 #include "net/base/load_flags.h"
12 #include "net/http/http_status_code.h"
13 #include "net/url_request/url_fetcher.h"
14 #include "net/url_request/url_request_status.h"
15 #include "sync/protocol/experiment_status.pb.h"
22 const char kRequestContentType
[] = "application/octet-stream";
23 const char kGCMChannelTag
[] = "gcm_channel";
24 const int kDefaultPollIntervalSeconds
= 60 * 60; // 60 minutes.
25 const int kMinPollIntervalSeconds
= 30 * 60; // 30 minutes.
29 GCMChannelStatusRequest::GCMChannelStatusRequest(
30 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
31 const std::string
& channel_status_request_url
,
32 const std::string
& user_agent
,
33 const GCMChannelStatusRequestCallback
& callback
)
34 : request_context_getter_(request_context_getter
),
35 channel_status_request_url_(channel_status_request_url
),
36 user_agent_(user_agent
),
38 backoff_entry_(&(GetGCMBackoffPolicy())),
39 weak_ptr_factory_(this) {
42 GCMChannelStatusRequest::~GCMChannelStatusRequest() {
46 int GCMChannelStatusRequest::default_poll_interval_seconds() {
47 return kDefaultPollIntervalSeconds
;
51 int GCMChannelStatusRequest::min_poll_interval_seconds() {
52 return kMinPollIntervalSeconds
;
55 void GCMChannelStatusRequest::Start() {
56 DCHECK(!url_fetcher_
.get());
58 GURL
request_url(channel_status_request_url_
);
60 sync_pb::ExperimentStatusRequest proto_data
;
61 proto_data
.add_experiment_name(kGCMChannelTag
);
62 std::string upload_data
;
63 if (!proto_data
.SerializeToString(&upload_data
)) {
68 net::URLFetcher::Create(request_url
, net::URLFetcher::POST
, this));
69 url_fetcher_
->SetRequestContext(request_context_getter_
.get());
70 url_fetcher_
->AddExtraRequestHeader("User-Agent: " + user_agent_
);
71 url_fetcher_
->SetUploadData(kRequestContentType
, upload_data
);
72 url_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
73 net::LOAD_DO_NOT_SAVE_COOKIES
);
74 url_fetcher_
->Start();
77 void GCMChannelStatusRequest::OnURLFetchComplete(
78 const net::URLFetcher
* source
) {
79 if (ParseResponse(source
))
82 RetryWithBackoff(true);
85 bool GCMChannelStatusRequest::ParseResponse(const net::URLFetcher
* source
) {
86 if (!source
->GetStatus().is_success()) {
87 LOG(ERROR
) << "GCM channel request failed.";
91 if (source
->GetResponseCode() != net::HTTP_OK
) {
92 LOG(ERROR
) << "GCM channel request failed. HTTP status: "
93 << source
->GetResponseCode();
97 std::string response_string
;
98 if (!source
->GetResponseAsString(&response_string
)) {
99 LOG(ERROR
) << "GCM channel response failed to be retrieved.";
103 // Empty response means to keep the existing values.
104 if (response_string
.empty()) {
105 callback_
.Run(false, false, 0);
109 sync_pb::ExperimentStatusResponse response_proto
;
110 if (!response_proto
.ParseFromString(response_string
)) {
111 LOG(ERROR
) << "GCM channel response failed to be parsed as proto.";
116 if (response_proto
.experiment_size() == 1 &&
117 response_proto
.experiment(0).has_gcm_channel() &&
118 response_proto
.experiment(0).gcm_channel().has_enabled()) {
119 enabled
= response_proto
.experiment(0).gcm_channel().enabled();
122 int poll_interval_seconds
;
123 if (response_proto
.has_poll_interval_seconds())
124 poll_interval_seconds
= response_proto
.poll_interval_seconds();
126 poll_interval_seconds
= kDefaultPollIntervalSeconds
;
127 if (poll_interval_seconds
< kMinPollIntervalSeconds
)
128 poll_interval_seconds
= kMinPollIntervalSeconds
;
130 callback_
.Run(true, enabled
, poll_interval_seconds
);
135 void GCMChannelStatusRequest::RetryWithBackoff(bool update_backoff
) {
136 if (update_backoff
) {
137 url_fetcher_
.reset();
138 backoff_entry_
.InformOfRequest(false);
141 if (backoff_entry_
.ShouldRejectRequest()) {
142 DVLOG(1) << "Delaying GCM channel request for "
143 << backoff_entry_
.GetTimeUntilRelease().InMilliseconds()
145 base::MessageLoop::current()->PostDelayedTask(
147 base::Bind(&GCMChannelStatusRequest::RetryWithBackoff
,
148 weak_ptr_factory_
.GetWeakPtr(),
150 backoff_entry_
.GetTimeUntilRelease());