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