Home | History | Annotate | Download | only in cloud_print
      1 // Copyright (c) 2012 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 "chrome/service/cloud_print/cloud_print_url_fetcher.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "base/values.h"
     10 #include "chrome/common/cloud_print/cloud_print_constants.h"
     11 #include "chrome/common/cloud_print/cloud_print_helpers.h"
     12 #include "chrome/service/cloud_print/cloud_print_service_helpers.h"
     13 #include "chrome/service/cloud_print/cloud_print_token_store.h"
     14 #include "chrome/service/net/service_url_request_context_getter.h"
     15 #include "chrome/service/service_process.h"
     16 #include "net/base/load_flags.h"
     17 #include "net/http/http_status_code.h"
     18 #include "net/url_request/url_fetcher.h"
     19 #include "net/url_request/url_request_status.h"
     20 #include "url/gurl.h"
     21 
     22 namespace cloud_print {
     23 
     24 namespace {
     25 
     26 void ReportRequestTime(CloudPrintURLFetcher::RequestType type,
     27                        base::TimeDelta time) {
     28   if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
     29     UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.Register", time);
     30   } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
     31     UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.UpdatePrinter", time);
     32   } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
     33     UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.DownloadData", time);
     34   } else {
     35     UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.Other", time);
     36   }
     37 }
     38 
     39 void ReportRetriesCount(CloudPrintURLFetcher::RequestType type,
     40                         int retries) {
     41   if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
     42     UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.Register", retries);
     43   } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
     44     UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.UpdatePrinter",
     45                              retries);
     46   } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
     47     UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.DownloadData",
     48                              retries);
     49   } else {
     50     UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.Other", retries);
     51   }
     52 }
     53 
     54 void ReportDownloadSize(CloudPrintURLFetcher::RequestType type, size_t size) {
     55   if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
     56     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.Register", size);
     57   } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
     58     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.UpdatePrinter",
     59                             size);
     60   } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
     61     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.DownloadData",
     62                             size);
     63   } else {
     64     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.Other", size);
     65   }
     66 }
     67 
     68 void ReportUploadSize(CloudPrintURLFetcher::RequestType type, size_t size) {
     69   if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
     70     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.Register", size);
     71   } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
     72     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.UpdatePrinter",
     73                             size);
     74   } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
     75     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.DownloadData",
     76                             size);
     77   } else {
     78     UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.Other", size);
     79   }
     80 }
     81 
     82 CloudPrintURLFetcherFactory* g_factory = NULL;
     83 
     84 }  // namespace
     85 
     86 // virtual
     87 CloudPrintURLFetcherFactory::~CloudPrintURLFetcherFactory() {}
     88 
     89 // static
     90 CloudPrintURLFetcher* CloudPrintURLFetcher::Create() {
     91   CloudPrintURLFetcherFactory* factory = CloudPrintURLFetcher::factory();
     92   return factory ? factory->CreateCloudPrintURLFetcher() :
     93       new CloudPrintURLFetcher;
     94 }
     95 
     96 // static
     97 CloudPrintURLFetcherFactory* CloudPrintURLFetcher::factory() {
     98   return g_factory;
     99 }
    100 
    101 // static
    102 void CloudPrintURLFetcher::set_factory(CloudPrintURLFetcherFactory* factory) {
    103   g_factory = factory;
    104 }
    105 
    106 CloudPrintURLFetcher::ResponseAction
    107 CloudPrintURLFetcher::Delegate::HandleRawResponse(
    108     const net::URLFetcher* source,
    109     const GURL& url,
    110     const net::URLRequestStatus& status,
    111     int response_code,
    112     const net::ResponseCookies& cookies,
    113     const std::string& data) {
    114   return CONTINUE_PROCESSING;
    115 }
    116 
    117 CloudPrintURLFetcher::ResponseAction
    118 CloudPrintURLFetcher::Delegate::HandleRawData(
    119     const net::URLFetcher* source,
    120     const GURL& url,
    121     const std::string& data) {
    122   return CONTINUE_PROCESSING;
    123 }
    124 
    125 CloudPrintURLFetcher::ResponseAction
    126 CloudPrintURLFetcher::Delegate::HandleJSONData(
    127     const net::URLFetcher* source,
    128     const GURL& url,
    129     base::DictionaryValue* json_data,
    130     bool succeeded) {
    131   return CONTINUE_PROCESSING;
    132 }
    133 
    134 CloudPrintURLFetcher::CloudPrintURLFetcher()
    135     : delegate_(NULL),
    136       num_retries_(0),
    137       type_(REQUEST_MAX) {
    138 }
    139 
    140 bool CloudPrintURLFetcher::IsSameRequest(const net::URLFetcher* source) {
    141   return (request_.get() == source);
    142 }
    143 
    144 void CloudPrintURLFetcher::StartGetRequest(
    145     RequestType type,
    146     const GURL& url,
    147     Delegate* delegate,
    148     int max_retries,
    149     const std::string& additional_headers) {
    150   StartRequestHelper(type, url, net::URLFetcher::GET, delegate, max_retries,
    151                      std::string(), std::string(), additional_headers);
    152 }
    153 
    154 void CloudPrintURLFetcher::StartPostRequest(
    155     RequestType type,
    156     const GURL& url,
    157     Delegate* delegate,
    158     int max_retries,
    159     const std::string& post_data_mime_type,
    160     const std::string& post_data,
    161     const std::string& additional_headers) {
    162   StartRequestHelper(type, url, net::URLFetcher::POST, delegate, max_retries,
    163                      post_data_mime_type, post_data, additional_headers);
    164 }
    165 
    166 void CloudPrintURLFetcher::OnURLFetchComplete(
    167     const net::URLFetcher* source) {
    168   VLOG(1) << "CP_PROXY: OnURLFetchComplete, url: " << source->GetURL()
    169           << ", response code: " << source->GetResponseCode();
    170   // Make sure we stay alive through the body of this function.
    171   scoped_refptr<CloudPrintURLFetcher> keep_alive(this);
    172   std::string data;
    173   source->GetResponseAsString(&data);
    174   ReportRequestTime(type_, base::Time::Now() - start_time_);
    175   ReportDownloadSize(type_, data.size());
    176   ResponseAction action = delegate_->HandleRawResponse(
    177       source,
    178       source->GetURL(),
    179       source->GetStatus(),
    180       source->GetResponseCode(),
    181       source->GetCookies(),
    182       data);
    183 
    184   // If we get auth error, notify delegate and check if it wants to proceed.
    185   if (action == CONTINUE_PROCESSING &&
    186       source->GetResponseCode() == net::HTTP_FORBIDDEN) {
    187     action = delegate_->OnRequestAuthError();
    188   }
    189 
    190   if (action == CONTINUE_PROCESSING) {
    191     // We need to retry on all network errors.
    192     if (!source->GetStatus().is_success() || (source->GetResponseCode() != 200))
    193       action = RETRY_REQUEST;
    194     else
    195       action = delegate_->HandleRawData(source, source->GetURL(), data);
    196 
    197     if (action == CONTINUE_PROCESSING) {
    198       // If the delegate is not interested in handling the raw response data,
    199       // we assume that a JSON response is expected. If we do not get a JSON
    200       // response, we will retry (to handle the case where we got redirected
    201       // to a non-cloudprint-server URL eg. for authentication).
    202       bool succeeded = false;
    203       scoped_ptr<base::DictionaryValue> response_dict =
    204           ParseResponseJSON(data, &succeeded);
    205 
    206       if (response_dict) {
    207         action = delegate_->HandleJSONData(source,
    208                                            source->GetURL(),
    209                                            response_dict.get(),
    210                                            succeeded);
    211       } else {
    212         action = RETRY_REQUEST;
    213       }
    214     }
    215   }
    216   // Retry the request if needed.
    217   if (action == RETRY_REQUEST) {
    218     // Explicitly call ReceivedContentWasMalformed() to ensure the current
    219     // request gets counted as a failure for calculation of the back-off
    220     // period.  If it was already a failure by status code, this call will
    221     // be ignored.
    222     request_->ReceivedContentWasMalformed();
    223 
    224     // If we receive error code from the server "Media Type Not Supported",
    225     // there is no reason to retry, request will never succeed.
    226     // In that case we should call OnRequestGiveUp() right away.
    227     if (source->GetResponseCode() == net::HTTP_UNSUPPORTED_MEDIA_TYPE)
    228       num_retries_ = source->GetMaxRetriesOn5xx();
    229 
    230     ++num_retries_;
    231     if ((-1 != source->GetMaxRetriesOn5xx()) &&
    232         (num_retries_ > source->GetMaxRetriesOn5xx())) {
    233       // Retry limit reached. Give up.
    234       delegate_->OnRequestGiveUp();
    235       action = STOP_PROCESSING;
    236     } else {
    237       // Either no retry limit specified or retry limit has not yet been
    238       // reached. Try again. Set up the request headers again because the token
    239       // may have changed.
    240       SetupRequestHeaders();
    241       request_->SetRequestContext(GetRequestContextGetter());
    242       start_time_ = base::Time::Now();
    243       request_->Start();
    244     }
    245   }
    246   if (action != RETRY_REQUEST) {
    247     ReportRetriesCount(type_, num_retries_);
    248   }
    249 }
    250 
    251 void CloudPrintURLFetcher::StartRequestHelper(
    252     RequestType type,
    253     const GURL& url,
    254     net::URLFetcher::RequestType request_type,
    255     Delegate* delegate,
    256     int max_retries,
    257     const std::string& post_data_mime_type,
    258     const std::string& post_data,
    259     const std::string& additional_headers) {
    260   DCHECK(delegate);
    261   type_ = type;
    262   UMA_HISTOGRAM_ENUMERATION("CloudPrint.UrlFetcherRequestType", type,
    263                             REQUEST_MAX);
    264   // Persist the additional headers in case we need to retry the request.
    265   additional_headers_ = additional_headers;
    266   request_.reset(net::URLFetcher::Create(0, url, request_type, this));
    267   request_->SetRequestContext(GetRequestContextGetter());
    268   // Since we implement our own retry logic, disable the retry in URLFetcher.
    269   request_->SetAutomaticallyRetryOn5xx(false);
    270   request_->SetMaxRetriesOn5xx(max_retries);
    271   delegate_ = delegate;
    272   SetupRequestHeaders();
    273   request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
    274                          net::LOAD_DO_NOT_SAVE_COOKIES);
    275   if (request_type == net::URLFetcher::POST) {
    276     request_->SetUploadData(post_data_mime_type, post_data);
    277     ReportUploadSize(type_, post_data.size());
    278   }
    279   start_time_ = base::Time::Now();
    280   request_->Start();
    281 }
    282 
    283 void CloudPrintURLFetcher::SetupRequestHeaders() {
    284   std::string headers = delegate_->GetAuthHeader();
    285   if (!headers.empty())
    286     headers += "\r\n";
    287   headers += kChromeCloudPrintProxyHeader;
    288   if (!additional_headers_.empty()) {
    289     headers += "\r\n";
    290     headers += additional_headers_;
    291   }
    292   request_->SetExtraRequestHeaders(headers);
    293 }
    294 
    295 CloudPrintURLFetcher::~CloudPrintURLFetcher() {}
    296 
    297 net::URLRequestContextGetter* CloudPrintURLFetcher::GetRequestContextGetter() {
    298   ServiceURLRequestContextGetter* getter =
    299       g_service_process->GetServiceURLRequestContextGetter();
    300   // Now set up the user agent for cloudprint.
    301   std::string user_agent = getter->user_agent();
    302   base::StringAppendF(&user_agent, " %s", kCloudPrintUserAgent);
    303   getter->set_user_agent(user_agent);
    304   return getter;
    305 }
    306 
    307 }  // namespace cloud_print
    308