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/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "components/gcm_driver/gcm_backoff_policy.h"
12 #include "net/base/escape.h"
13 #include "net/base/load_flags.h"
14 #include "net/http/http_status_code.h"
15 #include "net/url_request/url_fetcher.h"
16 #include "net/url_request/url_request_status.h"
17 #include "sync/protocol/experiment_status.pb.h"
24 const char kRequestContentType
[] = "application/octet-stream";
25 const char kGCMChannelTag
[] = "gcm_channel";
26 const int kDefaultPollIntervalSeconds
= 60 * 60; // 60 minutes.
27 const int kMinPollIntervalSeconds
= 30 * 60; // 30 minutes.
31 GCMChannelStatusRequest::GCMChannelStatusRequest(
32 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
33 const std::string
& channel_status_request_url
,
34 const std::string
& user_agent
,
35 const GCMChannelStatusRequestCallback
& callback
)
36 : request_context_getter_(request_context_getter
),
37 channel_status_request_url_(channel_status_request_url
),
38 user_agent_(user_agent
),
40 backoff_entry_(&(GetGCMBackoffPolicy())),
41 weak_ptr_factory_(this) {
44 GCMChannelStatusRequest::~GCMChannelStatusRequest() {
48 int GCMChannelStatusRequest::default_poll_interval_seconds() {
49 return kDefaultPollIntervalSeconds
;
53 int GCMChannelStatusRequest::min_poll_interval_seconds() {
54 return kMinPollIntervalSeconds
;
57 void GCMChannelStatusRequest::Start() {
58 DCHECK(!url_fetcher_
.get());
60 GURL
request_url(channel_status_request_url_
);
62 sync_pb::ExperimentStatusRequest proto_data
;
63 proto_data
.add_experiment_name(kGCMChannelTag
);
64 std::string upload_data
;
65 if (!proto_data
.SerializeToString(&upload_data
)) {
70 net::URLFetcher::Create(request_url
, net::URLFetcher::POST
, this);
71 url_fetcher_
->SetRequestContext(request_context_getter_
.get());
72 url_fetcher_
->AddExtraRequestHeader("User-Agent: " + user_agent_
);
73 url_fetcher_
->SetUploadData(kRequestContentType
, upload_data
);
74 url_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
75 net::LOAD_DO_NOT_SAVE_COOKIES
);
76 url_fetcher_
->Start();
79 void GCMChannelStatusRequest::OnURLFetchComplete(
80 const net::URLFetcher
* source
) {
81 if (ParseResponse(source
))
84 RetryWithBackoff(true);
87 bool GCMChannelStatusRequest::ParseResponse(const net::URLFetcher
* source
) {
88 if (!source
->GetStatus().is_success()) {
89 LOG(ERROR
) << "GCM channel request failed.";
93 if (source
->GetResponseCode() != net::HTTP_OK
) {
94 LOG(ERROR
) << "GCM channel request failed. HTTP status: "
95 << source
->GetResponseCode();
99 std::string response_string
;
100 if (!source
->GetResponseAsString(&response_string
)) {
101 LOG(ERROR
) << "GCM channel response failed to be retrieved.";
105 // Empty response means to keep the existing values.
106 if (response_string
.empty()) {
107 callback_
.Run(false, false, 0);
111 sync_pb::ExperimentStatusResponse response_proto
;
112 if (!response_proto
.ParseFromString(response_string
)) {
113 LOG(ERROR
) << "GCM channel response failed to be parsed as proto.";
118 if (response_proto
.experiment_size() == 1 &&
119 response_proto
.experiment(0).has_gcm_channel() &&
120 response_proto
.experiment(0).gcm_channel().has_enabled()) {
121 enabled
= response_proto
.experiment(0).gcm_channel().enabled();
124 int poll_interval_seconds
;
125 if (response_proto
.has_poll_interval_seconds())
126 poll_interval_seconds
= response_proto
.poll_interval_seconds();
128 poll_interval_seconds
= kDefaultPollIntervalSeconds
;
129 if (poll_interval_seconds
< kMinPollIntervalSeconds
)
130 poll_interval_seconds
= kMinPollIntervalSeconds
;
132 callback_
.Run(true, enabled
, poll_interval_seconds
);
137 void GCMChannelStatusRequest::RetryWithBackoff(bool update_backoff
) {
138 if (update_backoff
) {
139 url_fetcher_
.reset();
140 backoff_entry_
.InformOfRequest(false);
143 if (backoff_entry_
.ShouldRejectRequest()) {
144 DVLOG(1) << "Delaying GCM channel request for "
145 << backoff_entry_
.GetTimeUntilRelease().InMilliseconds()
147 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
148 FROM_HERE
, base::Bind(&GCMChannelStatusRequest::RetryWithBackoff
,
149 weak_ptr_factory_
.GetWeakPtr(), false),
150 backoff_entry_
.GetTimeUntilRelease());