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.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