• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ReportRequestTime(CloudPrintURLFetcher::RequestType type,base::TimeDelta time)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 
ReportRetriesCount(CloudPrintURLFetcher::RequestType type,int retries)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 
ReportDownloadSize(CloudPrintURLFetcher::RequestType type,size_t size)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 
ReportUploadSize(CloudPrintURLFetcher::RequestType type,size_t size)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
~CloudPrintURLFetcherFactory()87 CloudPrintURLFetcherFactory::~CloudPrintURLFetcherFactory() {}
88 
89 // static
Create()90 CloudPrintURLFetcher* CloudPrintURLFetcher::Create() {
91   CloudPrintURLFetcherFactory* factory = CloudPrintURLFetcher::factory();
92   return factory ? factory->CreateCloudPrintURLFetcher() :
93       new CloudPrintURLFetcher;
94 }
95 
96 // static
factory()97 CloudPrintURLFetcherFactory* CloudPrintURLFetcher::factory() {
98   return g_factory;
99 }
100 
101 // static
set_factory(CloudPrintURLFetcherFactory * factory)102 void CloudPrintURLFetcher::set_factory(CloudPrintURLFetcherFactory* factory) {
103   g_factory = factory;
104 }
105 
106 CloudPrintURLFetcher::ResponseAction
HandleRawResponse(const net::URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const net::ResponseCookies & cookies,const std::string & data)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
HandleRawData(const net::URLFetcher * source,const GURL & url,const std::string & data)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
HandleJSONData(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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 
CloudPrintURLFetcher()134 CloudPrintURLFetcher::CloudPrintURLFetcher()
135     : delegate_(NULL),
136       num_retries_(0),
137       type_(REQUEST_MAX) {
138 }
139 
IsSameRequest(const net::URLFetcher * source)140 bool CloudPrintURLFetcher::IsSameRequest(const net::URLFetcher* source) {
141   return (request_.get() == source);
142 }
143 
StartGetRequest(RequestType type,const GURL & url,Delegate * delegate,int max_retries,const std::string & additional_headers)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 
StartPostRequest(RequestType type,const GURL & url,Delegate * delegate,int max_retries,const std::string & post_data_mime_type,const std::string & post_data,const std::string & additional_headers)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 
OnURLFetchComplete(const net::URLFetcher * source)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 
StartRequestHelper(RequestType type,const GURL & url,net::URLFetcher::RequestType request_type,Delegate * delegate,int max_retries,const std::string & post_data_mime_type,const std::string & post_data,const std::string & additional_headers)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 
SetupRequestHeaders()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 
~CloudPrintURLFetcher()295 CloudPrintURLFetcher::~CloudPrintURLFetcher() {}
296 
GetRequestContextGetter()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