url_fetcher_core.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 "net/url_request/url_fetcher_core.h" 6 7#include "base/bind.h" 8#include "base/file_util_proxy.h" 9#include "base/logging.h" 10#include "base/single_thread_task_runner.h" 11#include "base/metrics/histogram.h" 12#include "base/stl_util.h" 13#include "base/thread_task_runner_handle.h" 14#include "base/tracked_objects.h" 15#include "net/base/io_buffer.h" 16#include "net/base/load_flags.h" 17#include "net/base/net_errors.h" 18#include "net/http/http_response_headers.h" 19#include "net/url_request/url_fetcher_delegate.h" 20#include "net/url_request/url_request_context.h" 21#include "net/url_request/url_request_context_getter.h" 22#include "net/url_request/url_request_throttler_manager.h" 23 24namespace { 25 26const int kBufferSize = 4096; 27const int kUploadProgressTimerInterval = 100; 28bool g_interception_enabled = false; 29 30} // namespace 31 32namespace net { 33 34// URLFetcherCore::Registry --------------------------------------------------- 35 36URLFetcherCore::Registry::Registry() {} 37URLFetcherCore::Registry::~Registry() {} 38 39void URLFetcherCore::Registry::AddURLFetcherCore(URLFetcherCore* core) { 40 DCHECK(!ContainsKey(fetchers_, core)); 41 fetchers_.insert(core); 42} 43 44void URLFetcherCore::Registry::RemoveURLFetcherCore(URLFetcherCore* core) { 45 DCHECK(ContainsKey(fetchers_, core)); 46 fetchers_.erase(core); 47} 48 49void URLFetcherCore::Registry::CancelAll() { 50 while (!fetchers_.empty()) 51 (*fetchers_.begin())->CancelURLRequest(); 52} 53 54 55// URLFetcherCore::FileWriter ------------------------------------------------- 56 57URLFetcherCore::FileWriter::FileWriter( 58 URLFetcherCore* core, 59 scoped_refptr<base::TaskRunner> file_task_runner) 60 : core_(core), 61 error_code_(base::PLATFORM_FILE_OK), 62 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 63 file_task_runner_(file_task_runner), 64 file_handle_(base::kInvalidPlatformFileValue) { 65} 66 67URLFetcherCore::FileWriter::~FileWriter() { 68 CloseAndDeleteFile(); 69} 70 71void URLFetcherCore::FileWriter::CreateFileAtPath( 72 const FilePath& file_path) { 73 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 74 DCHECK(file_task_runner_.get()); 75 base::FileUtilProxy::CreateOrOpen( 76 file_task_runner_, 77 file_path, 78 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE, 79 base::Bind(&URLFetcherCore::FileWriter::DidCreateFile, 80 weak_factory_.GetWeakPtr(), 81 file_path)); 82} 83 84void URLFetcherCore::FileWriter::CreateTempFile() { 85 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 86 DCHECK(file_task_runner_.get()); 87 base::FileUtilProxy::CreateTemporary( 88 file_task_runner_, 89 0, // No additional file flags. 90 base::Bind(&URLFetcherCore::FileWriter::DidCreateTempFile, 91 weak_factory_.GetWeakPtr())); 92} 93 94void URLFetcherCore::FileWriter::WriteBuffer(int num_bytes) { 95 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 96 97 // Start writing to the file by setting the initial state 98 // of |pending_bytes_| and |buffer_offset_| to indicate that the 99 // entire buffer has not yet been written. 100 pending_bytes_ = num_bytes; 101 buffer_offset_ = 0; 102 ContinueWrite(base::PLATFORM_FILE_OK, 0); 103} 104 105void URLFetcherCore::FileWriter::ContinueWrite( 106 base::PlatformFileError error_code, 107 int bytes_written) { 108 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 109 110 if (file_handle_ == base::kInvalidPlatformFileValue) { 111 // While a write was being done on the file thread, a request 112 // to close or disown the file occured on the IO thread. At 113 // this point a request to close the file is pending on the 114 // file thread. 115 return; 116 } 117 118 // Every code path that resets |core_->request_| should reset 119 // |core->file_writer_| or cause the file writer to disown the file. In the 120 // former case, this callback can not be called, because the weak pointer to 121 // |this| will be NULL. In the latter case, the check of |file_handle_| at the 122 // start of this method ensures that we can not reach this point. 123 CHECK(core_->request_.get()); 124 125 if (base::PLATFORM_FILE_OK != error_code) { 126 error_code_ = error_code; 127 CloseAndDeleteFile(); 128 core_->delegate_task_runner_->PostTask( 129 FROM_HERE, 130 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, core_)); 131 return; 132 } 133 134 total_bytes_written_ += bytes_written; 135 buffer_offset_ += bytes_written; 136 pending_bytes_ -= bytes_written; 137 138 if (pending_bytes_ > 0) { 139 base::FileUtilProxy::Write( 140 file_task_runner_, file_handle_, 141 total_bytes_written_, // Append to the end 142 (core_->buffer_->data() + buffer_offset_), pending_bytes_, 143 base::Bind(&URLFetcherCore::FileWriter::ContinueWrite, 144 weak_factory_.GetWeakPtr())); 145 } else { 146 // Finished writing core_->buffer_ to the file. Read some more. 147 core_->ReadResponse(); 148 } 149} 150 151void URLFetcherCore::FileWriter::DisownFile() { 152 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 153 154 // Disowning is done by the delegate's OnURLFetchComplete method. 155 // The file should be closed by the time that method is called. 156 DCHECK(file_handle_ == base::kInvalidPlatformFileValue); 157 158 // Forget about any file by reseting the path. 159 file_path_.clear(); 160} 161 162void URLFetcherCore::FileWriter::CloseFileAndCompleteRequest() { 163 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 164 165 if (file_handle_ != base::kInvalidPlatformFileValue) { 166 base::FileUtilProxy::Close( 167 file_task_runner_, file_handle_, 168 base::Bind(&URLFetcherCore::FileWriter::DidCloseFile, 169 weak_factory_.GetWeakPtr())); 170 file_handle_ = base::kInvalidPlatformFileValue; 171 } 172} 173 174void URLFetcherCore::FileWriter::CloseAndDeleteFile() { 175 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 176 177 if (file_handle_ == base::kInvalidPlatformFileValue) { 178 DeleteFile(base::PLATFORM_FILE_OK); 179 return; 180 } 181 // Close the file if it is open. 182 base::FileUtilProxy::Close( 183 file_task_runner_, file_handle_, 184 base::Bind(&URLFetcherCore::FileWriter::DeleteFile, 185 weak_factory_.GetWeakPtr())); 186 file_handle_ = base::kInvalidPlatformFileValue; 187} 188 189void URLFetcherCore::FileWriter::DeleteFile( 190 base::PlatformFileError error_code) { 191 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 192 if (file_path_.empty()) 193 return; 194 195 base::FileUtilProxy::Delete( 196 file_task_runner_, file_path_, 197 false, // No need to recurse, as the path is to a file. 198 base::FileUtilProxy::StatusCallback()); 199 DisownFile(); 200} 201 202void URLFetcherCore::FileWriter::DidCreateFile( 203 const FilePath& file_path, 204 base::PlatformFileError error_code, 205 base::PassPlatformFile file_handle, 206 bool created) { 207 DidCreateFileInternal(file_path, error_code, file_handle); 208} 209 210void URLFetcherCore::FileWriter::DidCreateTempFile( 211 base::PlatformFileError error_code, 212 base::PassPlatformFile file_handle, 213 const FilePath& file_path) { 214 DidCreateFileInternal(file_path, error_code, file_handle); 215} 216 217void URLFetcherCore::FileWriter::DidCreateFileInternal( 218 const FilePath& file_path, 219 base::PlatformFileError error_code, 220 base::PassPlatformFile file_handle) { 221 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 222 223 if (base::PLATFORM_FILE_OK != error_code) { 224 error_code_ = error_code; 225 CloseAndDeleteFile(); 226 core_->delegate_task_runner_->PostTask( 227 FROM_HERE, 228 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, core_)); 229 return; 230 } 231 232 file_path_ = file_path; 233 file_handle_ = file_handle.ReleaseValue(); 234 total_bytes_written_ = 0; 235 236 core_->network_task_runner_->PostTask( 237 FROM_HERE, 238 base::Bind(&URLFetcherCore::StartURLRequestWhenAppropriate, core_)); 239} 240 241void URLFetcherCore::FileWriter::DidCloseFile( 242 base::PlatformFileError error_code) { 243 DCHECK(core_->network_task_runner_->BelongsToCurrentThread()); 244 245 if (base::PLATFORM_FILE_OK != error_code) { 246 error_code_ = error_code; 247 CloseAndDeleteFile(); 248 core_->delegate_task_runner_->PostTask( 249 FROM_HERE, 250 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, core_)); 251 return; 252 } 253 254 // If the file was successfully closed, then the URL request is complete. 255 core_->RetryOrCompleteUrlFetch(); 256} 257 258 259// URLFetcherCore ------------------------------------------------------------- 260 261// static 262base::LazyInstance<URLFetcherCore::Registry> 263 URLFetcherCore::g_registry = LAZY_INSTANCE_INITIALIZER; 264 265URLFetcherCore::URLFetcherCore(URLFetcher* fetcher, 266 const GURL& original_url, 267 URLFetcher::RequestType request_type, 268 URLFetcherDelegate* d) 269 : fetcher_(fetcher), 270 original_url_(original_url), 271 request_type_(request_type), 272 delegate_(d), 273 delegate_task_runner_( 274 base::ThreadTaskRunnerHandle::Get()), 275 request_(NULL), 276 load_flags_(LOAD_NORMAL), 277 response_code_(URLFetcher::RESPONSE_CODE_INVALID), 278 buffer_(new IOBuffer(kBufferSize)), 279 url_request_data_key_(NULL), 280 was_fetched_via_proxy_(false), 281 is_chunked_upload_(false), 282 num_retries_(0), 283 was_cancelled_(false), 284 response_destination_(STRING), 285 stop_on_redirect_(false), 286 stopped_on_redirect_(false), 287 automatically_retry_on_5xx_(true), 288 max_retries_(0), 289 current_upload_bytes_(-1), 290 current_response_bytes_(0), 291 total_response_bytes_(-1) { 292 CHECK(original_url_.is_valid()); 293} 294 295void URLFetcherCore::Start() { 296 DCHECK(delegate_task_runner_); 297 DCHECK(request_context_getter_) << "We need an URLRequestContext!"; 298 if (network_task_runner_) { 299 DCHECK_EQ(network_task_runner_, 300 request_context_getter_->GetNetworkTaskRunner()); 301 } else { 302 network_task_runner_ = request_context_getter_->GetNetworkTaskRunner(); 303 } 304 DCHECK(network_task_runner_.get()) << "We need an IO task runner"; 305 306 network_task_runner_->PostTask( 307 FROM_HERE, base::Bind(&URLFetcherCore::StartOnIOThread, this)); 308} 309 310void URLFetcherCore::Stop() { 311 if (delegate_task_runner_) // May be NULL in tests. 312 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 313 314 delegate_ = NULL; 315 fetcher_ = NULL; 316 if (!network_task_runner_.get()) 317 return; 318 if (network_task_runner_->RunsTasksOnCurrentThread()) { 319 CancelURLRequest(); 320 } else { 321 network_task_runner_->PostTask( 322 FROM_HERE, base::Bind(&URLFetcherCore::CancelURLRequest, this)); 323 } 324} 325 326void URLFetcherCore::SetUploadData(const std::string& upload_content_type, 327 const std::string& upload_content) { 328 DCHECK(!is_chunked_upload_); 329 upload_content_type_ = upload_content_type; 330 upload_content_ = upload_content; 331} 332 333void URLFetcherCore::SetChunkedUpload(const std::string& content_type) { 334 DCHECK(is_chunked_upload_ || 335 (upload_content_type_.empty() && 336 upload_content_.empty())); 337 upload_content_type_ = content_type; 338 upload_content_.clear(); 339 is_chunked_upload_ = true; 340} 341 342void URLFetcherCore::AppendChunkToUpload(const std::string& content, 343 bool is_last_chunk) { 344 DCHECK(delegate_task_runner_); 345 DCHECK(network_task_runner_.get()); 346 network_task_runner_->PostTask( 347 FROM_HERE, 348 base::Bind(&URLFetcherCore::CompleteAddingUploadDataChunk, this, content, 349 is_last_chunk)); 350} 351 352void URLFetcherCore::SetLoadFlags(int load_flags) { 353 load_flags_ = load_flags; 354} 355 356int URLFetcherCore::GetLoadFlags() const { 357 return load_flags_; 358} 359 360void URLFetcherCore::SetReferrer(const std::string& referrer) { 361 referrer_ = referrer; 362} 363 364void URLFetcherCore::SetExtraRequestHeaders( 365 const std::string& extra_request_headers) { 366 extra_request_headers_.Clear(); 367 extra_request_headers_.AddHeadersFromString(extra_request_headers); 368} 369 370void URLFetcherCore::AddExtraRequestHeader(const std::string& header_line) { 371 extra_request_headers_.AddHeaderFromString(header_line); 372} 373 374void URLFetcherCore::GetExtraRequestHeaders( 375 HttpRequestHeaders* headers) const { 376 headers->CopyFrom(extra_request_headers_); 377} 378 379void URLFetcherCore::SetRequestContext( 380 URLRequestContextGetter* request_context_getter) { 381 DCHECK(!request_context_getter_); 382 DCHECK(request_context_getter); 383 request_context_getter_ = request_context_getter; 384} 385 386void URLFetcherCore::SetFirstPartyForCookies( 387 const GURL& first_party_for_cookies) { 388 DCHECK(first_party_for_cookies_.is_empty()); 389 first_party_for_cookies_ = first_party_for_cookies; 390} 391 392void URLFetcherCore::SetURLRequestUserData( 393 const void* key, 394 const URLFetcher::CreateDataCallback& create_data_callback) { 395 DCHECK(key); 396 DCHECK(!create_data_callback.is_null()); 397 url_request_data_key_ = key; 398 url_request_create_data_callback_ = create_data_callback; 399} 400 401void URLFetcherCore::SetStopOnRedirect(bool stop_on_redirect) { 402 stop_on_redirect_ = stop_on_redirect; 403} 404 405void URLFetcherCore::SetAutomaticallyRetryOn5xx(bool retry) { 406 automatically_retry_on_5xx_ = retry; 407} 408 409void URLFetcherCore::SetMaxRetries(int max_retries) { 410 max_retries_ = max_retries; 411} 412 413int URLFetcherCore::GetMaxRetries() const { 414 return max_retries_; 415} 416 417base::TimeDelta URLFetcherCore::GetBackoffDelay() const { 418 return backoff_delay_; 419} 420 421void URLFetcherCore::SaveResponseToFileAtPath( 422 const FilePath& file_path, 423 scoped_refptr<base::TaskRunner> file_task_runner) { 424 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 425 file_task_runner_ = file_task_runner; 426 response_destination_ = URLFetcherCore::PERMANENT_FILE; 427 response_destination_file_path_ = file_path; 428} 429 430void URLFetcherCore::SaveResponseToTemporaryFile( 431 scoped_refptr<base::TaskRunner> file_task_runner) { 432 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 433 file_task_runner_ = file_task_runner; 434 response_destination_ = URLFetcherCore::TEMP_FILE; 435} 436 437HttpResponseHeaders* URLFetcherCore::GetResponseHeaders() const { 438 return response_headers_; 439} 440 441// TODO(panayiotis): socket_address_ is written in the IO thread, 442// if this is accessed in the UI thread, this could result in a race. 443// Same for response_headers_ above and was_fetched_via_proxy_ below. 444HostPortPair URLFetcherCore::GetSocketAddress() const { 445 return socket_address_; 446} 447 448bool URLFetcherCore::WasFetchedViaProxy() const { 449 return was_fetched_via_proxy_; 450} 451 452const GURL& URLFetcherCore::GetOriginalURL() const { 453 return original_url_; 454} 455 456const GURL& URLFetcherCore::GetURL() const { 457 return url_; 458} 459 460const URLRequestStatus& URLFetcherCore::GetStatus() const { 461 return status_; 462} 463 464int URLFetcherCore::GetResponseCode() const { 465 return response_code_; 466} 467 468const ResponseCookies& URLFetcherCore::GetCookies() const { 469 return cookies_; 470} 471 472bool URLFetcherCore::FileErrorOccurred( 473 base::PlatformFileError* out_error_code) const { 474 475 // Can't have a file error if no file is being created or written to. 476 if (!file_writer_.get()) 477 return false; 478 479 base::PlatformFileError error_code = file_writer_->error_code(); 480 if (error_code == base::PLATFORM_FILE_OK) 481 return false; 482 483 *out_error_code = error_code; 484 return true; 485} 486 487void URLFetcherCore::ReceivedContentWasMalformed() { 488 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 489 if (network_task_runner_.get()) { 490 network_task_runner_->PostTask( 491 FROM_HERE, base::Bind(&URLFetcherCore::NotifyMalformedContent, this)); 492 } 493} 494 495bool URLFetcherCore::GetResponseAsString( 496 std::string* out_response_string) const { 497 if (response_destination_ != URLFetcherCore::STRING) 498 return false; 499 500 *out_response_string = data_; 501 UMA_HISTOGRAM_MEMORY_KB("UrlFetcher.StringResponseSize", 502 (data_.length() / 1024)); 503 504 return true; 505} 506 507bool URLFetcherCore::GetResponseAsFilePath(bool take_ownership, 508 FilePath* out_response_path) { 509 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 510 const bool destination_is_file = 511 response_destination_ == URLFetcherCore::TEMP_FILE || 512 response_destination_ == URLFetcherCore::PERMANENT_FILE; 513 if (!destination_is_file || !file_writer_.get()) 514 return false; 515 516 *out_response_path = file_writer_->file_path(); 517 518 if (take_ownership) { 519 network_task_runner_->PostTask( 520 FROM_HERE, 521 base::Bind(&URLFetcherCore::DisownFile, this)); 522 } 523 return true; 524} 525 526void URLFetcherCore::OnReceivedRedirect(URLRequest* request, 527 const GURL& new_url, 528 bool* defer_redirect) { 529 DCHECK_EQ(request, request_.get()); 530 DCHECK(network_task_runner_->BelongsToCurrentThread()); 531 if (stop_on_redirect_) { 532 stopped_on_redirect_ = true; 533 url_ = new_url; 534 response_code_ = request_->GetResponseCode(); 535 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); 536 request->Cancel(); 537 OnReadCompleted(request, 0); 538 } 539} 540 541void URLFetcherCore::OnResponseStarted(URLRequest* request) { 542 DCHECK_EQ(request, request_.get()); 543 DCHECK(network_task_runner_->BelongsToCurrentThread()); 544 if (request_->status().is_success()) { 545 response_code_ = request_->GetResponseCode(); 546 response_headers_ = request_->response_headers(); 547 socket_address_ = request_->GetSocketAddress(); 548 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); 549 total_response_bytes_ = request_->GetExpectedContentSize(); 550 } 551 552 ReadResponse(); 553} 554 555void URLFetcherCore::OnReadCompleted(URLRequest* request, 556 int bytes_read) { 557 DCHECK(request == request_); 558 DCHECK(network_task_runner_->BelongsToCurrentThread()); 559 560 if (!stopped_on_redirect_) 561 url_ = request->url(); 562 URLRequestThrottlerManager* throttler_manager = 563 request->context()->throttler_manager(); 564 if (throttler_manager) { 565 url_throttler_entry_ = throttler_manager->RegisterRequestUrl(url_); 566 } 567 568 bool waiting_on_write = false; 569 do { 570 if (!request_->status().is_success() || bytes_read <= 0) 571 break; 572 573 current_response_bytes_ += bytes_read; 574 InformDelegateDownloadProgress(); 575 InformDelegateDownloadDataIfNecessary(bytes_read); 576 577 if (!WriteBuffer(bytes_read)) { 578 // If WriteBuffer() returns false, we have a pending write to 579 // wait on before reading further. 580 waiting_on_write = true; 581 break; 582 } 583 } while (request_->Read(buffer_, kBufferSize, &bytes_read)); 584 585 const URLRequestStatus status = request_->status(); 586 587 if (status.is_success()) 588 request_->GetResponseCookies(&cookies_); 589 590 // See comments re: HEAD requests in ReadResponse(). 591 if ((!status.is_io_pending() && !waiting_on_write) || 592 (request_type_ == URLFetcher::HEAD)) { 593 status_ = status; 594 ReleaseRequest(); 595 596 // If a file is open, close it. 597 if (file_writer_.get()) { 598 // If the file is open, close it. After closing the file, 599 // RetryOrCompleteUrlFetch() will be called. 600 file_writer_->CloseFileAndCompleteRequest(); 601 } else { 602 // Otherwise, complete or retry the URL request directly. 603 RetryOrCompleteUrlFetch(); 604 } 605 } 606} 607 608void URLFetcherCore::CancelAll() { 609 g_registry.Get().CancelAll(); 610} 611 612int URLFetcherCore::GetNumFetcherCores() { 613 return g_registry.Get().size(); 614} 615 616void URLFetcherCore::SetEnableInterceptionForTests(bool enabled) { 617 g_interception_enabled = enabled; 618} 619 620URLFetcherCore::~URLFetcherCore() { 621 // |request_| should be NULL. If not, it's unsafe to delete it here since we 622 // may not be on the IO thread. 623 DCHECK(!request_.get()); 624} 625 626void URLFetcherCore::StartOnIOThread() { 627 DCHECK(network_task_runner_->BelongsToCurrentThread()); 628 629 switch (response_destination_) { 630 case STRING: 631 StartURLRequestWhenAppropriate(); 632 break; 633 634 case PERMANENT_FILE: 635 case TEMP_FILE: 636 DCHECK(file_task_runner_.get()) 637 << "Need to set the file task runner."; 638 639 file_writer_.reset(new FileWriter(this, file_task_runner_)); 640 641 // If the file is successfully created, 642 // URLFetcherCore::StartURLRequestWhenAppropriate() will be called. 643 switch (response_destination_) { 644 case PERMANENT_FILE: 645 file_writer_->CreateFileAtPath(response_destination_file_path_); 646 break; 647 case TEMP_FILE: 648 file_writer_->CreateTempFile(); 649 break; 650 default: 651 NOTREACHED(); 652 } 653 break; 654 655 default: 656 NOTREACHED(); 657 } 658} 659 660void URLFetcherCore::StartURLRequest() { 661 DCHECK(network_task_runner_->BelongsToCurrentThread()); 662 663 if (was_cancelled_) { 664 // Since StartURLRequest() is posted as a *delayed* task, it may 665 // run after the URLFetcher was already stopped. 666 return; 667 } 668 669 DCHECK(request_context_getter_); 670 DCHECK(!request_.get()); 671 672 g_registry.Get().AddURLFetcherCore(this); 673 current_response_bytes_ = 0; 674 request_.reset(request_context_getter_->GetURLRequestContext()->CreateRequest( 675 original_url_, this)); 676 request_->set_stack_trace(stack_trace_); 677 int flags = request_->load_flags() | load_flags_; 678 if (!g_interception_enabled) 679 flags = flags | LOAD_DISABLE_INTERCEPT; 680 681 if (is_chunked_upload_) 682 request_->EnableChunkedUpload(); 683 request_->set_load_flags(flags); 684 request_->set_referrer(referrer_); 685 request_->set_first_party_for_cookies(first_party_for_cookies_.is_empty() ? 686 original_url_ : first_party_for_cookies_); 687 if (url_request_data_key_ && !url_request_create_data_callback_.is_null()) { 688 request_->SetUserData(url_request_data_key_, 689 url_request_create_data_callback_.Run()); 690 } 691 692 switch (request_type_) { 693 case URLFetcher::GET: 694 break; 695 696 case URLFetcher::POST: 697 case URLFetcher::PUT: 698 DCHECK(!upload_content_type_.empty()); 699 700 request_->set_method( 701 request_type_ == URLFetcher::POST ? "POST" : "PUT"); 702 extra_request_headers_.SetHeader(HttpRequestHeaders::kContentType, 703 upload_content_type_); 704 if (!upload_content_.empty()) { 705 request_->AppendBytesToUpload( 706 upload_content_.data(), static_cast<int>(upload_content_.length())); 707 } 708 709 current_upload_bytes_ = -1; 710 // TODO(kinaba): http://crbug.com/118103. Implement upload callback in the 711 // layer and avoid using timer here. 712 upload_progress_checker_timer_.reset( 713 new base::RepeatingTimer<URLFetcherCore>()); 714 upload_progress_checker_timer_->Start( 715 FROM_HERE, 716 base::TimeDelta::FromMilliseconds(kUploadProgressTimerInterval), 717 this, 718 &URLFetcherCore::InformDelegateUploadProgress); 719 break; 720 721 case URLFetcher::HEAD: 722 request_->set_method("HEAD"); 723 break; 724 725 case URLFetcher::DELETE_REQUEST: 726 request_->set_method("DELETE"); 727 break; 728 729 default: 730 NOTREACHED(); 731 } 732 733 if (!extra_request_headers_.IsEmpty()) 734 request_->SetExtraRequestHeaders(extra_request_headers_); 735 736 // There might be data left over from a previous request attempt. 737 data_.clear(); 738 739 // If we are writing the response to a file, the only caller 740 // of this function should have created it and not written yet. 741 DCHECK(!file_writer_.get() || file_writer_->total_bytes_written() == 0); 742 743 request_->Start(); 744} 745 746void URLFetcherCore::StartURLRequestWhenAppropriate() { 747 DCHECK(network_task_runner_->BelongsToCurrentThread()); 748 749 if (was_cancelled_) 750 return; 751 752 DCHECK(request_context_getter_); 753 754 int64 delay = 0LL; 755 if (original_url_throttler_entry_ == NULL) { 756 URLRequestThrottlerManager* manager = 757 request_context_getter_->GetURLRequestContext()->throttler_manager(); 758 if (manager) { 759 original_url_throttler_entry_ = 760 manager->RegisterRequestUrl(original_url_); 761 } 762 } 763 if (original_url_throttler_entry_ != NULL) { 764 delay = original_url_throttler_entry_->ReserveSendingTimeForNextRequest( 765 GetBackoffReleaseTime()); 766 } 767 768 if (delay == 0) { 769 StartURLRequest(); 770 } else { 771 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 772 FROM_HERE, base::Bind(&URLFetcherCore::StartURLRequest, this), 773 base::TimeDelta::FromMilliseconds(delay)); 774 } 775} 776 777void URLFetcherCore::CancelURLRequest() { 778 DCHECK(network_task_runner_->BelongsToCurrentThread()); 779 780 if (request_.get()) { 781 request_->Cancel(); 782 ReleaseRequest(); 783 } 784 // Release the reference to the request context. There could be multiple 785 // references to URLFetcher::Core at this point so it may take a while to 786 // delete the object, but we cannot delay the destruction of the request 787 // context. 788 request_context_getter_ = NULL; 789 first_party_for_cookies_ = GURL(); 790 url_request_data_key_ = NULL; 791 url_request_create_data_callback_.Reset(); 792 was_cancelled_ = true; 793 file_writer_.reset(); 794} 795 796void URLFetcherCore::OnCompletedURLRequest( 797 base::TimeDelta backoff_delay) { 798 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 799 800 // Save the status and backoff_delay so that delegates can read it. 801 if (delegate_) { 802 backoff_delay_ = backoff_delay; 803 InformDelegateFetchIsComplete(); 804 } 805} 806 807void URLFetcherCore::InformDelegateFetchIsComplete() { 808 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 809 if (delegate_) 810 delegate_->OnURLFetchComplete(fetcher_); 811} 812 813void URLFetcherCore::NotifyMalformedContent() { 814 DCHECK(network_task_runner_->BelongsToCurrentThread()); 815 if (url_throttler_entry_ != NULL) { 816 int status_code = response_code_; 817 if (status_code == URLFetcher::RESPONSE_CODE_INVALID) { 818 // The status code will generally be known by the time clients 819 // call the |ReceivedContentWasMalformed()| function (which ends up 820 // calling the current function) but if it's not, we need to assume 821 // the response was successful so that the total failure count 822 // used to calculate exponential back-off goes up. 823 status_code = 200; 824 } 825 url_throttler_entry_->ReceivedContentWasMalformed(status_code); 826 } 827} 828 829void URLFetcherCore::RetryOrCompleteUrlFetch() { 830 DCHECK(network_task_runner_->BelongsToCurrentThread()); 831 base::TimeDelta backoff_delay; 832 833 // Checks the response from server. 834 if (response_code_ >= 500 || 835 status_.error() == ERR_TEMPORARILY_THROTTLED) { 836 // When encountering a server error, we will send the request again 837 // after backoff time. 838 ++num_retries_; 839 840 // Note that backoff_delay may be 0 because (a) the 841 // URLRequestThrottlerManager and related code does not 842 // necessarily back off on the first error, (b) it only backs off 843 // on some of the 5xx status codes, (c) not all URLRequestContexts 844 // have a throttler manager. 845 base::TimeTicks backoff_release_time = GetBackoffReleaseTime(); 846 backoff_delay = backoff_release_time - base::TimeTicks::Now(); 847 if (backoff_delay < base::TimeDelta()) 848 backoff_delay = base::TimeDelta(); 849 850 if (automatically_retry_on_5xx_ && num_retries_ <= max_retries_) { 851 StartOnIOThread(); 852 return; 853 } 854 } else { 855 backoff_delay = base::TimeDelta(); 856 } 857 request_context_getter_ = NULL; 858 first_party_for_cookies_ = GURL(); 859 url_request_data_key_ = NULL; 860 url_request_create_data_callback_.Reset(); 861 bool posted = delegate_task_runner_->PostTask( 862 FROM_HERE, 863 base::Bind(&URLFetcherCore::OnCompletedURLRequest, this, backoff_delay)); 864 865 // If the delegate message loop does not exist any more, then the delegate 866 // should be gone too. 867 DCHECK(posted || !delegate_); 868} 869 870void URLFetcherCore::ReleaseRequest() { 871 upload_progress_checker_timer_.reset(); 872 request_.reset(); 873 g_registry.Get().RemoveURLFetcherCore(this); 874} 875 876base::TimeTicks URLFetcherCore::GetBackoffReleaseTime() { 877 DCHECK(network_task_runner_->BelongsToCurrentThread()); 878 879 if (original_url_throttler_entry_) { 880 base::TimeTicks original_url_backoff = 881 original_url_throttler_entry_->GetExponentialBackoffReleaseTime(); 882 base::TimeTicks destination_url_backoff; 883 if (url_throttler_entry_ != NULL && 884 original_url_throttler_entry_ != url_throttler_entry_) { 885 destination_url_backoff = 886 url_throttler_entry_->GetExponentialBackoffReleaseTime(); 887 } 888 889 return original_url_backoff > destination_url_backoff ? 890 original_url_backoff : destination_url_backoff; 891 } else { 892 return base::TimeTicks(); 893 } 894} 895 896void URLFetcherCore::CompleteAddingUploadDataChunk( 897 const std::string& content, bool is_last_chunk) { 898 if (was_cancelled_) { 899 // Since CompleteAddingUploadDataChunk() is posted as a *delayed* task, it 900 // may run after the URLFetcher was already stopped. 901 return; 902 } 903 DCHECK(is_chunked_upload_); 904 DCHECK(request_.get()); 905 DCHECK(!content.empty()); 906 request_->AppendChunkToUpload(content.data(), 907 static_cast<int>(content.length()), 908 is_last_chunk); 909} 910 911// Return true if the write was done and reading may continue. 912// Return false if the write is pending, and the next read will 913// be done later. 914bool URLFetcherCore::WriteBuffer(int num_bytes) { 915 bool write_complete = false; 916 switch (response_destination_) { 917 case STRING: 918 data_.append(buffer_->data(), num_bytes); 919 write_complete = true; 920 break; 921 922 case PERMANENT_FILE: 923 case TEMP_FILE: 924 file_writer_->WriteBuffer(num_bytes); 925 // WriteBuffer() sends a request the file thread. 926 // The write is not done yet. 927 write_complete = false; 928 break; 929 930 default: 931 NOTREACHED(); 932 } 933 return write_complete; 934} 935 936void URLFetcherCore::ReadResponse() { 937 // Some servers may treat HEAD requests as GET requests. To free up the 938 // network connection as soon as possible, signal that the request has 939 // completed immediately, without trying to read any data back (all we care 940 // about is the response code and headers, which we already have). 941 int bytes_read = 0; 942 if (request_->status().is_success() && 943 (request_type_ != URLFetcher::HEAD)) 944 request_->Read(buffer_, kBufferSize, &bytes_read); 945 OnReadCompleted(request_.get(), bytes_read); 946} 947 948void URLFetcherCore::DisownFile() { 949 file_writer_->DisownFile(); 950} 951 952void URLFetcherCore::InformDelegateUploadProgress() { 953 DCHECK(network_task_runner_->BelongsToCurrentThread()); 954 if (request_.get()) { 955 int64 current = request_->GetUploadProgress().position(); 956 if (current_upload_bytes_ != current) { 957 current_upload_bytes_ = current; 958 int64 total = -1; 959 if (!is_chunked_upload_) 960 total = static_cast<int64>(upload_content_.size()); 961 delegate_task_runner_->PostTask( 962 FROM_HERE, 963 base::Bind( 964 &URLFetcherCore::InformDelegateUploadProgressInDelegateThread, 965 this, current, total)); 966 } 967 } 968} 969 970void URLFetcherCore::InformDelegateUploadProgressInDelegateThread( 971 int64 current, int64 total) { 972 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 973 if (delegate_) 974 delegate_->OnURLFetchUploadProgress(fetcher_, current, total); 975} 976 977void URLFetcherCore::InformDelegateDownloadProgress() { 978 DCHECK(network_task_runner_->BelongsToCurrentThread()); 979 delegate_task_runner_->PostTask( 980 FROM_HERE, 981 base::Bind( 982 &URLFetcherCore::InformDelegateDownloadProgressInDelegateThread, 983 this, current_response_bytes_, total_response_bytes_)); 984} 985 986void URLFetcherCore::InformDelegateDownloadProgressInDelegateThread( 987 int64 current, int64 total) { 988 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 989 if (delegate_) 990 delegate_->OnURLFetchDownloadProgress(fetcher_, current, total); 991} 992 993void URLFetcherCore::InformDelegateDownloadDataIfNecessary(int bytes_read) { 994 DCHECK(network_task_runner_->BelongsToCurrentThread()); 995 if (delegate_ && delegate_->ShouldSendDownloadData()) { 996 scoped_ptr<std::string> download_data( 997 new std::string(buffer_->data(), bytes_read)); 998 delegate_task_runner_->PostTask( 999 FROM_HERE, 1000 base::Bind( 1001 &URLFetcherCore::InformDelegateDownloadDataInDelegateThread, 1002 this, base::Passed(&download_data))); 1003 } 1004} 1005 1006void URLFetcherCore::InformDelegateDownloadDataInDelegateThread( 1007 scoped_ptr<std::string> download_data) { 1008 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 1009 if (delegate_) 1010 delegate_->OnURLFetchDownloadData(fetcher_, download_data.Pass()); 1011} 1012 1013} // namespace net 1014