cloud_print_url_fetcher.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 22namespace cloud_print { 23 24namespace { 25 26void 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 39void 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 54void 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 68void 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 82CloudPrintURLFetcherFactory* g_factory = NULL; 83 84} // namespace 85 86// virtual 87CloudPrintURLFetcherFactory::~CloudPrintURLFetcherFactory() {} 88 89// static 90CloudPrintURLFetcher* CloudPrintURLFetcher::Create() { 91 CloudPrintURLFetcherFactory* factory = CloudPrintURLFetcher::factory(); 92 return factory ? factory->CreateCloudPrintURLFetcher() : 93 new CloudPrintURLFetcher; 94} 95 96// static 97CloudPrintURLFetcherFactory* CloudPrintURLFetcher::factory() { 98 return g_factory; 99} 100 101// static 102void CloudPrintURLFetcher::set_factory(CloudPrintURLFetcherFactory* factory) { 103 g_factory = factory; 104} 105 106CloudPrintURLFetcher::ResponseAction 107CloudPrintURLFetcher::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 117CloudPrintURLFetcher::ResponseAction 118CloudPrintURLFetcher::Delegate::HandleRawData( 119 const net::URLFetcher* source, 120 const GURL& url, 121 const std::string& data) { 122 return CONTINUE_PROCESSING; 123} 124 125CloudPrintURLFetcher::ResponseAction 126CloudPrintURLFetcher::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 134CloudPrintURLFetcher::CloudPrintURLFetcher() 135 : delegate_(NULL), 136 num_retries_(0), 137 type_(REQUEST_MAX) { 138} 139 140bool CloudPrintURLFetcher::IsSameRequest(const net::URLFetcher* source) { 141 return (request_.get() == source); 142} 143 144void 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 154void 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 166void 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 251void 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 283void 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 295CloudPrintURLFetcher::~CloudPrintURLFetcher() {} 296 297net::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