diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 21:39:55 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 21:39:55 +0000 |
commit | 68d2a05fb6380f35eb10e7e1b9f789a742b89871 (patch) | |
tree | 74939817681c52027083ef86ebba375f047f5228 /chrome/common/net/url_fetcher.cc | |
parent | 527498450ae8b478b6719d4603be03dfcdc8d812 (diff) | |
download | chromium_src-68d2a05fb6380f35eb10e7e1b9f789a742b89871.zip chromium_src-68d2a05fb6380f35eb10e7e1b9f789a742b89871.tar.gz chromium_src-68d2a05fb6380f35eb10e7e1b9f789a742b89871.tar.bz2 |
Moved URLFetcher and related classes to chrome/common/net.
BUG=None
TEST=Build and Unittests
Review URL: http://codereview.chromium.org/1995008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46739 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/net/url_fetcher.cc')
-rw-r--r-- | chrome/common/net/url_fetcher.cc | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/chrome/common/net/url_fetcher.cc b/chrome/common/net/url_fetcher.cc new file mode 100644 index 0000000..042ff2f --- /dev/null +++ b/chrome/common/net/url_fetcher.cc @@ -0,0 +1,361 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/net/url_fetcher.h" + +#include "base/compiler_specific.h" +#include "base/message_loop_proxy.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/net/url_fetcher_protect.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "googleurl/src/gurl.h" +#include "net/base/load_flags.h" +#include "net/base/io_buffer.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" + +static const int kBufferSize = 4096; + +bool URLFetcher::g_interception_enabled = false; + +class URLFetcher::Core + : public base::RefCountedThreadSafe<URLFetcher::Core>, + public URLRequest::Delegate { + public: + // For POST requests, set |content_type| to the MIME type of the content + // and set |content| to the data to upload. |flags| are flags to apply to + // the load operation--these should be one or more of the LOAD_* flags + // defined in url_request.h. + Core(URLFetcher* fetcher, + const GURL& original_url, + RequestType request_type, + URLFetcher::Delegate* d); + + // Starts the load. It's important that this not happen in the constructor + // because it causes the IO thread to begin AddRef()ing and Release()ing + // us. If our caller hasn't had time to fully construct us and take a + // reference, the IO thread could interrupt things, run a task, Release() + // us, and destroy us, leaving the caller with an already-destroyed object + // when construction finishes. + void Start(); + + // Stops any in-progress load and ensures no callback will happen. It is + // safe to call this multiple times. + void Stop(); + + // URLRequest::Delegate implementations + virtual void OnResponseStarted(URLRequest* request); + virtual void OnReadCompleted(URLRequest* request, int bytes_read); + + URLFetcher::Delegate* delegate() const { return delegate_; } + + private: + friend class base::RefCountedThreadSafe<URLFetcher::Core>; + + ~Core() {} + + // Wrapper functions that allow us to ensure actions happen on the right + // thread. + void StartURLRequest(); + void CancelURLRequest(); + void OnCompletedURLRequest(const URLRequestStatus& status); + + URLFetcher* fetcher_; // Corresponding fetcher object + GURL original_url_; // The URL we were asked to fetch + GURL url_; // The URL we eventually wound up at + RequestType request_type_; // What type of request is this? + URLFetcher::Delegate* delegate_; // Object to notify on completion + MessageLoop* delegate_loop_; // Message loop of the creating thread + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + // The message loop proxy for the thread + // on which the request IO happens. + URLRequest* request_; // The actual request this wraps + int load_flags_; // Flags for the load operation + int response_code_; // HTTP status code for the request + std::string data_; // Results of the request + scoped_refptr<net::IOBuffer> buffer_; + // Read buffer + scoped_refptr<URLRequestContextGetter> request_context_getter_; + // Cookie/cache info for the request + ResponseCookies cookies_; // Response cookies + std::string extra_request_headers_;// Extra headers for the request, if any + scoped_refptr<net::HttpResponseHeaders> response_headers_; + + std::string upload_content_; // HTTP POST payload + std::string upload_content_type_; // MIME type of POST payload + + // The overload protection entry for this URL. This is used to + // incrementally back off how rapidly we'll send requests to a particular + // URL, to avoid placing too much demand on the remote resource. We update + // this with the status of all requests as they return, and in turn use it + // to determine how long to wait before making another request. + URLFetcherProtectEntry* protect_entry_; + // |num_retries_| indicates how many times we've failed to successfully + // fetch this URL. Once this value exceeds the maximum number of retries + // specified by the protection manager, we'll give up. + int num_retries_; + + // True if the URLFetcher has been cancelled. + bool was_cancelled_; + + friend class URLFetcher; + DISALLOW_COPY_AND_ASSIGN(Core); +}; + +// static +URLFetcher::Factory* URLFetcher::factory_ = NULL; + +URLFetcher::URLFetcher(const GURL& url, + RequestType request_type, + Delegate* d) + : ALLOW_THIS_IN_INITIALIZER_LIST( + core_(new Core(this, url, request_type, d))), + automatically_retry_on_5xx_(true) { +} + +URLFetcher::~URLFetcher() { + core_->Stop(); +} + +// static +URLFetcher* URLFetcher::Create(int id, const GURL& url, + RequestType request_type, Delegate* d) { + return factory_ ? factory_->CreateURLFetcher(id, url, request_type, d) : + new URLFetcher(url, request_type, d); +} + +URLFetcher::Core::Core(URLFetcher* fetcher, + const GURL& original_url, + RequestType request_type, + URLFetcher::Delegate* d) + : fetcher_(fetcher), + original_url_(original_url), + request_type_(request_type), + delegate_(d), + delegate_loop_(MessageLoop::current()), + request_(NULL), + load_flags_(net::LOAD_NORMAL), + response_code_(-1), + buffer_(new net::IOBuffer(kBufferSize)), + protect_entry_(URLFetcherProtectManager::GetInstance()->Register( + original_url_.host())), + num_retries_(0), + was_cancelled_(false) { +} + +void URLFetcher::Core::Start() { + DCHECK(delegate_loop_); + CHECK(request_context_getter_) << "We need an URLRequestContext!"; + io_message_loop_proxy_ = request_context_getter_->GetIOMessageLoopProxy(); + CHECK(io_message_loop_proxy_.get()) << "We need an IO message loop proxy"; + io_message_loop_proxy_->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &Core::StartURLRequest), + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SEND)); +} + +void URLFetcher::Core::Stop() { + DCHECK_EQ(MessageLoop::current(), delegate_loop_); + delegate_ = NULL; + fetcher_ = NULL; + if (io_message_loop_proxy_.get()) { + io_message_loop_proxy_->PostTask( + FROM_HERE, NewRunnableMethod(this, &Core::CancelURLRequest)); + } +} + +void URLFetcher::Core::OnResponseStarted(URLRequest* request) { + DCHECK(request == request_); + DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); + if (request_->status().is_success()) { + response_code_ = request_->GetResponseCode(); + response_headers_ = request_->response_headers(); + } + + int bytes_read = 0; + // Some servers may treat HEAD requests as GET requests. To free up the + // network connection as soon as possible, signal that the request has + // completed immediately, without trying to read any data back (all we care + // about is the response code and headers, which we already have). + if (request_->status().is_success() && (request_type_ != HEAD)) + request_->Read(buffer_, kBufferSize, &bytes_read); + OnReadCompleted(request_, bytes_read); +} + +void URLFetcher::Core::OnReadCompleted(URLRequest* request, int bytes_read) { + DCHECK(request == request_); + DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); + + url_ = request->url(); + + do { + if (!request_->status().is_success() || bytes_read <= 0) + break; + data_.append(buffer_->data(), bytes_read); + } while (request_->Read(buffer_, kBufferSize, &bytes_read)); + + if (request_->status().is_success()) + request_->GetResponseCookies(&cookies_); + + // See comments re: HEAD requests in OnResponseStarted(). + if (!request_->status().is_io_pending() || (request_type_ == HEAD)) { + delegate_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Core::OnCompletedURLRequest, request_->status())); + delete request_; + request_ = NULL; + } +} + +void URLFetcher::Core::StartURLRequest() { + DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); + + if (was_cancelled_) { + // Since StartURLRequest() is posted as a *delayed* task, it may + // run after the URLFetcher was already stopped. + return; + } + + CHECK(request_context_getter_); + DCHECK(!request_); + + request_ = new URLRequest(original_url_, this); + int flags = request_->load_flags() | load_flags_; + if (!g_interception_enabled) { + flags = flags | net::LOAD_DISABLE_INTERCEPT; + } + request_->set_load_flags(flags); + request_->set_context(request_context_getter_->GetURLRequestContext()); + + switch (request_type_) { + case GET: + break; + + case POST: + DCHECK(!upload_content_.empty()); + DCHECK(!upload_content_type_.empty()); + + request_->set_method("POST"); + if (!extra_request_headers_.empty()) + extra_request_headers_ += "\r\n"; + StringAppendF(&extra_request_headers_, + "Content-Type: %s", upload_content_type_.c_str()); + request_->AppendBytesToUpload(upload_content_.data(), + static_cast<int>(upload_content_.size())); + break; + + case HEAD: + request_->set_method("HEAD"); + break; + + default: + NOTREACHED(); + } + + if (!extra_request_headers_.empty()) + request_->SetExtraRequestHeaders(extra_request_headers_); + + request_->Start(); +} + +void URLFetcher::Core::CancelURLRequest() { + DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); + + if (request_) { + request_->Cancel(); + delete request_; + request_ = NULL; + } + // Release the reference to the request context. There could be multiple + // references to URLFetcher::Core at this point so it may take a while to + // delete the object, but we cannot delay the destruction of the request + // context. + request_context_getter_ = NULL; + was_cancelled_ = true; +} + +void URLFetcher::Core::OnCompletedURLRequest(const URLRequestStatus& status) { + DCHECK(MessageLoop::current() == delegate_loop_); + + // Checks the response from server. + if (response_code_ >= 500) { + // When encountering a server error, we will send the request again + // after backoff time. + int64 back_off_time = + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::FAILURE); + if (delegate_) { + fetcher_->backoff_delay_ = + base::TimeDelta::FromMilliseconds(back_off_time); + } + ++num_retries_; + // Restarts the request if we still need to notify the delegate. + if (delegate_) { + if (fetcher_->automatically_retry_on_5xx_ && + num_retries_ <= protect_entry_->max_retries()) { + io_message_loop_proxy_->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &Core::StartURLRequest), back_off_time); + } else { + delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_, + cookies_, data_); + } + } + } else { + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SUCCESS); + if (delegate_) { + fetcher_->backoff_delay_ = base::TimeDelta(); + delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_, + cookies_, data_); + } + } +} + +void URLFetcher::set_upload_data(const std::string& upload_content_type, + const std::string& upload_content) { + core_->upload_content_type_ = upload_content_type; + core_->upload_content_ = upload_content; +} + +const std::string& URLFetcher::upload_data() const { + return core_->upload_content_; +} + +void URLFetcher::set_load_flags(int load_flags) { + core_->load_flags_ = load_flags; +} + +int URLFetcher::load_flags() const { + return core_->load_flags_; +} + +void URLFetcher::set_extra_request_headers( + const std::string& extra_request_headers) { + core_->extra_request_headers_ = extra_request_headers; +} + +void URLFetcher::set_request_context( + URLRequestContextGetter* request_context_getter) { + core_->request_context_getter_ = request_context_getter; +} + +void URLFetcher::set_automatcally_retry_on_5xx(bool retry) { + automatically_retry_on_5xx_ = retry; +} + +net::HttpResponseHeaders* URLFetcher::response_headers() const { + return core_->response_headers_; +} + +void URLFetcher::Start() { + core_->Start(); +} + +const GURL& URLFetcher::url() const { + return core_->url_; +} + +URLFetcher::Delegate* URLFetcher::delegate() const { + return core_->delegate(); +} |