summaryrefslogtreecommitdiffstats
path: root/chrome/common/net/url_fetcher.cc
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 21:39:55 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 21:39:55 +0000
commit68d2a05fb6380f35eb10e7e1b9f789a742b89871 (patch)
tree74939817681c52027083ef86ebba375f047f5228 /chrome/common/net/url_fetcher.cc
parent527498450ae8b478b6719d4603be03dfcdc8d812 (diff)
downloadchromium_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.cc361
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();
+}