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