Home | History | Annotate | Download | only in gcm_driver
      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 "components/gcm_driver/gcm_channel_status_request.h"
      6 
      7 #include "base/bind.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"
     16 #include "url/gurl.h"
     17 
     18 namespace gcm {
     19 
     20 namespace {
     21 
     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.
     26 
     27 }  // namespace
     28 
     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),
     37       callback_(callback),
     38       backoff_entry_(&(GetGCMBackoffPolicy())),
     39       weak_ptr_factory_(this) {
     40 }
     41 
     42 GCMChannelStatusRequest::~GCMChannelStatusRequest() {
     43 }
     44 
     45 // static
     46 int GCMChannelStatusRequest::default_poll_interval_seconds() {
     47   return kDefaultPollIntervalSeconds;
     48 }
     49 
     50 // static
     51 int GCMChannelStatusRequest::min_poll_interval_seconds() {
     52   return kMinPollIntervalSeconds;
     53 }
     54 
     55 void GCMChannelStatusRequest::Start() {
     56   DCHECK(!url_fetcher_.get());
     57 
     58   GURL request_url(channel_status_request_url_);
     59 
     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)) {
     64      NOTREACHED();
     65   }
     66 
     67   url_fetcher_.reset(
     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();
     75 }
     76 
     77 void GCMChannelStatusRequest::OnURLFetchComplete(
     78     const net::URLFetcher* source) {
     79   if (ParseResponse(source))
     80     return;
     81 
     82   RetryWithBackoff(true);
     83 }
     84 
     85 bool GCMChannelStatusRequest::ParseResponse(const net::URLFetcher* source) {
     86   if (!source->GetStatus().is_success()) {
     87     LOG(ERROR) << "GCM channel request failed.";
     88     return false;
     89   }
     90 
     91   if (source->GetResponseCode() != net::HTTP_OK) {
     92     LOG(ERROR) << "GCM channel request failed. HTTP status: "
     93                << source->GetResponseCode();
     94     return false;
     95   }
     96 
     97   std::string response_string;
     98   if (!source->GetResponseAsString(&response_string)) {
     99     LOG(ERROR) << "GCM channel response failed to be retrieved.";
    100     return false;
    101   }
    102 
    103   // Empty response means to keep the existing values.
    104   if (response_string.empty()) {
    105     callback_.Run(false, false, 0);
    106     return true;
    107   }
    108 
    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.";
    112     return false;
    113   }
    114 
    115   bool enabled = true;
    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();
    120   }
    121 
    122   int poll_interval_seconds;
    123   if (response_proto.has_poll_interval_seconds())
    124     poll_interval_seconds = response_proto.poll_interval_seconds();
    125   else
    126     poll_interval_seconds = kDefaultPollIntervalSeconds;
    127   if (poll_interval_seconds < kMinPollIntervalSeconds)
    128     poll_interval_seconds = kMinPollIntervalSeconds;
    129 
    130   callback_.Run(true, enabled, poll_interval_seconds);
    131 
    132   return true;
    133 }
    134 
    135 void GCMChannelStatusRequest::RetryWithBackoff(bool update_backoff) {
    136   if (update_backoff) {
    137     url_fetcher_.reset();
    138     backoff_entry_.InformOfRequest(false);
    139   }
    140 
    141   if (backoff_entry_.ShouldRejectRequest()) {
    142     DVLOG(1) << "Delaying GCM channel request for "
    143              << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
    144              << " ms.";
    145     base::MessageLoop::current()->PostDelayedTask(
    146         FROM_HERE,
    147         base::Bind(&GCMChannelStatusRequest::RetryWithBackoff,
    148                    weak_ptr_factory_.GetWeakPtr(),
    149                    false),
    150         backoff_entry_.GetTimeUntilRelease());
    151     return;
    152   }
    153 
    154   Start();
    155 }
    156 
    157 }  // namespace gcm
    158