summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to 'content')
-rw-r--r--content/browser/DEPS2
-rw-r--r--content/browser/geolocation/network_location_provider_unittest.cc2
-rw-r--r--content/browser/geolocation/network_location_request.h2
-rw-r--r--content/browser/speech/speech_recognition_request.h2
-rw-r--r--content/browser/speech/speech_recognition_request_unittest.cc2
-rw-r--r--content/browser/speech/speech_recognizer_unittest.cc2
-rw-r--r--content/common/test_url_fetcher_factory.cc196
-rw-r--r--content/common/test_url_fetcher_factory.h227
-rw-r--r--content/common/url_fetcher.cc1041
-rw-r--r--content/common/url_fetcher.h315
-rw-r--r--content/common/url_fetcher_unittest.cc904
-rw-r--r--content/content_common.gypi2
12 files changed, 2690 insertions, 7 deletions
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 038a5bb..3210564 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -78,8 +78,6 @@ include_rules = [
"+chrome/common/chrome_paths.h",
"+chrome/common/logging_chrome.h",
- "+chrome/common/net/url_fetcher.h",
-
# ONLY USED BY TESTS
"+chrome/browser/net/url_request_failed_dns_job.h",
"+chrome/browser/net/url_request_mock_http_job.h",
diff --git a/content/browser/geolocation/network_location_provider_unittest.cc b/content/browser/geolocation/network_location_provider_unittest.cc
index a4983e0..1192531 100644
--- a/content/browser/geolocation/network_location_provider_unittest.cc
+++ b/content/browser/geolocation/network_location_provider_unittest.cc
@@ -7,9 +7,9 @@
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
-#include "chrome/common/net/test_url_fetcher_factory.h"
#include "content/browser/geolocation/fake_access_token_store.h"
#include "content/browser/geolocation/network_location_provider.h"
+#include "content/common/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/geolocation/network_location_request.h b/content/browser/geolocation/network_location_request.h
index bc5d346..5da0e50 100644
--- a/content/browser/geolocation/network_location_request.h
+++ b/content/browser/geolocation/network_location_request.h
@@ -11,8 +11,8 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "chrome/common/net/url_fetcher.h"
#include "content/browser/geolocation/device_data_provider.h"
+#include "content/common/url_fetcher.h"
#include "googleurl/src/gurl.h"
class URLFetcher;
diff --git a/content/browser/speech/speech_recognition_request.h b/content/browser/speech/speech_recognition_request.h
index 9ca9670..c375b0f 100644
--- a/content/browser/speech/speech_recognition_request.h
+++ b/content/browser/speech/speech_recognition_request.h
@@ -11,8 +11,8 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "chrome/common/net/url_fetcher.h"
#include "content/common/speech_input_result.h"
+#include "content/common/url_fetcher.h"
#include "googleurl/src/gurl.h"
class URLFetcher;
diff --git a/content/browser/speech/speech_recognition_request_unittest.cc b/content/browser/speech/speech_recognition_request_unittest.cc
index 0982453..871eb56 100644
--- a/content/browser/speech/speech_recognition_request_unittest.cc
+++ b/content/browser/speech/speech_recognition_request_unittest.cc
@@ -3,8 +3,8 @@
// found in the LICENSE file.
#include "base/utf_string_conversions.h"
-#include "chrome/common/net/test_url_fetcher_factory.h"
#include "content/browser/speech/speech_recognition_request.h"
+#include "content/common/test_url_fetcher_factory.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/speech/speech_recognizer_unittest.cc b/content/browser/speech/speech_recognizer_unittest.cc
index fbb04db..af50c18 100644
--- a/content/browser/speech/speech_recognizer_unittest.cc
+++ b/content/browser/speech/speech_recognizer_unittest.cc
@@ -4,9 +4,9 @@
#include <vector>
-#include "chrome/common/net/test_url_fetcher_factory.h"
#include "content/browser/browser_thread.h"
#include "content/browser/speech/speech_recognizer.h"
+#include "content/common/test_url_fetcher_factory.h"
#include "media/audio/test_audio_input_controller_factory.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_status.h"
diff --git a/content/common/test_url_fetcher_factory.cc b/content/common/test_url_fetcher_factory.cc
new file mode 100644
index 0000000..c92ae0f
--- /dev/null
+++ b/content/common/test_url_fetcher_factory.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2011 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 "content/common/test_url_fetcher_factory.h"
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "net/url_request/url_request_status.h"
+
+TestURLFetcher::TestURLFetcher(int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d)
+ : URLFetcher(url, request_type, d),
+ id_(id),
+ original_url_(url),
+ did_receive_last_chunk_(false) {
+}
+
+TestURLFetcher::~TestURLFetcher() {
+}
+
+void TestURLFetcher::AppendChunkToUpload(const std::string& data,
+ bool is_last_chunk) {
+ DCHECK(!did_receive_last_chunk_);
+ did_receive_last_chunk_ = is_last_chunk;
+ chunks_.push_back(data);
+}
+
+void TestURLFetcher::set_status(const net::URLRequestStatus& status) {
+ fake_status_ = status;
+}
+
+void TestURLFetcher::SetResponseString(const std::string& response) {
+ SetResponseDestinationForTesting(STRING);
+ fake_response_string_ = response;
+}
+
+void TestURLFetcher::SetResponseFilePath(const FilePath& path) {
+ SetResponseDestinationForTesting(TEMP_FILE);
+ fake_response_file_path_ = path;
+}
+
+bool TestURLFetcher::GetResponseAsString(
+ std::string* out_response_string) const {
+ if (GetResponseDestinationForTesting() != STRING)
+ return false;
+
+ *out_response_string = fake_response_string_;
+ return true;
+}
+
+bool TestURLFetcher::GetResponseAsFilePath(
+ bool take_ownership, FilePath* out_response_path) const {
+ if (GetResponseDestinationForTesting() != TEMP_FILE)
+ return false;
+
+ *out_response_path = fake_response_file_path_;
+ return true;
+}
+
+TestURLFetcherFactory::TestURLFetcherFactory() {}
+
+TestURLFetcherFactory::~TestURLFetcherFactory() {}
+
+URLFetcher* TestURLFetcherFactory::CreateURLFetcher(
+ int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d) {
+ TestURLFetcher* fetcher = new TestURLFetcher(id, url, request_type, d);
+ fetchers_[id] = fetcher;
+ return fetcher;
+}
+
+TestURLFetcher* TestURLFetcherFactory::GetFetcherByID(int id) const {
+ Fetchers::const_iterator i = fetchers_.find(id);
+ return i == fetchers_.end() ? NULL : i->second;
+}
+
+void TestURLFetcherFactory::RemoveFetcherFromMap(int id) {
+ Fetchers::iterator i = fetchers_.find(id);
+ DCHECK(i != fetchers_.end());
+ fetchers_.erase(i);
+}
+
+const GURL& TestURLFetcher::url() const {
+ return fake_url_;
+}
+
+const net::URLRequestStatus& TestURLFetcher::status() const {
+ return fake_status_;
+}
+
+int TestURLFetcher::response_code() const {
+ return fake_response_code_;
+}
+
+// This class is used by the FakeURLFetcherFactory below.
+class FakeURLFetcher : public URLFetcher {
+ public:
+ // Normal URL fetcher constructor but also takes in a pre-baked response.
+ FakeURLFetcher(const GURL& url, RequestType request_type, Delegate* d,
+ const std::string& response_data, bool success)
+ : URLFetcher(url, request_type, d),
+ url_(url),
+ response_data_(response_data),
+ success_(success),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+ }
+
+ // Start the request. This will call the given delegate asynchronously
+ // with the pre-baked response as parameter.
+ virtual void Start() {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&FakeURLFetcher::RunDelegate));
+ }
+
+ private:
+ virtual ~FakeURLFetcher() {
+ }
+
+ // This is the method which actually calls the delegate that is passed in the
+ // constructor.
+ void RunDelegate() {
+ net::URLRequestStatus status;
+ status.set_status(success_ ? net::URLRequestStatus::SUCCESS :
+ net::URLRequestStatus::FAILED);
+ delegate()->OnURLFetchComplete(this, url_, status, success_ ? 200 : 500,
+ net::ResponseCookies(), response_data_);
+ }
+
+ // Pre-baked response data and flag which indicates whether the request should
+ // be successful or not.
+ GURL url_;
+ std::string response_data_;
+ bool success_;
+
+ // Method factory used to run the delegate.
+ ScopedRunnableMethodFactory<FakeURLFetcher> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeURLFetcher);
+};
+
+FakeURLFetcherFactory::FakeURLFetcherFactory() {}
+
+FakeURLFetcherFactory::FakeURLFetcherFactory(
+ URLFetcher::Factory* default_factory) : default_factory_(default_factory) {}
+
+FakeURLFetcherFactory::~FakeURLFetcherFactory() {}
+
+URLFetcher* FakeURLFetcherFactory::CreateURLFetcher(
+ int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d) {
+ FakeResponseMap::const_iterator it = fake_responses_.find(url);
+ if (it == fake_responses_.end()) {
+ if (default_factory_ == NULL) {
+ // If we don't have a baked response for that URL we return NULL.
+ DLOG(ERROR) << "No baked response for URL: " << url.spec();
+ return NULL;
+ } else {
+ return default_factory_->CreateURLFetcher(id, url, request_type, d);
+ }
+ }
+ return new FakeURLFetcher(url, request_type, d,
+ it->second.first, it->second.second);
+}
+
+void FakeURLFetcherFactory::SetFakeResponse(const std::string& url,
+ const std::string& response_data,
+ bool success) {
+ // Overwrite existing URL if it already exists.
+ fake_responses_[GURL(url)] = std::make_pair(response_data, success);
+}
+
+void FakeURLFetcherFactory::ClearFakeReponses() {
+ fake_responses_.clear();
+}
+
+URLFetcherFactory::URLFetcherFactory() {}
+
+URLFetcherFactory::~URLFetcherFactory() {}
+
+URLFetcher* URLFetcherFactory::CreateURLFetcher(
+ int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d) {
+ return new URLFetcher(url, request_type, d);
+}
diff --git a/content/common/test_url_fetcher_factory.h b/content/common/test_url_fetcher_factory.h
new file mode 100644
index 0000000..70cf6f8
--- /dev/null
+++ b/content/common/test_url_fetcher_factory.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_TEST_URL_FETCHER_FACTORY_H_
+#define CONTENT_COMMON_TEST_URL_FETCHER_FACTORY_H_
+#pragma once
+
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "content/common/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+#include "googleurl/src/gurl.h"
+
+// TestURLFetcher and TestURLFetcherFactory are used for testing consumers of
+// URLFetcher. TestURLFetcherFactory is a URLFetcher::Factory that creates
+// TestURLFetchers. TestURLFetcher::Start is overriden to do nothing. It is
+// expected that you'll grab the delegate from the TestURLFetcher and invoke
+// the callback method when appropriate. In this way it's easy to mock a
+// URLFetcher.
+// Typical usage:
+// // TestURLFetcher requires a MessageLoop:
+// MessageLoopForUI message_loop;
+// // And io_thread to release URLRequestContextGetter in URLFetcher::Core.
+// BrowserThread io_thread(BrowserThread::IO, &message_loop);
+// // Create and register factory.
+// TestURLFetcherFactory factory;
+// URLFetcher::set_factory(&factory);
+// // Do something that triggers creation of a URLFetcher.
+// TestURLFetcher* fetcher = factory.GetFetcherByID(expected_id);
+// DCHECK(fetcher);
+// // Notify delegate with whatever data you want.
+// fetcher->delegate()->OnURLFetchComplete(...);
+// // Make sure consumer of URLFetcher does the right thing.
+// ...
+// // Reset factory.
+// URLFetcher::set_factory(NULL);
+//
+// Note: if you don't know when your request objects will be created you
+// might want to use the FakeUrlFetcher and FakeUrlFetcherFactory classes
+// below.
+
+class TestURLFetcher : public URLFetcher {
+ public:
+ TestURLFetcher(int id,
+ const GURL& url,
+ RequestType request_type,
+ Delegate* d);
+ virtual ~TestURLFetcher();
+
+ // Overriden to do nothing. It is assumed the caller will notify the delegate.
+ virtual void Start() {}
+
+ // Overriden to cache the chunks uploaded. Caller can read back the uploaded
+ // chunks with the upload_data() accessor.
+ virtual void AppendChunkToUpload(const std::string& data, bool is_last_chunk);
+
+ // Unique ID in our factory.
+ int id() const { return id_; }
+
+ // URL we were created with. Because of how we're using URLFetcher url()
+ // always returns an empty URL. Chances are you'll want to use original_url()
+ // in your tests.
+ const GURL& original_url() const { return original_url_; }
+
+ // Returns the data uploaded on this URLFetcher.
+ const std::string& upload_data() const { return URLFetcher::upload_data(); }
+
+ // Returns the chunks of data uploaded on this URLFetcher.
+ const std::list<std::string>& upload_chunks() const { return chunks_; }
+
+ // Returns the delegate installed on the URLFetcher.
+ Delegate* delegate() const { return URLFetcher::delegate(); }
+
+ void set_url(const GURL& url) { fake_url_ = url; }
+ virtual const GURL& url() const;
+
+ void set_status(const net::URLRequestStatus& status);
+ virtual const net::URLRequestStatus& status() const;
+
+ void set_response_code(int response_code) {
+ fake_response_code_ = response_code;
+ }
+ virtual int response_code() const;
+
+ // Set string data.
+ void SetResponseString(const std::string& response);
+
+ // Set File data.
+ void SetResponseFilePath(const FilePath& path);
+
+ // Override response access functions to return fake data.
+ virtual bool GetResponseAsString(std::string* out_response_string) const;
+ virtual bool GetResponseAsFilePath(bool take_ownership,
+ FilePath* out_response_path) const;
+
+ private:
+ const int id_;
+ const GURL original_url_;
+ std::list<std::string> chunks_;
+ bool did_receive_last_chunk_;
+
+ // User can use set_* methods to provide values returned by getters.
+ // Setting the real values is not possible, because the real class
+ // has no setters. The data is a private member of a class defined
+ // in a .cc file, so we can't get at it with friendship.
+ GURL fake_url_;
+ net::URLRequestStatus fake_status_;
+ int fake_response_code_;
+ std::string fake_response_string_;
+ FilePath fake_response_file_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestURLFetcher);
+};
+
+// Simple URLFetcher::Factory method that creates TestURLFetchers. All fetchers
+// are registered in a map by the id passed to the create method.
+class TestURLFetcherFactory : public URLFetcher::Factory {
+ public:
+ TestURLFetcherFactory();
+ virtual ~TestURLFetcherFactory();
+
+ virtual URLFetcher* CreateURLFetcher(int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d);
+ TestURLFetcher* GetFetcherByID(int id) const;
+ void RemoveFetcherFromMap(int id);
+
+ private:
+ // Maps from id passed to create to the returned URLFetcher.
+ typedef std::map<int, TestURLFetcher*> Fetchers;
+ Fetchers fetchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestURLFetcherFactory);
+};
+
+// The FakeUrlFetcher and FakeUrlFetcherFactory classes are similar to the
+// ones above but don't require you to know when exactly the URLFetcher objects
+// will be created.
+//
+// These classes let you set pre-baked HTTP responses for particular URLs.
+// E.g., if the user requests http://a.com/ then respond with an HTTP/500.
+//
+// We assume that the thread that is calling Start() on the URLFetcher object
+// has a message loop running.
+//
+// This class is not thread-safe. You should not call SetFakeResponse or
+// ClearFakeResponse at the same time you call CreateURLFetcher. However, it is
+// OK to start URLFetcher objects while setting or clearning fake responses
+// since already created URLFetcher objects will not be affected by any changes
+// made to the fake responses (once a URLFetcher object is created you cannot
+// change its fake response).
+//
+// Example usage:
+// FakeURLFetcherFactory factory;
+// URLFetcher::set_factory(&factory);
+//
+// // You know that class SomeService will request url http://a.com/ and you
+// // want to test the service class by returning an error.
+// factory.SetFakeResponse("http://a.com/", "", false);
+// // But if the service requests http://b.com/asdf you want to respond with
+// // a simple html page and an HTTP/200 code.
+// factory.SetFakeResponse("http://b.com/asdf",
+// "<html><body>hello world</body></html>",
+// true);
+//
+// SomeService service;
+// service.Run(); // Will eventually request these two URLs.
+
+class FakeURLFetcherFactory : public URLFetcher::Factory {
+ public:
+ FakeURLFetcherFactory();
+ // FakeURLFetcherFactory that will delegate creating URLFetcher for unknown
+ // url to the given factory.
+ explicit FakeURLFetcherFactory(URLFetcher::Factory* default_factory);
+ virtual ~FakeURLFetcherFactory();
+
+ // If no fake response is set for the given URL this method will delegate the
+ // call to |default_factory_| if it is not NULL, or return NULL if it is
+ // NULL.
+ // Otherwise, it will return a URLFetcher object which will respond with the
+ // pre-baked response that the client has set by calling SetFakeResponse().
+ virtual URLFetcher* CreateURLFetcher(int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d);
+
+ // Sets the fake response for a given URL. If success is true we will serve
+ // an HTTP/200 and an HTTP/500 otherwise. The |response_data| may be empty.
+ void SetFakeResponse(const std::string& url,
+ const std::string& response_data,
+ bool success);
+
+ // Clear all the fake responses that were previously set via
+ // SetFakeResponse().
+ void ClearFakeReponses();
+
+ private:
+ typedef std::map<GURL, std::pair<std::string, bool> > FakeResponseMap;
+ FakeResponseMap fake_responses_;
+ URLFetcher::Factory* default_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeURLFetcherFactory);
+};
+
+// This is an implementation of URLFetcher::Factory that will create a real
+// URLFetcher. It can be use in conjunction with a FakeURLFetcherFactory in
+// integration tests to control the behavior of some requests but execute
+// all the other ones.
+class URLFetcherFactory : public URLFetcher::Factory {
+ public:
+ URLFetcherFactory();
+ virtual ~URLFetcherFactory();
+
+ // This method will create a real URLFetcher.
+ virtual URLFetcher* CreateURLFetcher(int id,
+ const GURL& url,
+ URLFetcher::RequestType request_type,
+ URLFetcher::Delegate* d);
+
+};
+
+#endif // CONTENT_COMMON_TEST_URL_FETCHER_FACTORY_H_
diff --git a/content/common/url_fetcher.cc b/content/common/url_fetcher.cc
new file mode 100644
index 0000000..9706a9d
--- /dev/null
+++ b/content/common/url_fetcher.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2011 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 "content/common/url_fetcher.h"
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util_proxy.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_callback_factory.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/threading/thread.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/load_flags.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/host_port_pair.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_throttler_manager.h"
+
+static const int kBufferSize = 4096;
+const int URLFetcher::kInvalidHttpResponseCode = -1;
+
+class URLFetcher::Core
+ : public base::RefCountedThreadSafe<URLFetcher::Core>,
+ public net::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 net/base/load_flags.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();
+
+ // Reports that the received content was malformed (i.e. failed parsing
+ // or validation). This makes the throttling logic that does exponential
+ // back-off when servers are having problems treat the current request as
+ // a failure. Your call to this method will be ignored if your request is
+ // already considered a failure based on the HTTP response code or response
+ // headers.
+ void ReceivedContentWasMalformed();
+
+ // Overridden from net::URLRequest::Delegate:
+ virtual void OnResponseStarted(net::URLRequest* request);
+ virtual void OnReadCompleted(net::URLRequest* request, int bytes_read);
+
+ URLFetcher::Delegate* delegate() const { return delegate_; }
+ static void CancelAll();
+
+ private:
+ friend class base::RefCountedThreadSafe<URLFetcher::Core>;
+
+ class Registry {
+ public:
+ Registry();
+ ~Registry();
+
+ void AddURLFetcherCore(Core* core);
+ void RemoveURLFetcherCore(Core* core);
+
+ void CancelAll();
+
+ int size() const {
+ return fetchers_.size();
+ }
+
+ private:
+ std::set<Core*> fetchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(Registry);
+ };
+
+ // Class TempFileWriter encapsulates all state involved in writing
+ // response bytes to a temporary file. It is only used if
+ // |Core::response_destination_| == TEMP_FILE.
+ class TempFileWriter {
+ public:
+ TempFileWriter(
+ URLFetcher::Core* core,
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy);
+
+ ~TempFileWriter();
+ void CreateTempFile();
+ void DidCreateTempFile(base::PlatformFileError error_code,
+ base::PassPlatformFile file_handle,
+ FilePath file_path);
+ void DidCloseTempFile(base::PlatformFileError error_code);
+ void DidReopenTempFile(base::PlatformFileError error_code,
+ base::PassPlatformFile file_handle,
+ bool created);
+
+ // Record |num_bytes_| response bytes in |core_->buffer_| to the file.
+ void WriteBuffer(int num_bytes);
+
+ // Called when a write has been done. Continues writing if there are
+ // any more bytes to write. Otherwise, initiates a read in core_.
+ void ContinueWrite(base::PlatformFileError error_code,
+ int bytes_written);
+
+ // Drop ownership of the file at path |temp_file_|. This class
+ // will not delete it or write to it again.
+ void DisownTempFile();
+
+ // Remove any file created.
+ void Destroy();
+
+ const FilePath& temp_file() const { return temp_file_; }
+ int64 total_bytes_written() { return total_bytes_written_; }
+ base::PlatformFileError error_code() const { return error_code_; }
+
+ private:
+ // The URLFetcher::Core which instantiated this class.
+ URLFetcher::Core* core_;
+
+ // The last error encountered on a file operation. base::PLATFORM_FILE_OK
+ // if no error occurred.
+ base::PlatformFileError error_code_;
+
+ // Callbacks are created for use with base::FileUtilProxy.
+ base::ScopedCallbackFactory<URLFetcher::Core::TempFileWriter>
+ callback_factory_;
+
+ // Message loop on which file opperations should happen.
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy_;
+
+ // Path to the temporary file. This path is empty when there
+ // is no temp file.
+ FilePath temp_file_;
+
+ // Handle to the temp file.
+ base::PlatformFile temp_file_handle_;
+
+ // We always append to the file. Track the total number of bytes
+ // written, so that writes know the offset to give.
+ int64 total_bytes_written_;
+
+ // How many bytes did the last Write() try to write? Needed so
+ // that if not all the bytes get written on a Write(), we can
+ // call Write() again with the rest.
+ int pending_bytes_;
+
+ // When writing, how many bytes from the buffer have been successfully
+ // written so far?
+ int buffer_offset_;
+ };
+
+ virtual ~Core();
+
+ // Wrapper functions that allow us to ensure actions happen on the right
+ // thread.
+ void StartURLRequest();
+ void StartURLRequestWhenAppropriate();
+ void CancelURLRequest();
+ void OnCompletedURLRequest(const net::URLRequestStatus& status);
+ void InformDelegateFetchIsComplete();
+ void NotifyMalformedContent();
+
+ // Deletes the request, removes it from the registry, and removes the
+ // destruction observer.
+ void ReleaseRequest();
+
+ // Returns the max value of exponential back-off release time for
+ // |original_url_| and |url_|.
+ base::TimeTicks GetBackoffReleaseTime();
+
+ void CompleteAddingUploadDataChunk(const std::string& data,
+ bool is_last_chunk);
+
+ // Adds a block of data to be uploaded in a POST body. This can only be
+ // called after Start().
+ void AppendChunkToUpload(const std::string& data, bool is_last_chunk);
+
+ // Store the response bytes in |buffer_| in the container indicated by
+ // |response_destination_|. Return true if the write has been
+ // done, and another read can overwrite |buffer_|. If this function
+ // returns false, it will post a task that will read more bytes once the
+ // write is complete.
+ bool WriteBuffer(int num_bytes);
+
+ // Read response bytes from the request.
+ void ReadResponse();
+
+ 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?
+ net::URLRequestStatus status_; // Status of the request
+ URLFetcher::Delegate* delegate_; // Object to notify on completion
+ scoped_refptr<base::MessageLoopProxy> delegate_loop_proxy_;
+ // Message loop proxy 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.
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy_;
+ // The message loop proxy for the thread
+ // on which file access happens.
+ scoped_ptr<net::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, when we are
+ // storing the response as a string.
+ scoped_refptr<net::IOBuffer> buffer_;
+ // Read buffer
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ // Cookie/cache info for the request
+ net::ResponseCookies cookies_; // Response cookies
+ net::HttpRequestHeaders extra_request_headers_;
+ scoped_refptr<net::HttpResponseHeaders> response_headers_;
+ bool was_fetched_via_proxy_;
+ net::HostPortPair socket_address_;
+
+ std::string upload_content_; // HTTP POST payload
+ std::string upload_content_type_; // MIME type of POST payload
+ std::string referrer_; // HTTP Referer header value
+ bool is_chunked_upload_; // True if using chunked transfer encoding
+
+ // Used to determine how long to wait before making a request or doing a
+ // retry.
+ // Both of them can only be accessed on the IO thread.
+ // We need not only the throttler entry for |original_URL|, but also the one
+ // for |url|. For example, consider the case that URL A redirects to URL B,
+ // for which the server returns a 500 response. In this case, the exponential
+ // back-off release time of URL A won't increase. If we retry without
+ // considering the back-off constraint of URL B, we may send out too many
+ // requests for URL A in a short period of time.
+ scoped_refptr<net::URLRequestThrottlerEntryInterface>
+ original_url_throttler_entry_;
+ scoped_refptr<net::URLRequestThrottlerEntryInterface> url_throttler_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 owner URLFetcher instance, we'll give up.
+ int num_retries_;
+
+ // True if the URLFetcher has been cancelled.
+ bool was_cancelled_;
+
+ // Since GetBackoffReleaseTime() can only be called on the IO thread, we cache
+ // its value to be used by OnCompletedURLRequest on the creating thread.
+ base::TimeTicks backoff_release_time_;
+
+ // If writing results to a file, |temp_file_writer_| will manage creation,
+ // writing, and destruction of that file.
+ scoped_ptr<TempFileWriter> temp_file_writer_;
+
+ // Where should responses be saved?
+ ResponseDestinationType response_destination_;
+
+ static base::LazyInstance<Registry> g_registry;
+
+ friend class URLFetcher;
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+URLFetcher::Core::Registry::Registry() {}
+URLFetcher::Core::Registry::~Registry() {}
+
+void URLFetcher::Core::Registry::AddURLFetcherCore(Core* core) {
+ DCHECK(!ContainsKey(fetchers_, core));
+ fetchers_.insert(core);
+}
+
+void URLFetcher::Core::Registry::RemoveURLFetcherCore(Core* core) {
+ DCHECK(ContainsKey(fetchers_, core));
+ fetchers_.erase(core);
+}
+
+void URLFetcher::Core::Registry::CancelAll() {
+ while (!fetchers_.empty())
+ (*fetchers_.begin())->CancelURLRequest();
+}
+
+// static
+base::LazyInstance<URLFetcher::Core::Registry>
+ URLFetcher::Core::g_registry(base::LINKER_INITIALIZED);
+
+URLFetcher::Core::TempFileWriter::TempFileWriter(
+ URLFetcher::Core* core,
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy)
+ : core_(core),
+ error_code_(base::PLATFORM_FILE_OK),
+ callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ file_message_loop_proxy_(file_message_loop_proxy) {
+}
+
+URLFetcher::Core::TempFileWriter::~TempFileWriter() {
+ Destroy();
+}
+
+void URLFetcher::Core::TempFileWriter::CreateTempFile() {
+ CHECK(file_message_loop_proxy_.get());
+ base::FileUtilProxy::CreateTemporary(
+ file_message_loop_proxy_,
+ callback_factory_.NewCallback(
+ &URLFetcher::Core::TempFileWriter::DidCreateTempFile));
+}
+
+void URLFetcher::Core::TempFileWriter::DidCreateTempFile(
+ base::PlatformFileError error_code,
+ base::PassPlatformFile file_handle,
+ FilePath file_path) {
+ if (base::PLATFORM_FILE_OK != error_code) {
+ error_code_ = error_code;
+ core_->InformDelegateFetchIsComplete();
+ return;
+ }
+
+ temp_file_ = file_path;
+
+ // The file was opened with async writes enabled. FileUtilProxy::Write()
+ // treats a write that returns IO_PENDING as an error, and does not inform
+ // the caller. We need to close and reopen the file with asyncronus writes
+ // disabled.
+ // TODO(skerner): Make FileUtilProxy::Write() play nice with async IO.
+ base::FileUtilProxy::Close(
+ file_message_loop_proxy_,
+ file_handle.ReleaseValue(),
+ callback_factory_.NewCallback(
+ &URLFetcher::Core::TempFileWriter::DidCloseTempFile));
+}
+
+void URLFetcher::Core::TempFileWriter::DidCloseTempFile(
+ base::PlatformFileError error_code) {
+ if (base::PLATFORM_FILE_OK != error_code) {
+ error_code_ = error_code;
+ core_->InformDelegateFetchIsComplete();
+ return;
+ }
+
+ int file_flags =
+ base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_TEMPORARY;
+
+ base::FileUtilProxy::CreateOrOpen(
+ file_message_loop_proxy_,
+ temp_file_,
+ file_flags,
+ callback_factory_.NewCallback(
+ &URLFetcher::Core::TempFileWriter::DidReopenTempFile));
+}
+
+void URLFetcher::Core::TempFileWriter::DidReopenTempFile(
+ base::PlatformFileError error_code,
+ base::PassPlatformFile file_handle,
+ bool created) {
+ if (base::PLATFORM_FILE_OK != error_code) {
+ error_code_ = error_code;
+ core_->InformDelegateFetchIsComplete();
+ return;
+ }
+
+ temp_file_handle_ = file_handle.ReleaseValue();
+ total_bytes_written_ = 0;
+
+ core_->io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(core_, &Core::StartURLRequestWhenAppropriate));
+}
+
+void URLFetcher::Core::TempFileWriter::WriteBuffer(int num_bytes) {
+ // Start writing to the temp file by setting the initial state
+ // of |pending_bytes_| and |buffer_offset_| to indicate that the
+ // entire buffer has not yet been written.
+ pending_bytes_ = num_bytes;
+ buffer_offset_ = 0;
+ ContinueWrite(base::PLATFORM_FILE_OK, 0);
+}
+
+void URLFetcher::Core::TempFileWriter::ContinueWrite(
+ base::PlatformFileError error_code,
+ int bytes_written) {
+ if (base::PLATFORM_FILE_OK != error_code) {
+ error_code_ = error_code;
+ core_->InformDelegateFetchIsComplete();
+ return;
+ }
+
+ total_bytes_written_ += bytes_written;
+ buffer_offset_ += bytes_written;
+ pending_bytes_ -= bytes_written;
+
+ if (pending_bytes_ > 0) {
+ base::FileUtilProxy::Write(
+ file_message_loop_proxy_,
+ temp_file_handle_,
+ total_bytes_written_, // Append to the end
+ (core_->buffer_->data() + buffer_offset_),
+ pending_bytes_,
+ callback_factory_.NewCallback(
+ &URLFetcher::Core::TempFileWriter::ContinueWrite));
+ } else {
+ // Finished writing core_->buffer_ to the file. Read some more.
+ core_->ReadResponse();
+ }
+}
+
+void URLFetcher::Core::TempFileWriter::DisownTempFile() {
+ // Forget about any temp file by reseting the path.
+ if (!temp_file_.empty()) {
+ base::FileUtilProxy::Close(
+ file_message_loop_proxy_,
+ temp_file_handle_,
+ NULL);
+ temp_file_ = FilePath();
+ }
+}
+
+void URLFetcher::Core::TempFileWriter::Destroy() {
+ if (!temp_file_.empty()) {
+ base::FileUtilProxy::Close(
+ file_message_loop_proxy_,
+ temp_file_handle_,
+ NULL);
+
+ base::FileUtilProxy::Delete(
+ file_message_loop_proxy_,
+ temp_file_,
+ false, // No need to recurse, as the path is to a file.
+ NULL); // No callback.
+ }
+ temp_file_ = FilePath();
+}
+
+// static
+URLFetcher::Factory* URLFetcher::factory_ = NULL;
+
+void URLFetcher::Delegate::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ NOTREACHED() << "If you don't implemnt this, the no-params version "
+ << "should also be implemented, in which case this "
+ << "method won't be called...";
+}
+
+// TODO(skerner): This default implementation will be removed, and the
+// method made pure virtual, once all users of URLFetcher are updated
+// to not expect response data as a string argument. Once this is removed,
+// the method URLFetcher::GetResponseStringRef() can be removed as well.
+// crbug.com/83592 tracks this.
+void URLFetcher::Delegate::OnURLFetchComplete(const URLFetcher* source) {
+ // A delegate that did not override this method is using the old
+ // parameter list to OnURLFetchComplete(). If a user asked to save
+ // the response to a file, they must use the new parameter list,
+ // in which case we can not get here.
+ // To avoid updating all callers, thunk to the old prototype for now.
+ OnURLFetchComplete(source,
+ source->url(),
+ source->status(),
+ source->response_code(),
+ source->cookies(),
+ source->GetResponseStringRef());
+}
+
+// static
+bool URLFetcher::g_interception_enabled = false;
+
+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),
+ max_retries_(0) {
+}
+
+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_proxy_(
+ base::MessageLoopProxy::CreateForCurrentThread()),
+ request_(NULL),
+ load_flags_(net::LOAD_NORMAL),
+ response_code_(URLFetcher::kInvalidHttpResponseCode),
+ buffer_(new net::IOBuffer(kBufferSize)),
+ is_chunked_upload_(false),
+ num_retries_(0),
+ was_cancelled_(false),
+ response_destination_(STRING) {
+}
+
+URLFetcher::Core::~Core() {
+ // |request_| should be NULL. If not, it's unsafe to delete it here since we
+ // may not be on the IO thread.
+ DCHECK(!request_.get());
+}
+
+void URLFetcher::Core::Start() {
+ DCHECK(delegate_loop_proxy_);
+ 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";
+
+ switch (response_destination_) {
+ case STRING:
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Core::StartURLRequestWhenAppropriate));
+ break;
+
+ case TEMP_FILE:
+ CHECK(file_message_loop_proxy_.get())
+ << "Need to set the file message loop proxy.";
+ temp_file_writer_.reset(
+ new TempFileWriter(this, file_message_loop_proxy_));
+ // CreateTempFile() will invoke Core::StartURLRequestWhenAppropriate
+ // once the file is created.
+ temp_file_writer_->CreateTempFile();
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void URLFetcher::Core::Stop() {
+ DCHECK(delegate_loop_proxy_->BelongsToCurrentThread());
+ delegate_ = NULL;
+ fetcher_ = NULL;
+ if (io_message_loop_proxy_.get()) {
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Core::CancelURLRequest));
+ }
+}
+
+void URLFetcher::Core::ReceivedContentWasMalformed() {
+ DCHECK(delegate_loop_proxy_->BelongsToCurrentThread());
+ if (io_message_loop_proxy_.get()) {
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Core::NotifyMalformedContent));
+ }
+}
+
+void URLFetcher::Core::CancelAll() {
+ g_registry.Get().CancelAll();
+}
+
+void URLFetcher::Core::OnResponseStarted(net::URLRequest* request) {
+ DCHECK_EQ(request, request_.get());
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ if (request_->status().is_success()) {
+ response_code_ = request_->GetResponseCode();
+ response_headers_ = request_->response_headers();
+ socket_address_ = request_->GetSocketAddress();
+ was_fetched_via_proxy_ = request_->was_fetched_via_proxy();
+ }
+
+ ReadResponse();
+}
+
+void URLFetcher::Core::CompleteAddingUploadDataChunk(
+ const std::string& content, bool is_last_chunk) {
+ DCHECK(is_chunked_upload_);
+ DCHECK(request_.get());
+ DCHECK(!content.empty());
+ request_->AppendChunkToUpload(content.data(),
+ static_cast<int>(content.length()),
+ is_last_chunk);
+}
+
+void URLFetcher::Core::AppendChunkToUpload(const std::string& content,
+ bool is_last_chunk) {
+ DCHECK(delegate_loop_proxy_);
+ CHECK(io_message_loop_proxy_.get());
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Core::CompleteAddingUploadDataChunk, content,
+ is_last_chunk));
+}
+
+// Return true if the write was done and reading may continue.
+// Return false if the write is pending, and the next read will
+// be done later.
+bool URLFetcher::Core::WriteBuffer(int num_bytes) {
+ bool write_complete = false;
+ switch (response_destination_) {
+ case STRING:
+ data_.append(buffer_->data(), num_bytes);
+ write_complete = true;
+ break;
+
+ case TEMP_FILE:
+ temp_file_writer_->WriteBuffer(num_bytes);
+ // WriteBuffer() sends a request the file thread.
+ // The write is not done yet.
+ write_complete = false;
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ return write_complete;
+}
+
+void URLFetcher::Core::OnReadCompleted(net::URLRequest* request,
+ int bytes_read) {
+ DCHECK(request == request_);
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+
+ url_ = request->url();
+ url_throttler_entry_ =
+ net::URLRequestThrottlerManager::GetInstance()->RegisterRequestUrl(url_);
+
+ bool waiting_on_write = false;
+ do {
+ if (!request_->status().is_success() || bytes_read <= 0)
+ break;
+
+ if (!WriteBuffer(bytes_read)) {
+ // If WriteBuffer() returns false, we have a pending write to
+ // wait on before reading further.
+ waiting_on_write = true;
+ break;
+ }
+ } while (request_->Read(buffer_, kBufferSize, &bytes_read));
+
+ if (request_->status().is_success())
+ request_->GetResponseCookies(&cookies_);
+
+ // See comments re: HEAD requests in ReadResponse().
+ if ((!request_->status().is_io_pending() && !waiting_on_write) ||
+ (request_type_ == HEAD)) {
+ backoff_release_time_ = GetBackoffReleaseTime();
+
+ bool posted = delegate_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &Core::OnCompletedURLRequest,
+ request_->status()));
+ // If the delegate message loop does not exist any more, then the delegate
+ // should be gone too.
+ DCHECK(posted || !delegate_);
+ ReleaseRequest();
+ }
+}
+
+void URLFetcher::Core::ReadResponse() {
+ // 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).
+ int bytes_read = 0;
+ if (request_->status().is_success() && (request_type_ != HEAD))
+ request_->Read(buffer_, kBufferSize, &bytes_read);
+ OnReadCompleted(request_.get(), bytes_read);
+}
+
+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_.get());
+
+ g_registry.Get().AddURLFetcherCore(this);
+ request_.reset(new net::URLRequest(original_url_, this));
+ int flags = request_->load_flags() | load_flags_;
+ if (!g_interception_enabled) {
+ flags = flags | net::LOAD_DISABLE_INTERCEPT;
+ }
+ if (is_chunked_upload_)
+ request_->EnableChunkedUpload();
+ request_->set_load_flags(flags);
+ request_->set_context(request_context_getter_->GetURLRequestContext());
+ request_->set_referrer(referrer_);
+
+ switch (request_type_) {
+ case GET:
+ break;
+
+ case POST:
+ DCHECK(!upload_content_.empty() || is_chunked_upload_);
+ DCHECK(!upload_content_type_.empty());
+
+ request_->set_method("POST");
+ extra_request_headers_.SetHeader(net::HttpRequestHeaders::kContentType,
+ upload_content_type_);
+ if (!upload_content_.empty()) {
+ request_->AppendBytesToUpload(
+ upload_content_.data(), static_cast<int>(upload_content_.length()));
+ }
+ break;
+
+ case HEAD:
+ request_->set_method("HEAD");
+ break;
+
+ default:
+ NOTREACHED();
+ }
+
+ if (!extra_request_headers_.IsEmpty())
+ request_->SetExtraRequestHeaders(extra_request_headers_);
+
+ // There might be data left over from a previous request attempt.
+ data_.clear();
+
+ // If we are writing the response to a file, the only caller
+ // of this function should have created it and not written yet.
+ CHECK(!temp_file_writer_.get() ||
+ temp_file_writer_->total_bytes_written() == 0);
+
+ request_->Start();
+}
+
+void URLFetcher::Core::StartURLRequestWhenAppropriate() {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+
+ if (was_cancelled_)
+ return;
+
+ if (original_url_throttler_entry_ == NULL) {
+ original_url_throttler_entry_ =
+ net::URLRequestThrottlerManager::GetInstance()->RegisterRequestUrl(
+ original_url_);
+ }
+
+ int64 delay = original_url_throttler_entry_->ReserveSendingTimeForNextRequest(
+ GetBackoffReleaseTime());
+ if (delay == 0) {
+ StartURLRequest();
+ } else {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Core::StartURLRequest),
+ delay);
+ }
+}
+
+void URLFetcher::Core::CancelURLRequest() {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+
+ if (request_.get()) {
+ request_->Cancel();
+ ReleaseRequest();
+ }
+ // 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;
+ temp_file_writer_.reset();
+}
+
+void URLFetcher::Core::OnCompletedURLRequest(
+ const net::URLRequestStatus& status) {
+ DCHECK(delegate_loop_proxy_->BelongsToCurrentThread());
+
+ // Save the status so that delegates can read it.
+ status_ = status;
+
+ // Checks the response from server.
+ if (response_code_ >= 500 ||
+ status.os_error() == net::ERR_TEMPORARILY_THROTTLED) {
+ // When encountering a server error, we will send the request again
+ // after backoff time.
+ ++num_retries_;
+ // Restarts the request if we still need to notify the delegate.
+ if (delegate_) {
+ // Note that backoff_delay_ may be 0 because (a) the URLRequestThrottler
+ // code does not necessarily back off on the first error, and (b) it
+ // only backs off on some of the 5xx status codes.
+ fetcher_->backoff_delay_ = backoff_release_time_ - base::TimeTicks::Now();
+ if (fetcher_->backoff_delay_ < base::TimeDelta())
+ fetcher_->backoff_delay_ = base::TimeDelta();
+
+ if (fetcher_->automatically_retry_on_5xx_ &&
+ num_retries_ <= fetcher_->max_retries()) {
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Core::StartURLRequestWhenAppropriate));
+ } else {
+ InformDelegateFetchIsComplete();
+ }
+ }
+ } else {
+ if (delegate_) {
+ fetcher_->backoff_delay_ = base::TimeDelta();
+ InformDelegateFetchIsComplete();
+ }
+ }
+}
+
+void URLFetcher::Core::InformDelegateFetchIsComplete() {
+ delegate_->OnURLFetchComplete(fetcher_);
+}
+
+void URLFetcher::Core::NotifyMalformedContent() {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ if (url_throttler_entry_ != NULL) {
+ int status_code = response_code_;
+ if (status_code == kInvalidHttpResponseCode) {
+ // The status code will generally be known by the time clients
+ // call the |ReceivedContentWasMalformed()| function (which ends up
+ // calling the current function) but if it's not, we need to assume
+ // the response was successful so that the total failure count
+ // used to calculate exponential back-off goes up.
+ status_code = 200;
+ }
+ url_throttler_entry_->ReceivedContentWasMalformed(status_code);
+ }
+}
+
+void URLFetcher::Core::ReleaseRequest() {
+ request_.reset();
+ g_registry.Get().RemoveURLFetcherCore(this);
+}
+
+base::TimeTicks URLFetcher::Core::GetBackoffReleaseTime() {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(original_url_throttler_entry_ != NULL);
+
+ base::TimeTicks original_url_backoff =
+ original_url_throttler_entry_->GetExponentialBackoffReleaseTime();
+ base::TimeTicks destination_url_backoff;
+ if (url_throttler_entry_ != NULL &&
+ original_url_throttler_entry_ != url_throttler_entry_) {
+ destination_url_backoff =
+ url_throttler_entry_->GetExponentialBackoffReleaseTime();
+ }
+
+ return original_url_backoff > destination_url_backoff ?
+ original_url_backoff : destination_url_backoff;
+}
+
+void URLFetcher::set_upload_data(const std::string& upload_content_type,
+ const std::string& upload_content) {
+ DCHECK(!core_->is_chunked_upload_);
+ core_->upload_content_type_ = upload_content_type;
+ core_->upload_content_ = upload_content;
+}
+
+void URLFetcher::set_chunked_upload(const std::string& content_type) {
+ DCHECK(core_->is_chunked_upload_ ||
+ (core_->upload_content_type_.empty() &&
+ core_->upload_content_.empty()));
+ core_->upload_content_type_ = content_type;
+ core_->upload_content_.clear();
+ core_->is_chunked_upload_ = true;
+}
+
+void URLFetcher::AppendChunkToUpload(const std::string& data,
+ bool is_last_chunk) {
+ DCHECK(data.length());
+ core_->AppendChunkToUpload(data, is_last_chunk);
+}
+
+const std::string& URLFetcher::upload_data() const {
+ return core_->upload_content_;
+}
+
+void URLFetcher::set_referrer(const std::string& referrer) {
+ core_->referrer_ = referrer;
+}
+
+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_.Clear();
+ core_->extra_request_headers_.AddHeadersFromString(extra_request_headers);
+}
+
+void URLFetcher::set_request_context(
+ net::URLRequestContextGetter* request_context_getter) {
+ core_->request_context_getter_ = request_context_getter;
+}
+
+void URLFetcher::set_automatically_retry_on_5xx(bool retry) {
+ automatically_retry_on_5xx_ = retry;
+}
+
+void URLFetcher::SaveResponseToTemporaryFile(
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy) {
+ core_->file_message_loop_proxy_ = file_message_loop_proxy;
+ core_->response_destination_ = TEMP_FILE;
+}
+
+net::HttpResponseHeaders* URLFetcher::response_headers() const {
+ return core_->response_headers_;
+}
+
+// TODO(panayiotis): socket_address_ is written in the IO thread,
+// if this is accessed in the UI thread, this could result in a race.
+// Same for response_headers_ above and was_fetched_via_proxy_ below.
+net::HostPortPair URLFetcher::socket_address() const {
+ return core_->socket_address_;
+}
+
+bool URLFetcher::was_fetched_via_proxy() const {
+ return core_->was_fetched_via_proxy_;
+}
+
+void URLFetcher::Start() {
+ core_->Start();
+}
+
+const GURL& URLFetcher::url() const {
+ return core_->url_;
+}
+
+const net::URLRequestStatus& URLFetcher::status() const {
+ return core_->status_;
+}
+
+int URLFetcher::response_code() const {
+ return core_->response_code_;
+}
+
+const net::ResponseCookies& URLFetcher::cookies() const {
+ return core_->cookies_;
+}
+
+bool URLFetcher::FileErrorOccurred(
+ base::PlatformFileError* out_error_code) const {
+
+ // Can't have a file error if no file is being created or written to.
+ if (!core_->temp_file_writer_.get()) {
+ return false;
+ }
+
+ base::PlatformFileError error_code = core_->temp_file_writer_->error_code();
+ if (error_code == base::PLATFORM_FILE_OK)
+ return false;
+
+ *out_error_code = error_code;
+ return true;
+}
+
+void URLFetcher::ReceivedContentWasMalformed() {
+ core_->ReceivedContentWasMalformed();
+}
+
+bool URLFetcher::GetResponseAsString(std::string* out_response_string) const {
+ if (core_->response_destination_ != STRING)
+ return false;
+
+ *out_response_string = core_->data_;
+ return true;
+}
+
+const std::string& URLFetcher::GetResponseStringRef() const {
+ CHECK(core_->response_destination_ == STRING);
+ return core_->data_;
+}
+
+void URLFetcher::SetResponseDestinationForTesting(
+ ResponseDestinationType value) {
+ core_->response_destination_ = value;
+}
+
+URLFetcher::ResponseDestinationType
+URLFetcher::GetResponseDestinationForTesting() const {
+ return core_->response_destination_;
+}
+
+bool URLFetcher::GetResponseAsFilePath(bool take_ownership,
+ FilePath* out_response_path) const {
+ if (core_->response_destination_ != TEMP_FILE ||
+ !core_->temp_file_writer_.get())
+ return false;
+
+ *out_response_path = core_->temp_file_writer_->temp_file();
+
+ if (take_ownership)
+ core_->temp_file_writer_->DisownTempFile();
+
+ return true;
+}
+
+// static
+void URLFetcher::CancelAll() {
+ Core::CancelAll();
+}
+
+// static
+int URLFetcher::GetNumFetcherCores() {
+ return Core::g_registry.Get().size();
+}
+
+URLFetcher::Delegate* URLFetcher::delegate() const {
+ return core_->delegate();
+}
diff --git a/content/common/url_fetcher.h b/content/common/url_fetcher.h
new file mode 100644
index 0000000..14df159
--- /dev/null
+++ b/content/common/url_fetcher.h
@@ -0,0 +1,315 @@
+// Copyright (c) 2011 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.
+
+// This file contains URLFetcher, a wrapper around net::URLRequest that handles
+// low-level details like thread safety, ref counting, and incremental buffer
+// reading. This is useful for callers who simply want to get the data from a
+// URL and don't care about all the nitty-gritty details.
+//
+// NOTE(willchan): Only one "IO" thread is supported for URLFetcher. This is a
+// temporary situation. We will work on allowing support for multiple "io"
+// threads per process.
+
+#ifndef CONTENT_COMMON_NET_URL_FETCHER_H_
+#define CONTENT_COMMON_NET_URL_FETCHER_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/time.h"
+
+class FilePath;
+class GURL;
+
+namespace base {
+class MessageLoopProxy;
+} // namespace base
+
+namespace net {
+class HostPortPair;
+class HttpResponseHeaders;
+class URLRequestContextGetter;
+class URLRequestStatus;
+typedef std::vector<std::string> ResponseCookies;
+} // namespace net
+
+// To use this class, create an instance with the desired URL and a pointer to
+// the object to be notified when the URL has been loaded:
+// URLFetcher* fetcher = new URLFetcher("http://www.google.com",
+// URLFetcher::GET, this);
+//
+// Then, optionally set properties on this object, like the request context or
+// extra headers:
+// fetcher->SetExtraRequestHeaders("X-Foo: bar");
+//
+// Finally, start the request:
+// fetcher->Start();
+//
+//
+// The object you supply as a delegate must inherit from URLFetcher::Delegate;
+// when the fetch is completed, OnURLFetchComplete() will be called with a
+// pointer to the URLFetcher. From that point until the original URLFetcher
+// instance is destroyed, you may use accessor methods to see the result of
+// the fetch. You should copy these objects if you need them to live longer
+// than the URLFetcher instance. If the URLFetcher instance is destroyed
+// before the callback happens, the fetch will be canceled and no callback
+// will occur.
+//
+// You may create the URLFetcher instance on any thread; OnURLFetchComplete()
+// will be called back on the same thread you use to create the instance.
+//
+//
+// NOTE: By default URLFetcher requests are NOT intercepted, except when
+// interception is explicitly enabled in tests.
+
+class URLFetcher {
+ public:
+ enum RequestType {
+ GET,
+ POST,
+ HEAD,
+ };
+
+ // Imposible http response code. Used to signal that no http response code
+ // was received.
+ static const int kInvalidHttpResponseCode;
+
+ class Delegate {
+ public:
+ // TODO(skerner): This will be removed in favor of the |source|-only
+ // version below. Leaving this for now to make the initial code review
+ // easy to read.
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ // This will be called when the URL has been fetched, successfully or not.
+ // Use accessor methods on |source| to get the results.
+ virtual void OnURLFetchComplete(const URLFetcher* source);
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ // URLFetcher::Create uses the currently registered Factory to create the
+ // URLFetcher. Factory is intended for testing.
+ class Factory {
+ public:
+ virtual URLFetcher* CreateURLFetcher(int id,
+ const GURL& url,
+ RequestType request_type,
+ Delegate* d) = 0;
+
+ protected:
+ virtual ~Factory() {}
+ };
+
+ // |url| is the URL to send the request to.
+ // |request_type| is the type of request to make.
+ // |d| the object that will receive the callback on fetch completion.
+ URLFetcher(const GURL& url, RequestType request_type, Delegate* d);
+
+ virtual ~URLFetcher();
+
+ // Sets the factory used by the static method Create to create a URLFetcher.
+ // URLFetcher does not take ownership of |factory|. A value of NULL results
+ // in a URLFetcher being created directly.
+#if defined(UNIT_TEST)
+ static void set_factory(Factory* factory) { factory_ = factory; }
+#endif
+
+ // Normally interception is disabled for URLFetcher, but you can use this
+ // to enable it for tests. Also see the set_factory method for another way
+ // of testing code that uses an URLFetcher.
+ static void enable_interception_for_tests(bool enabled) {
+ g_interception_enabled = enabled;
+ }
+
+ // Creates a URLFetcher, ownership returns to the caller. If there is no
+ // Factory (the default) this creates and returns a new URLFetcher. See the
+ // constructor for a description of the args. |id| may be used during testing
+ // to identify who is creating the URLFetcher.
+ static URLFetcher* Create(int id, const GURL& url, RequestType request_type,
+ Delegate* d);
+
+ // Sets data only needed by POSTs. All callers making POST requests should
+ // call this before the request is started. |upload_content_type| is the MIME
+ // type of the content, while |upload_content| is the data to be sent (the
+ // Content-Length header value will be set to the length of this data).
+ void set_upload_data(const std::string& upload_content_type,
+ const std::string& upload_content);
+
+ // Indicates that the POST data is sent via chunked transfer encoding.
+ // This may only be called before calling Start().
+ // Use AppendChunkToUpload() to give the data chunks after calling Start().
+ void set_chunked_upload(const std::string& upload_content_type);
+
+ // Adds the given bytes to a request's POST data transmitted using chunked
+ // transfer encoding.
+ // This method should be called ONLY after calling Start().
+ virtual void AppendChunkToUpload(const std::string& data, bool is_last_chunk);
+
+ // Set one or more load flags as defined in net/base/load_flags.h. Must be
+ // called before the request is started.
+ void set_load_flags(int load_flags);
+
+ // Returns the current load flags.
+ int load_flags() const;
+
+ // The referrer URL for the request. Must be called before the request is
+ // started.
+ void set_referrer(const std::string& referrer);
+
+ // Set extra headers on the request. Must be called before the request
+ // is started.
+ void set_extra_request_headers(const std::string& extra_request_headers);
+
+ // Set the net::URLRequestContext on the request. Must be called before the
+ // request is started.
+ void set_request_context(
+ net::URLRequestContextGetter* request_context_getter);
+
+ // If |retry| is false, 5xx responses will be propagated to the observer,
+ // if it is true URLFetcher will automatically re-execute the request,
+ // after backoff_delay() elapses. URLFetcher has it set to true by default.
+ void set_automatically_retry_on_5xx(bool retry);
+
+ int max_retries() const { return max_retries_; }
+
+ void set_max_retries(int max_retries) { max_retries_ = max_retries; }
+
+ // Returns the back-off delay before the request will be retried,
+ // when a 5xx response was received.
+ base::TimeDelta backoff_delay() const { return backoff_delay_; }
+
+ // Sets the back-off delay, allowing to mock 5xx requests in unit-tests.
+#if defined(UNIT_TEST)
+ void set_backoff_delay(base::TimeDelta backoff_delay) {
+ backoff_delay_ = backoff_delay;
+ }
+#endif // defined(UNIT_TEST)
+
+ // By default, the response is saved in a string. Call this method to save the
+ // response to a temporary file instead. Must be called before Start().
+ // |file_message_loop_proxy| will be used for all file operations.
+ void SaveResponseToTemporaryFile(
+ scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy);
+
+ // Retrieve the response headers from the request. Must only be called after
+ // the OnURLFetchComplete callback has run.
+ virtual net::HttpResponseHeaders* response_headers() const;
+
+ // Retrieve the remote socket address from the request. Must only
+ // be called after the OnURLFetchComplete callback has run and if
+ // the request has not failed.
+ net::HostPortPair socket_address() const;
+
+ // Returns true if the request was delivered through a proxy. Must only
+ // be called after the OnURLFetchComplete callback has run and the request
+ // has not failed.
+ bool was_fetched_via_proxy() const;
+
+ // Start the request. After this is called, you may not change any other
+ // settings.
+ virtual void Start();
+
+ // Return the URL that this fetcher is processing.
+ virtual const GURL& url() const;
+
+ // The status of the URL fetch.
+ virtual const net::URLRequestStatus& status() const;
+
+ // The http response code received. Will return
+ // URLFetcher::kInvalidHttpResponseCode if an error prevented any response
+ // from being received.
+ virtual int response_code() const;
+
+ // Cookies recieved.
+ virtual const net::ResponseCookies& cookies() const;
+
+ // Return true if any file system operation failed. If so, set |error_code|
+ // to the error code. File system errors are only possible if user called
+ // SaveResponseToTemporaryFile().
+ virtual bool FileErrorOccurred(base::PlatformFileError* out_error_code) const;
+
+ // Reports that the received content was malformed.
+ void ReceivedContentWasMalformed();
+
+ // Get the response as a string. Return false if the fetcher was not
+ // set to store the response as a string.
+ virtual bool GetResponseAsString(std::string* out_response_string) const;
+
+ // Get the path to the file containing the response body. Returns false
+ // if the response body was not saved to a file. If take_ownership is
+ // true, caller takes responsibility for the temp file, and it will not
+ // be removed once the URLFetcher is destroyed.
+ virtual bool GetResponseAsFilePath(bool take_ownership,
+ FilePath* out_response_path) const;
+
+ // Cancels all existing URLFetchers. Will notify the URLFetcher::Delegates.
+ // Note that any new URLFetchers created while this is running will not be
+ // cancelled. Typically, one would call this in the CleanUp() method of an IO
+ // thread, so that no new URLRequests would be able to start on the IO thread
+ // anyway. This doesn't prevent new URLFetchers from trying to post to the IO
+ // thread though, even though the task won't ever run.
+ static void CancelAll();
+
+ protected:
+ // How should the response be stored?
+ enum ResponseDestinationType {
+ STRING, // Default: In a std::string
+ TEMP_FILE // Write to a temp file
+ };
+
+ // Returns the delegate.
+ Delegate* delegate() const;
+
+ // Used by tests.
+ const std::string& upload_data() const;
+
+ // Return a reference to the string data fetched. Response type must
+ // be STRING, or this will CHECK. This method exists to support the
+ // old signiture to OnURLFetchComplete(), and will be removed as part
+ // of crbug.com/83592 .
+ const std::string& GetResponseStringRef() const;
+
+ void SetResponseDestinationForTesting(ResponseDestinationType);
+ ResponseDestinationType GetResponseDestinationForTesting() const;
+
+ private:
+ friend class URLFetcherTest;
+ friend class TestURLFetcher;
+
+ // Only used by URLFetcherTest, returns the number of URLFetcher::Core objects
+ // actively running.
+ static int GetNumFetcherCores();
+
+ class Core;
+ scoped_refptr<Core> core_;
+
+ static Factory* factory_;
+
+ // If |automatically_retry_on_5xx_| is false, 5xx responses will be
+ // propagated to the observer, if it is true URLFetcher will automatically
+ // re-execute the request, after the back-off delay has expired.
+ // true by default.
+ bool automatically_retry_on_5xx_;
+ // Back-off time delay. 0 by default.
+ base::TimeDelta backoff_delay_;
+ // Maximum retries allowed.
+ int max_retries_;
+
+ static bool g_interception_enabled;
+
+ DISALLOW_COPY_AND_ASSIGN(URLFetcher);
+};
+
+#endif // CONTENT_COMMON_NET_URL_FETCHER_H_
diff --git a/content/common/url_fetcher_unittest.cc b/content/common/url_fetcher_unittest.cc
new file mode 100644
index 0000000..b2c9979
--- /dev/null
+++ b/content/common/url_fetcher_unittest.cc
@@ -0,0 +1,904 @@
+// Copyright (c) 2011 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 "base/message_loop_proxy.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "content/common/url_fetcher.h"
+#include "crypto/nss_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/test_server.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "net/url_request/url_request_throttler_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_NSS)
+#include "net/ocsp/nss_ocsp.h"
+#endif
+
+using base::Time;
+using base::TimeDelta;
+
+// TODO(eroman): Add a regression test for http://crbug.com/40505.
+
+namespace {
+
+const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
+const std::string kTestServerFilePrefix = "files/";
+
+class CurriedTask : public Task {
+ public:
+ CurriedTask(Task* task, MessageLoop* target_loop)
+ : task_(task),
+ target_loop_(target_loop) {}
+
+ virtual void Run() {
+ target_loop_->PostTask(FROM_HERE, task_);
+ }
+
+ private:
+ Task* const task_;
+ MessageLoop* const target_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurriedTask);
+};
+
+class TestURLRequestContextGetter : public net::URLRequestContextGetter {
+ public:
+ explicit TestURLRequestContextGetter(
+ base::MessageLoopProxy* io_message_loop_proxy)
+ : io_message_loop_proxy_(io_message_loop_proxy) {
+ }
+ virtual net::URLRequestContext* GetURLRequestContext() {
+ if (!context_)
+ context_ = new TestURLRequestContext();
+ return context_;
+ }
+ virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
+ return io_message_loop_proxy_;
+ }
+
+ protected:
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+
+ private:
+ ~TestURLRequestContextGetter() {}
+
+ scoped_refptr<net::URLRequestContext> context_;
+};
+
+} // namespace
+
+class URLFetcherTest : public testing::Test, public URLFetcher::Delegate {
+ public:
+ URLFetcherTest() : fetcher_(NULL) { }
+
+ // Creates a URLFetcher, using the program's main thread to do IO.
+ virtual void CreateFetcher(const GURL& url);
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() {
+ return io_message_loop_proxy_;
+ }
+
+ protected:
+ virtual void SetUp() {
+ testing::Test::SetUp();
+
+ io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
+
+#if defined(USE_NSS)
+ crypto::EnsureNSSInit();
+ net::EnsureOCSPInit();
+#endif
+ }
+
+ virtual void TearDown() {
+#if defined(USE_NSS)
+ net::ShutdownOCSP();
+#endif
+ }
+
+ int GetNumFetcherCores() const {
+ return URLFetcher::GetNumFetcherCores();
+ }
+
+ // URLFetcher is designed to run on the main UI thread, but in our tests
+ // we assume that the current thread is the IO thread where the URLFetcher
+ // dispatches its requests to. When we wish to simulate being used from
+ // a UI thread, we dispatch a worker thread to do so.
+ MessageLoopForIO io_loop_;
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+
+ URLFetcher* fetcher_;
+};
+
+void URLFetcherTest::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter(
+ io_message_loop_proxy()));
+ fetcher_->Start();
+}
+
+void URLFetcherTest::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ EXPECT_TRUE(status.is_success());
+ EXPECT_EQ(200, response_code); // HTTP OK
+ EXPECT_FALSE(data.empty());
+
+ delete fetcher_; // Have to delete this here and not in the destructor,
+ // because the destructor won't necessarily run on the
+ // same thread that CreateFetcher() did.
+
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ // If the current message loop is not the IO loop, it will be shut down when
+ // the main loop returns and this thread subsequently goes out of scope.
+}
+
+namespace {
+
+// Version of URLFetcherTest that does a POST instead
+class URLFetcherPostTest : public URLFetcherTest {
+ public:
+ virtual void CreateFetcher(const GURL& url);
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+};
+
+// Version of URLFetcherTest that tests headers.
+class URLFetcherHeadersTest : public URLFetcherTest {
+ public:
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+};
+
+// Version of URLFetcherTest that tests SocketAddress.
+class URLFetcherSocketAddressTest : public URLFetcherTest {
+ public:
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+ protected:
+ std::string expected_host_;
+ uint16 expected_port_;
+};
+
+// Version of URLFetcherTest that tests overload protection.
+class URLFetcherProtectTest : public URLFetcherTest {
+ public:
+ virtual void CreateFetcher(const GURL& url);
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+ private:
+ Time start_time_;
+};
+
+// Version of URLFetcherTest that tests overload protection, when responses
+// passed through.
+class URLFetcherProtectTestPassedThrough : public URLFetcherTest {
+ public:
+ virtual void CreateFetcher(const GURL& url);
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+ private:
+ Time start_time_;
+};
+
+// Version of URLFetcherTest that tests bad HTTPS requests.
+class URLFetcherBadHTTPSTest : public URLFetcherTest {
+ public:
+ URLFetcherBadHTTPSTest();
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ private:
+ FilePath cert_dir_;
+};
+
+// Version of URLFetcherTest that tests request cancellation on shutdown.
+class URLFetcherCancelTest : public URLFetcherTest {
+ public:
+ virtual void CreateFetcher(const GURL& url);
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ void CancelRequest();
+};
+
+// Version of TestURLRequestContext that posts a Quit task to the IO
+// thread once it is deleted.
+class CancelTestURLRequestContext : public TestURLRequestContext {
+ virtual ~CancelTestURLRequestContext() {
+ // The d'tor should execute in the IO thread. Post the quit task to the
+ // current thread.
+ MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ }
+};
+
+class CancelTestURLRequestContextGetter : public net::URLRequestContextGetter {
+ public:
+ explicit CancelTestURLRequestContextGetter(
+ base::MessageLoopProxy* io_message_loop_proxy)
+ : io_message_loop_proxy_(io_message_loop_proxy),
+ context_created_(false, false) {
+ }
+ virtual net::URLRequestContext* GetURLRequestContext() {
+ if (!context_) {
+ context_ = new CancelTestURLRequestContext();
+ context_created_.Signal();
+ }
+ return context_;
+ }
+ virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
+ return io_message_loop_proxy_;
+ }
+ void WaitForContextCreation() {
+ context_created_.Wait();
+ }
+
+ private:
+ ~CancelTestURLRequestContextGetter() {}
+
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+ base::WaitableEvent context_created_;
+ scoped_refptr<net::URLRequestContext> context_;
+};
+
+// Version of URLFetcherTest that tests retying the same request twice.
+class URLFetcherMultipleAttemptTest : public URLFetcherTest {
+ public:
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+ private:
+ std::string data_;
+};
+
+class URLFetcherTempFileTest : public URLFetcherTest {
+ public:
+ URLFetcherTempFileTest()
+ : take_ownership_of_temp_file_(false) {
+ }
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source);
+
+ // This obsolete signature should not be used, but must be present
+ // to make clang happy.
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ virtual void CreateFetcher(const GURL& url);
+
+ protected:
+ FilePath expected_file_;
+ FilePath temp_file_;
+
+ // Set by the test. Used in OnURLFetchComplete() to decide if
+ // the URLFetcher should own the temp file, so that we can test
+ // disowning prevents the file from being deleted.
+ bool take_ownership_of_temp_file_;
+};
+
+void URLFetcherTempFileTest::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter(
+ io_message_loop_proxy()));
+
+ // Use the IO message loop to do the file operations in this test.
+ fetcher_->SaveResponseToTemporaryFile(io_message_loop_proxy());
+ fetcher_->Start();
+}
+
+TEST_F(URLFetcherTempFileTest, SmallGet) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Get a small file.
+ const char* kFileToFetch = "simple.html";
+ expected_file_ = test_server.document_root().AppendASCII(kFileToFetch);
+ CreateFetcher(test_server.GetURL(kTestServerFilePrefix + kFileToFetch));
+
+ MessageLoop::current()->Run(); // OnURLFetchComplete() will Quit().
+
+ ASSERT_FALSE(file_util::PathExists(temp_file_))
+ << temp_file_.value() << " not removed.";
+}
+
+TEST_F(URLFetcherTempFileTest, LargeGet) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Get a file large enough to require more than one read into
+ // URLFetcher::Core's IOBuffer.
+ const char* kFileToFetch = "animate1.gif";
+ expected_file_ = test_server.document_root().AppendASCII(kFileToFetch);
+ CreateFetcher(test_server.GetURL(kTestServerFilePrefix + kFileToFetch));
+
+ MessageLoop::current()->Run(); // OnURLFetchComplete() will Quit().
+}
+
+TEST_F(URLFetcherTempFileTest, CanTakeOwnershipOfFile) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Get a small file.
+ const char* kFileToFetch = "simple.html";
+ expected_file_ = test_server.document_root().AppendASCII(kFileToFetch);
+ CreateFetcher(test_server.GetURL(kTestServerFilePrefix + kFileToFetch));
+
+ MessageLoop::current()->Run(); // OnURLFetchComplete() will Quit().
+
+ MessageLoop::current()->RunAllPending();
+ ASSERT_FALSE(file_util::PathExists(temp_file_))
+ << temp_file_.value() << " not removed.";
+}
+
+// Wrapper that lets us call CreateFetcher() on a thread of our choice. We
+// could make URLFetcherTest refcounted and use PostTask(FROM_HERE.. ) to call
+// CreateFetcher() directly, but the ownership of the URLFetcherTest is a bit
+// confusing in that case because GTest doesn't know about the refcounting.
+// It's less confusing to just do it this way.
+class FetcherWrapperTask : public Task {
+ public:
+ FetcherWrapperTask(URLFetcherTest* test, const GURL& url)
+ : test_(test), url_(url) { }
+ virtual void Run() {
+ test_->CreateFetcher(url_);
+ }
+
+ private:
+ URLFetcherTest* test_;
+ GURL url_;
+};
+
+void URLFetcherPostTest::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::POST, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter(
+ io_message_loop_proxy()));
+ fetcher_->set_upload_data("application/x-www-form-urlencoded",
+ "bobsyeruncle");
+ fetcher_->Start();
+}
+
+void URLFetcherPostTest::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ EXPECT_EQ(std::string("bobsyeruncle"), data);
+ URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
+ cookies, data);
+}
+
+void URLFetcherHeadersTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ std::string header;
+ EXPECT_TRUE(source->response_headers()->GetNormalizedHeader("cache-control",
+ &header));
+ EXPECT_EQ("private", header);
+ URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
+ cookies, data);
+}
+
+void URLFetcherSocketAddressTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ EXPECT_EQ("127.0.0.1", source->socket_address().host());
+ EXPECT_EQ(expected_port_, source->socket_address().port());
+ URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
+ cookies, data);
+}
+
+void URLFetcherProtectTest::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter(
+ io_message_loop_proxy()));
+ start_time_ = Time::Now();
+ fetcher_->set_max_retries(11);
+ fetcher_->Start();
+}
+
+void URLFetcherProtectTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
+ if (response_code >= 500) {
+ // Now running ServerUnavailable test.
+ // It takes more than 1 second to finish all 11 requests.
+ EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
+ EXPECT_TRUE(status.is_success());
+ EXPECT_FALSE(data.empty());
+ delete fetcher_;
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ } else {
+ // Now running Overload test.
+ static int count = 0;
+ count++;
+ if (count < 20) {
+ fetcher_->Start();
+ } else {
+ // We have already sent 20 requests continuously. And we expect that
+ // it takes more than 1 second due to the overload protection settings.
+ EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
+ URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
+ cookies, data);
+ }
+ }
+}
+
+void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter(
+ io_message_loop_proxy()));
+ fetcher_->set_automatically_retry_on_5xx(false);
+ start_time_ = Time::Now();
+ fetcher_->set_max_retries(11);
+ fetcher_->Start();
+}
+
+void URLFetcherProtectTestPassedThrough::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ const TimeDelta one_minute = TimeDelta::FromMilliseconds(60000);
+ if (response_code >= 500) {
+ // Now running ServerUnavailable test.
+ // It should get here on the first attempt, so almost immediately and
+ // *not* to attempt to execute all 11 requests (2.5 minutes).
+ EXPECT_TRUE(Time::Now() - start_time_ < one_minute);
+ EXPECT_TRUE(status.is_success());
+ // Check that suggested back off time is bigger than 0.
+ EXPECT_GT(fetcher_->backoff_delay().InMicroseconds(), 0);
+ EXPECT_FALSE(data.empty());
+ } else {
+ // We should not get here!
+ ADD_FAILURE();
+ }
+
+ delete fetcher_;
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+
+URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() {
+ PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
+ cert_dir_ = cert_dir_.AppendASCII("chrome");
+ cert_dir_ = cert_dir_.AppendASCII("test");
+ cert_dir_ = cert_dir_.AppendASCII("data");
+ cert_dir_ = cert_dir_.AppendASCII("ssl");
+ cert_dir_ = cert_dir_.AppendASCII("certificates");
+}
+
+// The "server certificate expired" error should result in automatic
+// cancellation of the request by
+// net::URLRequest::Delegate::OnSSLCertificateError.
+void URLFetcherBadHTTPSTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ // This part is different from URLFetcherTest::OnURLFetchComplete
+ // because this test expects the request to be cancelled.
+ EXPECT_EQ(net::URLRequestStatus::CANCELED, status.status());
+ EXPECT_EQ(net::ERR_ABORTED, status.os_error());
+ EXPECT_EQ(-1, response_code);
+ EXPECT_TRUE(cookies.empty());
+ EXPECT_TRUE(data.empty());
+
+ // The rest is the same as URLFetcherTest::OnURLFetchComplete.
+ delete fetcher_;
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+void URLFetcherCancelTest::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ CancelTestURLRequestContextGetter* context_getter =
+ new CancelTestURLRequestContextGetter(io_message_loop_proxy());
+ fetcher_->set_request_context(context_getter);
+ fetcher_->set_max_retries(2);
+ fetcher_->Start();
+ // We need to wait for the creation of the net::URLRequestContext, since we
+ // rely on it being destroyed as a signal to end the test.
+ context_getter->WaitForContextCreation();
+ CancelRequest();
+}
+
+void URLFetcherCancelTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ // We should have cancelled the request before completion.
+ ADD_FAILURE();
+ delete fetcher_;
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+void URLFetcherCancelTest::CancelRequest() {
+ delete fetcher_;
+ // The URLFetcher's test context will post a Quit task once it is
+ // deleted. So if this test simply hangs, it means cancellation
+ // did not work.
+}
+
+void URLFetcherMultipleAttemptTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ EXPECT_TRUE(status.is_success());
+ EXPECT_EQ(200, response_code); // HTTP OK
+ EXPECT_FALSE(data.empty());
+ if (!data.empty() && data_.empty()) {
+ data_ = data;
+ fetcher_->Start();
+ } else {
+ EXPECT_EQ(data, data_);
+ delete fetcher_; // Have to delete this here and not in the destructor,
+ // because the destructor won't necessarily run on the
+ // same thread that CreateFetcher() did.
+
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ // If the current message loop is not the IO loop, it will be shut down when
+ // the main loop returns and this thread subsequently goes out of scope.
+ }
+}
+
+void URLFetcherTempFileTest::OnURLFetchComplete(const URLFetcher* source) {
+ EXPECT_TRUE(source->status().is_success());
+ EXPECT_EQ(source->response_code(), 200);
+
+ EXPECT_TRUE(source->GetResponseAsFilePath(
+ take_ownership_of_temp_file_, &temp_file_));
+
+ EXPECT_TRUE(file_util::ContentsEqual(expected_file_, temp_file_));
+
+ delete fetcher_;
+
+ io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+void URLFetcherTempFileTest::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ NOTREACHED();
+}
+
+
+TEST_F(URLFetcherTest, SameThreadsTest) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Create the fetcher on the main thread. Since IO will happen on the main
+ // thread, this will test URLFetcher's ability to do everything on one
+ // thread.
+ CreateFetcher(test_server.GetURL("defaultresponse"));
+
+ MessageLoop::current()->Run();
+}
+
+#if defined(OS_MACOSX)
+// SIGSEGV on Mac: http://crbug.com/60426
+TEST_F(URLFetcherTest, DISABLED_DifferentThreadsTest) {
+#else
+TEST_F(URLFetcherTest, DifferentThreadsTest) {
+#endif
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Create a separate thread that will create the URLFetcher. The current
+ // (main) thread will do the IO, and when the fetch is complete it will
+ // terminate the main thread's message loop; then the other thread's
+ // message loop will be shut down automatically as the thread goes out of
+ // scope.
+ base::Thread t("URLFetcher test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this,
+ test_server.GetURL("defaultresponse")));
+
+ MessageLoop::current()->Run();
+}
+
+#if defined(OS_MACOSX)
+// SIGSEGV on Mac: http://crbug.com/60426
+TEST_F(URLFetcherPostTest, DISABLED_Basic) {
+#else
+TEST_F(URLFetcherPostTest, Basic) {
+#endif
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ CreateFetcher(test_server.GetURL("echo"));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(URLFetcherHeadersTest, Headers) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+ ASSERT_TRUE(test_server.Start());
+
+ CreateFetcher(test_server.GetURL("files/with-headers.html"));
+ MessageLoop::current()->Run();
+ // The actual tests are in the URLFetcherHeadersTest fixture.
+}
+
+TEST_F(URLFetcherSocketAddressTest, SocketAddress) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+ ASSERT_TRUE(test_server.Start());
+ expected_port_ = test_server.host_port_pair().port();
+
+ // Reusing "with-headers.html" but doesn't really matter.
+ CreateFetcher(test_server.GetURL("files/with-headers.html"));
+ MessageLoop::current()->Run();
+ // The actual tests are in the URLFetcherSocketAddressTest fixture.
+}
+
+TEST_F(URLFetcherProtectTest, Overload) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL url(test_server.GetURL("defaultresponse"));
+
+ // Registers an entry for test url. It only allows 3 requests to be sent
+ // in 200 milliseconds.
+ net::URLRequestThrottlerManager* manager =
+ net::URLRequestThrottlerManager::GetInstance();
+ scoped_refptr<net::URLRequestThrottlerEntry> entry(
+ new net::URLRequestThrottlerEntry(manager, "", 200, 3, 1, 2.0, 0.0, 256));
+ manager->OverrideEntryForTests(url, entry);
+
+ CreateFetcher(url);
+
+ MessageLoop::current()->Run();
+
+ net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url);
+}
+
+TEST_F(URLFetcherProtectTest, ServerUnavailable) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL url(test_server.GetURL("files/server-unavailable.html"));
+
+ // Registers an entry for test url. The backoff time is calculated by:
+ // new_backoff = 2.0 * old_backoff + 0
+ // and maximum backoff time is 256 milliseconds.
+ // Maximum retries allowed is set to 11.
+ net::URLRequestThrottlerManager* manager =
+ net::URLRequestThrottlerManager::GetInstance();
+ scoped_refptr<net::URLRequestThrottlerEntry> entry(
+ new net::URLRequestThrottlerEntry(manager, "", 200, 3, 1, 2.0, 0.0, 256));
+ manager->OverrideEntryForTests(url, entry);
+
+ CreateFetcher(url);
+
+ MessageLoop::current()->Run();
+
+ net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url);
+}
+
+TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL url(test_server.GetURL("files/server-unavailable.html"));
+
+ // Registers an entry for test url. The backoff time is calculated by:
+ // new_backoff = 2.0 * old_backoff + 0
+ // and maximum backoff time is 150000 milliseconds.
+ // Maximum retries allowed is set to 11.
+ net::URLRequestThrottlerManager* manager =
+ net::URLRequestThrottlerManager::GetInstance();
+ scoped_refptr<net::URLRequestThrottlerEntry> entry(
+ new net::URLRequestThrottlerEntry(
+ manager, "", 200, 3, 100, 2.0, 0.0, 150000));
+ // Total time if *not* for not doing automatic backoff would be 150s.
+ // In reality it should be "as soon as server responds".
+ manager->OverrideEntryForTests(url, entry);
+
+ CreateFetcher(url);
+
+ MessageLoop::current()->Run();
+
+ net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url);
+}
+
+#if defined(OS_MACOSX)
+// SIGSEGV on Mac: http://crbug.com/60426
+TEST_F(URLFetcherBadHTTPSTest, DISABLED_BadHTTPSTest) {
+#else
+TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) {
+#endif
+ net::TestServer::HTTPSOptions https_options(
+ net::TestServer::HTTPSOptions::CERT_EXPIRED);
+ net::TestServer test_server(https_options, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ CreateFetcher(test_server.GetURL("defaultresponse"));
+ MessageLoop::current()->Run();
+}
+
+#if defined(OS_MACOSX)
+// SIGSEGV on Mac: http://crbug.com/60426
+TEST_F(URLFetcherCancelTest, DISABLED_ReleasesContext) {
+#else
+TEST_F(URLFetcherCancelTest, ReleasesContext) {
+#endif
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL url(test_server.GetURL("files/server-unavailable.html"));
+
+ // Registers an entry for test url. The backoff time is calculated by:
+ // new_backoff = 2.0 * old_backoff +0
+ // The initial backoff is 2 seconds and maximum backoff is 4 seconds.
+ // Maximum retries allowed is set to 2.
+ net::URLRequestThrottlerManager* manager =
+ net::URLRequestThrottlerManager::GetInstance();
+ scoped_refptr<net::URLRequestThrottlerEntry> entry(
+ new net::URLRequestThrottlerEntry(
+ manager, "", 200, 3, 2000, 2.0, 0.0, 4000));
+ manager->OverrideEntryForTests(url, entry);
+
+ // Create a separate thread that will create the URLFetcher. The current
+ // (main) thread will do the IO, and when the fetch is complete it will
+ // terminate the main thread's message loop; then the other thread's
+ // message loop will be shut down automatically as the thread goes out of
+ // scope.
+ base::Thread t("URLFetcher test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url));
+
+ MessageLoop::current()->Run();
+
+ net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url);
+}
+
+TEST_F(URLFetcherCancelTest, CancelWhileDelayedStartTaskPending) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL url(test_server.GetURL("files/server-unavailable.html"));
+
+ // Register an entry for test url.
+ // Using a sliding window of 4 seconds, and max of 1 request, under a fast
+ // run we expect to have a 4 second delay when posting the Start task.
+ net::URLRequestThrottlerManager* manager =
+ net::URLRequestThrottlerManager::GetInstance();
+ scoped_refptr<net::URLRequestThrottlerEntry> entry(
+ new net::URLRequestThrottlerEntry(
+ manager, "", 4000, 1, 2000, 2.0, 0.0, 4000));
+ manager->OverrideEntryForTests(url, entry);
+ // Fake that a request has just started.
+ entry->ReserveSendingTimeForNextRequest(base::TimeTicks());
+
+ // The next request we try to send will be delayed by ~4 seconds.
+ // The slower the test runs, the less the delay will be (since it takes the
+ // time difference from now).
+
+ base::Thread t("URLFetcher test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url));
+
+ MessageLoop::current()->Run();
+
+ net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url);
+}
+
+TEST_F(URLFetcherMultipleAttemptTest, SameData) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+
+ // Create the fetcher on the main thread. Since IO will happen on the main
+ // thread, this will test URLFetcher's ability to do everything on one
+ // thread.
+ CreateFetcher(test_server.GetURL("defaultresponse"));
+
+ MessageLoop::current()->Run();
+}
+
+// Tests to make sure CancelAll() will successfully cancel existing URLFetchers.
+TEST_F(URLFetcherTest, CancelAll) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
+ ASSERT_TRUE(test_server.Start());
+ EXPECT_EQ(0, GetNumFetcherCores());
+
+ CreateFetcher(test_server.GetURL("defaultresponse"));
+ io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ new CurriedTask(new MessageLoop::QuitTask(), MessageLoop::current()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(1, GetNumFetcherCores());
+ URLFetcher::CancelAll();
+ EXPECT_EQ(0, GetNumFetcherCores());
+ delete fetcher_;
+}
+
+} // namespace.
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 242bfa8..9d135ce 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -227,6 +227,8 @@
'common/unix_domain_socket_posix.h',
'common/url_constants.cc',
'common/url_constants.h',
+ 'common/url_fetcher.cc',
+ 'common/url_fetcher.h',
'common/video_capture_messages.h',
'common/view_messages.h',
'common/view_types.cc',