url_request.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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 "net/url_request/url_request.h" 6 7#include "base/compiler_specific.h" 8#include "base/message_loop.h" 9#include "base/metrics/stats_counters.h" 10#include "base/singleton.h" 11#include "net/base/load_flags.h" 12#include "net/base/net_errors.h" 13#include "net/base/net_log.h" 14#include "net/base/ssl_cert_request_info.h" 15#include "net/base/upload_data.h" 16#include "net/http/http_response_headers.h" 17#include "net/http/http_util.h" 18#include "net/url_request/url_request_context.h" 19#include "net/url_request/url_request_job.h" 20#include "net/url_request/url_request_job_manager.h" 21#include "net/url_request/url_request_netlog_params.h" 22 23using base::Time; 24using net::UploadData; 25using std::string; 26 27namespace { 28 29// Max number of http redirects to follow. Same number as gecko. 30const int kMaxRedirects = 20; 31 32URLRequestJobManager* GetJobManager() { 33 return Singleton<URLRequestJobManager>::get(); 34} 35 36// Discard headers which have meaning in POST (Content-Length, Content-Type, 37// Origin). 38void StripPostSpecificHeaders(net::HttpRequestHeaders* headers) { 39 // These are headers that may be attached to a POST. 40 headers->RemoveHeader(net::HttpRequestHeaders::kContentLength); 41 headers->RemoveHeader(net::HttpRequestHeaders::kContentType); 42 headers->RemoveHeader(net::HttpRequestHeaders::kOrigin); 43} 44 45} // namespace 46 47/////////////////////////////////////////////////////////////////////////////// 48// URLRequest::Interceptor 49 50URLRequestJob* URLRequest::Interceptor::MaybeInterceptRedirect( 51 URLRequest* request, 52 const GURL& location) { 53 return NULL; 54} 55 56URLRequestJob* URLRequest::Interceptor::MaybeInterceptResponse( 57 URLRequest* request) { 58 return NULL; 59} 60 61/////////////////////////////////////////////////////////////////////////////// 62// URLRequest::Delegate 63 64void URLRequest::Delegate::OnReceivedRedirect(URLRequest* request, 65 const GURL& new_url, 66 bool* defer_redirect) { 67} 68 69void URLRequest::Delegate::OnAuthRequired(URLRequest* request, 70 net::AuthChallengeInfo* auth_info) { 71 request->CancelAuth(); 72} 73 74void URLRequest::Delegate::OnCertificateRequested( 75 URLRequest* request, 76 net::SSLCertRequestInfo* cert_request_info) { 77 request->ContinueWithCertificate(NULL); 78} 79 80void URLRequest::Delegate::OnSSLCertificateError(URLRequest* request, 81 int cert_error, 82 net::X509Certificate* cert) { 83 request->Cancel(); 84} 85 86void URLRequest::Delegate::OnGetCookies(URLRequest* request, 87 bool blocked_by_policy) { 88} 89 90void URLRequest::Delegate::OnSetCookie(URLRequest* request, 91 const std::string& cookie_line, 92 bool blocked_by_policy) { 93} 94 95/////////////////////////////////////////////////////////////////////////////// 96// URLRequest 97 98URLRequest::URLRequest(const GURL& url, Delegate* delegate) 99 : url_(url), 100 original_url_(url), 101 method_("GET"), 102 load_flags_(net::LOAD_NORMAL), 103 delegate_(delegate), 104 is_pending_(false), 105 enable_profiling_(false), 106 redirect_limit_(kMaxRedirects), 107 final_upload_progress_(0), 108 priority_(net::LOWEST) { 109 SIMPLE_STATS_COUNTER("URLRequestCount"); 110 111 // Sanity check out environment. 112 DCHECK(MessageLoop::current()) << 113 "The current MessageLoop must exist"; 114 DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << 115 "The current MessageLoop must be TYPE_IO"; 116} 117 118URLRequest::~URLRequest() { 119 Cancel(); 120 121 if (job_) 122 OrphanJob(); 123 124 set_context(NULL); 125} 126 127// static 128URLRequest::ProtocolFactory* URLRequest::RegisterProtocolFactory( 129 const string& scheme, ProtocolFactory* factory) { 130 return GetJobManager()->RegisterProtocolFactory(scheme, factory); 131} 132 133// static 134void URLRequest::RegisterRequestInterceptor(Interceptor* interceptor) { 135 GetJobManager()->RegisterRequestInterceptor(interceptor); 136} 137 138// static 139void URLRequest::UnregisterRequestInterceptor(Interceptor* interceptor) { 140 GetJobManager()->UnregisterRequestInterceptor(interceptor); 141} 142 143void URLRequest::AppendBytesToUpload(const char* bytes, int bytes_len) { 144 DCHECK(bytes_len > 0 && bytes); 145 if (!upload_) 146 upload_ = new UploadData(); 147 upload_->AppendBytes(bytes, bytes_len); 148} 149 150void URLRequest::AppendFileRangeToUpload( 151 const FilePath& file_path, 152 uint64 offset, 153 uint64 length, 154 const base::Time& expected_modification_time) { 155 DCHECK(file_path.value().length() > 0 && length > 0); 156 if (!upload_) 157 upload_ = new UploadData(); 158 upload_->AppendFileRange(file_path, offset, length, 159 expected_modification_time); 160} 161 162void URLRequest::set_upload(net::UploadData* upload) { 163 upload_ = upload; 164} 165 166// Get the upload data directly. 167net::UploadData* URLRequest::get_upload() { 168 return upload_.get(); 169} 170 171bool URLRequest::has_upload() const { 172 return upload_ != NULL; 173} 174 175void URLRequest::SetExtraRequestHeaderById(int id, const string& value, 176 bool overwrite) { 177 DCHECK(!is_pending_); 178 NOTREACHED() << "implement me!"; 179} 180 181void URLRequest::SetExtraRequestHeaderByName(const string& name, 182 const string& value, 183 bool overwrite) { 184 DCHECK(!is_pending_); 185 NOTREACHED() << "implement me!"; 186} 187 188void URLRequest::SetExtraRequestHeaders( 189 const net::HttpRequestHeaders& headers) { 190 DCHECK(!is_pending_); 191 extra_request_headers_ = headers; 192 193 // NOTE: This method will likely become non-trivial once the other setters 194 // for request headers are implemented. 195} 196 197net::LoadState URLRequest::GetLoadState() const { 198 return job_ ? job_->GetLoadState() : net::LOAD_STATE_IDLE; 199} 200 201uint64 URLRequest::GetUploadProgress() const { 202 if (!job_) { 203 // We haven't started or the request was cancelled 204 return 0; 205 } 206 if (final_upload_progress_) { 207 // The first job completed and none of the subsequent series of 208 // GETs when following redirects will upload anything, so we return the 209 // cached results from the initial job, the POST. 210 return final_upload_progress_; 211 } 212 return job_->GetUploadProgress(); 213} 214 215void URLRequest::GetResponseHeaderById(int id, string* value) { 216 DCHECK(job_); 217 NOTREACHED() << "implement me!"; 218} 219 220void URLRequest::GetResponseHeaderByName(const string& name, string* value) { 221 DCHECK(value); 222 if (response_info_.headers) { 223 response_info_.headers->GetNormalizedHeader(name, value); 224 } else { 225 value->clear(); 226 } 227} 228 229void URLRequest::GetAllResponseHeaders(string* headers) { 230 DCHECK(headers); 231 if (response_info_.headers) { 232 response_info_.headers->GetNormalizedHeaders(headers); 233 } else { 234 headers->clear(); 235 } 236} 237 238net::HttpResponseHeaders* URLRequest::response_headers() const { 239 return response_info_.headers.get(); 240} 241 242bool URLRequest::GetResponseCookies(ResponseCookies* cookies) { 243 DCHECK(job_); 244 return job_->GetResponseCookies(cookies); 245} 246 247void URLRequest::GetMimeType(string* mime_type) { 248 DCHECK(job_); 249 job_->GetMimeType(mime_type); 250} 251 252void URLRequest::GetCharset(string* charset) { 253 DCHECK(job_); 254 job_->GetCharset(charset); 255} 256 257int URLRequest::GetResponseCode() { 258 DCHECK(job_); 259 return job_->GetResponseCode(); 260} 261 262// static 263bool URLRequest::IsHandledProtocol(const std::string& scheme) { 264 return GetJobManager()->SupportsScheme(scheme); 265} 266 267// static 268bool URLRequest::IsHandledURL(const GURL& url) { 269 if (!url.is_valid()) { 270 // We handle error cases. 271 return true; 272 } 273 274 return IsHandledProtocol(url.scheme()); 275} 276 277// static 278void URLRequest::AllowFileAccess() { 279 GetJobManager()->set_enable_file_access(true); 280} 281 282// static 283bool URLRequest::IsFileAccessAllowed() { 284 return GetJobManager()->enable_file_access(); 285} 286 287 288void URLRequest::set_first_party_for_cookies( 289 const GURL& first_party_for_cookies) { 290 first_party_for_cookies_ = first_party_for_cookies; 291} 292 293void URLRequest::set_method(const std::string& method) { 294 DCHECK(!is_pending_); 295 method_ = method; 296} 297 298void URLRequest::set_referrer(const std::string& referrer) { 299 DCHECK(!is_pending_); 300 referrer_ = referrer; 301} 302 303GURL URLRequest::GetSanitizedReferrer() const { 304 GURL ret(referrer()); 305 306 // Ensure that we do not send username and password fields in the referrer. 307 if (ret.has_username() || ret.has_password()) { 308 GURL::Replacements referrer_mods; 309 referrer_mods.ClearUsername(); 310 referrer_mods.ClearPassword(); 311 ret = ret.ReplaceComponents(referrer_mods); 312 } 313 314 return ret; 315} 316 317void URLRequest::Start() { 318 StartJob(GetJobManager()->CreateJob(this)); 319} 320 321/////////////////////////////////////////////////////////////////////////////// 322 323void URLRequest::StartJob(URLRequestJob* job) { 324 DCHECK(!is_pending_); 325 DCHECK(!job_); 326 327 net_log_.BeginEvent( 328 net::NetLog::TYPE_URL_REQUEST_START_JOB, 329 make_scoped_refptr(new URLRequestStartEventParameters( 330 url_, method_, load_flags_, priority_))); 331 332 job_ = job; 333 job_->SetExtraRequestHeaders(extra_request_headers_); 334 335 if (upload_.get()) 336 job_->SetUpload(upload_.get()); 337 338 is_pending_ = true; 339 340 response_info_.request_time = Time::Now(); 341 response_info_.was_cached = false; 342 343 // Don't allow errors to be sent from within Start(). 344 // TODO(brettw) this may cause NotifyDone to be sent synchronously, 345 // we probably don't want this: they should be sent asynchronously so 346 // the caller does not get reentered. 347 job_->Start(); 348} 349 350void URLRequest::Restart() { 351 // Should only be called if the original job didn't make any progress. 352 DCHECK(job_ && !job_->has_response_started()); 353 RestartWithJob(GetJobManager()->CreateJob(this)); 354} 355 356void URLRequest::RestartWithJob(URLRequestJob *job) { 357 DCHECK(job->request() == this); 358 PrepareToRestart(); 359 StartJob(job); 360} 361 362void URLRequest::Cancel() { 363 DoCancel(net::ERR_ABORTED, net::SSLInfo()); 364} 365 366void URLRequest::SimulateError(int os_error) { 367 DoCancel(os_error, net::SSLInfo()); 368} 369 370void URLRequest::SimulateSSLError(int os_error, const net::SSLInfo& ssl_info) { 371 // This should only be called on a started request. 372 if (!is_pending_ || !job_ || job_->has_response_started()) { 373 NOTREACHED(); 374 return; 375 } 376 DoCancel(os_error, ssl_info); 377} 378 379void URLRequest::DoCancel(int os_error, const net::SSLInfo& ssl_info) { 380 DCHECK(os_error < 0); 381 382 // If the URL request already has an error status, then canceling is a no-op. 383 // Plus, we don't want to change the error status once it has been set. 384 if (status_.is_success()) { 385 status_.set_status(URLRequestStatus::CANCELED); 386 status_.set_os_error(os_error); 387 response_info_.ssl_info = ssl_info; 388 } 389 390 // There's nothing to do if we are not waiting on a Job. 391 if (!is_pending_ || !job_) 392 return; 393 394 job_->Kill(); 395 396 // The Job will call our NotifyDone method asynchronously. This is done so 397 // that the Delegate implementation can call Cancel without having to worry 398 // about being called recursively. 399} 400 401bool URLRequest::Read(net::IOBuffer* dest, int dest_size, int* bytes_read) { 402 DCHECK(job_); 403 DCHECK(bytes_read); 404 DCHECK(!job_->is_done()); 405 *bytes_read = 0; 406 407 if (dest_size == 0) { 408 // Caller is not too bright. I guess we've done what they asked. 409 return true; 410 } 411 412 // Once the request fails or is cancelled, read will just return 0 bytes 413 // to indicate end of stream. 414 if (!status_.is_success()) { 415 return true; 416 } 417 418 return job_->Read(dest, dest_size, bytes_read); 419} 420 421void URLRequest::StopCaching() { 422 DCHECK(job_); 423 job_->StopCaching(); 424} 425 426void URLRequest::ReceivedRedirect(const GURL& location, bool* defer_redirect) { 427 URLRequestJob* job = GetJobManager()->MaybeInterceptRedirect(this, location); 428 if (job) { 429 RestartWithJob(job); 430 } else if (delegate_) { 431 delegate_->OnReceivedRedirect(this, location, defer_redirect); 432 } 433} 434 435void URLRequest::ResponseStarted() { 436 scoped_refptr<net::NetLog::EventParameters> params; 437 if (!status_.is_success()) 438 params = new net::NetLogIntegerParameter("net_error", status_.os_error()); 439 net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, params); 440 441 URLRequestJob* job = GetJobManager()->MaybeInterceptResponse(this); 442 if (job) { 443 RestartWithJob(job); 444 } else if (delegate_) { 445 delegate_->OnResponseStarted(this); 446 } 447} 448 449void URLRequest::FollowDeferredRedirect() { 450 CHECK(job_); 451 CHECK(status_.is_success()); 452 453 job_->FollowDeferredRedirect(); 454} 455 456void URLRequest::SetAuth(const string16& username, const string16& password) { 457 DCHECK(job_); 458 DCHECK(job_->NeedsAuth()); 459 460 job_->SetAuth(username, password); 461} 462 463void URLRequest::CancelAuth() { 464 DCHECK(job_); 465 DCHECK(job_->NeedsAuth()); 466 467 job_->CancelAuth(); 468} 469 470void URLRequest::ContinueWithCertificate(net::X509Certificate* client_cert) { 471 DCHECK(job_); 472 473 job_->ContinueWithCertificate(client_cert); 474} 475 476void URLRequest::ContinueDespiteLastError() { 477 DCHECK(job_); 478 479 job_->ContinueDespiteLastError(); 480} 481 482void URLRequest::PrepareToRestart() { 483 DCHECK(job_); 484 485 // Close the current URL_REQUEST_START_JOB, since we will be starting a new 486 // one. 487 net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, NULL); 488 489 job_->Kill(); 490 OrphanJob(); 491 492 response_info_ = net::HttpResponseInfo(); 493 status_ = URLRequestStatus(); 494 is_pending_ = false; 495} 496 497void URLRequest::OrphanJob() { 498 job_->Kill(); 499 job_->DetachRequest(); // ensures that the job will not call us again 500 job_ = NULL; 501} 502 503int URLRequest::Redirect(const GURL& location, int http_status_code) { 504 if (net_log_.IsLoggingAllEvents()) { 505 net_log_.AddEvent( 506 net::NetLog::TYPE_URL_REQUEST_REDIRECTED, 507 make_scoped_refptr(new net::NetLogStringParameter( 508 "location", location.possibly_invalid_spec()))); 509 } 510 if (redirect_limit_ <= 0) { 511 DVLOG(1) << "disallowing redirect: exceeds limit"; 512 return net::ERR_TOO_MANY_REDIRECTS; 513 } 514 515 if (!location.is_valid()) 516 return net::ERR_INVALID_URL; 517 518 if (!job_->IsSafeRedirect(location)) { 519 DVLOG(1) << "disallowing redirect: unsafe protocol"; 520 return net::ERR_UNSAFE_REDIRECT; 521 } 522 523 bool strip_post_specific_headers = false; 524 if (http_status_code != 307) { 525 // NOTE: Even though RFC 2616 says to preserve the request method when 526 // following a 302 redirect, normal browsers don't do that. Instead, they 527 // all convert a POST into a GET in response to a 302 and so shall we. For 528 // 307 redirects, browsers preserve the method. The RFC says to prompt the 529 // user to confirm the generation of a new POST request, but IE omits this 530 // prompt and so shall we. 531 strip_post_specific_headers = method_ == "POST"; 532 method_ = "GET"; 533 upload_ = NULL; 534 } 535 536 // Suppress the referrer if we're redirecting out of https. 537 if (GURL(referrer_).SchemeIsSecure() && !location.SchemeIsSecure()) 538 referrer_.clear(); 539 540 url_ = location; 541 --redirect_limit_; 542 543 if (strip_post_specific_headers) { 544 // If being switched from POST to GET, must remove headers that were 545 // specific to the POST and don't have meaning in GET. For example 546 // the inclusion of a multipart Content-Type header in GET can cause 547 // problems with some servers: 548 // http://code.google.com/p/chromium/issues/detail?id=843 549 StripPostSpecificHeaders(&extra_request_headers_); 550 } 551 552 if (!final_upload_progress_) 553 final_upload_progress_ = job_->GetUploadProgress(); 554 555 PrepareToRestart(); 556 Start(); 557 return net::OK; 558} 559 560URLRequestContext* URLRequest::context() { 561 return context_.get(); 562} 563 564void URLRequest::set_context(URLRequestContext* context) { 565 scoped_refptr<URLRequestContext> prev_context = context_; 566 567 context_ = context; 568 569 // If the context this request belongs to has changed, update the tracker. 570 if (prev_context != context) { 571 net_log_.EndEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL); 572 net_log_ = net::BoundNetLog(); 573 574 if (context) { 575 net_log_ = net::BoundNetLog::Make(context->net_log(), 576 net::NetLog::SOURCE_URL_REQUEST); 577 net_log_.BeginEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL); 578 } 579 } 580} 581 582int64 URLRequest::GetExpectedContentSize() const { 583 int64 expected_content_size = -1; 584 if (job_) 585 expected_content_size = job_->expected_content_size(); 586 587 return expected_content_size; 588} 589 590URLRequest::UserData* URLRequest::GetUserData(const void* key) const { 591 UserDataMap::const_iterator found = user_data_.find(key); 592 if (found != user_data_.end()) 593 return found->second.get(); 594 return NULL; 595} 596 597void URLRequest::SetUserData(const void* key, UserData* data) { 598 user_data_[key] = linked_ptr<UserData>(data); 599} 600