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.
5 #include "chrome/service/cloud_print/cloud_print_url_fetcher.h"
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"
22 namespace cloud_print
{
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
);
35 UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.Other", time
);
39 void ReportRetriesCount(CloudPrintURLFetcher::RequestType type
,
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",
46 } else if (type
== CloudPrintURLFetcher::REQUEST_DATA
) {
47 UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.DownloadData",
50 UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.Other", retries
);
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",
60 } else if (type
== CloudPrintURLFetcher::REQUEST_DATA
) {
61 UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.DownloadData",
64 UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.Other", 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",
74 } else if (type
== CloudPrintURLFetcher::REQUEST_DATA
) {
75 UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.DownloadData",
78 UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.Other", size
);
82 CloudPrintURLFetcherFactory
* g_factory
= NULL
;
87 CloudPrintURLFetcherFactory::~CloudPrintURLFetcherFactory() {}
90 CloudPrintURLFetcher
* CloudPrintURLFetcher::Create() {
91 CloudPrintURLFetcherFactory
* factory
= CloudPrintURLFetcher::factory();
92 return factory
? factory
->CreateCloudPrintURLFetcher() :
93 new CloudPrintURLFetcher
;
97 CloudPrintURLFetcherFactory
* CloudPrintURLFetcher::factory() {
102 void CloudPrintURLFetcher::set_factory(CloudPrintURLFetcherFactory
* factory
) {
106 CloudPrintURLFetcher::ResponseAction
107 CloudPrintURLFetcher::Delegate::HandleRawResponse(
108 const net::URLFetcher
* source
,
110 const net::URLRequestStatus
& status
,
112 const net::ResponseCookies
& cookies
,
113 const std::string
& data
) {
114 return CONTINUE_PROCESSING
;
117 CloudPrintURLFetcher::ResponseAction
118 CloudPrintURLFetcher::Delegate::HandleRawData(
119 const net::URLFetcher
* source
,
121 const std::string
& data
) {
122 return CONTINUE_PROCESSING
;
125 CloudPrintURLFetcher::ResponseAction
126 CloudPrintURLFetcher::Delegate::HandleJSONData(
127 const net::URLFetcher
* source
,
129 base::DictionaryValue
* json_data
,
131 return CONTINUE_PROCESSING
;
134 CloudPrintURLFetcher::CloudPrintURLFetcher()
140 bool CloudPrintURLFetcher::IsSameRequest(const net::URLFetcher
* source
) {
141 return (request_
.get() == source
);
144 void CloudPrintURLFetcher::StartGetRequest(
149 const std::string
& additional_headers
) {
150 StartRequestHelper(type
, url
, net::URLFetcher::GET
, delegate
, max_retries
,
151 std::string(), std::string(), additional_headers
);
154 void CloudPrintURLFetcher::StartPostRequest(
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
);
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);
173 source
->GetResponseAsString(&data
);
174 ReportRequestTime(type_
, base::Time::Now() - start_time_
);
175 ReportDownloadSize(type_
, data
.size());
176 ResponseAction action
= delegate_
->HandleRawResponse(
180 source
->GetResponseCode(),
181 source
->GetCookies(),
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();
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
;
195 action
= delegate_
->HandleRawData(source
, source
->GetURL(), data
);
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
);
207 action
= delegate_
->HandleJSONData(source
,
212 action
= RETRY_REQUEST
;
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
222 request_
->ReceivedContentWasMalformed();
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();
231 if ((-1 != source
->GetMaxRetriesOn5xx()) &&
232 (num_retries_
> source
->GetMaxRetriesOn5xx())) {
233 // Retry limit reached. Give up.
234 delegate_
->OnRequestGiveUp();
235 action
= STOP_PROCESSING
;
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
240 SetupRequestHeaders();
241 request_
->SetRequestContext(GetRequestContextGetter());
242 start_time_
= base::Time::Now();
246 if (action
!= RETRY_REQUEST
) {
247 ReportRetriesCount(type_
, num_retries_
);
251 void CloudPrintURLFetcher::StartRequestHelper(
254 net::URLFetcher::RequestType request_type
,
257 const std::string
& post_data_mime_type
,
258 const std::string
& post_data
,
259 const std::string
& additional_headers
) {
262 UMA_HISTOGRAM_ENUMERATION("CloudPrint.UrlFetcherRequestType", type
,
264 // Persist the additional headers in case we need to retry the request.
265 additional_headers_
= additional_headers
;
266 request_
= 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());
279 start_time_
= base::Time::Now();
283 void CloudPrintURLFetcher::SetupRequestHeaders() {
284 std::string headers
= delegate_
->GetAuthHeader();
285 if (!headers
.empty())
287 headers
+= kChromeCloudPrintProxyHeader
;
288 if (!additional_headers_
.empty()) {
290 headers
+= additional_headers_
;
292 request_
->SetExtraRequestHeaders(headers
);
295 CloudPrintURLFetcher::~CloudPrintURLFetcher() {}
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
);
307 } // namespace cloud_print