// Copyright (c) 2012 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 NET_URL_REQUEST_TEST_URL_FETCHER_FACTORY_H_ #define NET_URL_REQUEST_TEST_URL_FETCHER_FACTORY_H_ #include <list> #include <map> #include <string> #include <utility> #include "base/basictypes.h" #include "base/callback.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "net/http/http_request_headers.h" #include "net/http/http_status_code.h" #include "net/url_request/url_fetcher_factory.h" #include "net/url_request/url_request_status.h" #include "url/gurl.h" namespace net { // Changes URLFetcher's Factory for the lifetime of the object. // Note that this scoper cannot be nested (to make it even harder to misuse). class ScopedURLFetcherFactory : public base::NonThreadSafe { public: explicit ScopedURLFetcherFactory(URLFetcherFactory* factory); virtual ~ScopedURLFetcherFactory(); private: DISALLOW_COPY_AND_ASSIGN(ScopedURLFetcherFactory); }; // TestURLFetcher and TestURLFetcherFactory are used for testing consumers of // URLFetcher. TestURLFetcherFactory is a URLFetcherFactory 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. // MessageLoop message_loop; // // And an IO thread to release URLRequestContextGetter in URLFetcher::Core. // BrowserThreadImpl io_thread(BrowserThread::IO, &message_loop); // // Create factory (it automatically sets itself as URLFetcher's factory). // TestURLFetcherFactory 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. // ... // // 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 TestURLFetcherFactory; class TestURLFetcher : public URLFetcher { public: // Interface for tests to intercept production code classes using URLFetcher. // Allows even-driven mock server classes to analyze the correctness of // requests / uploads events and forge responses back at the right moment. class DelegateForTests { public: // Callback issued correspondingly to the call to the |Start()| method. virtual void OnRequestStart(int fetcher_id) = 0; // Callback issued correspondingly to the call to |AppendChunkToUpload|. // Uploaded chunks can be retrieved with the |upload_chunks()| getter. virtual void OnChunkUpload(int fetcher_id) = 0; // Callback issued correspondingly to the destructor. virtual void OnRequestEnd(int fetcher_id) = 0; }; TestURLFetcher(int id, const GURL& url, URLFetcherDelegate* d); virtual ~TestURLFetcher(); // URLFetcher implementation virtual void SetUploadData(const std::string& upload_content_type, const std::string& upload_content) OVERRIDE; virtual void SetUploadFilePath( const std::string& upload_content_type, const base::FilePath& file_path, uint64 range_offset, uint64 range_length, scoped_refptr<base::TaskRunner> file_task_runner) OVERRIDE; virtual void SetChunkedUpload( const std::string& upload_content_type) OVERRIDE; // Overriden to cache the chunks uploaded. Caller can read back the uploaded // chunks with the upload_chunks() accessor. virtual void AppendChunkToUpload(const std::string& data, bool is_last_chunk) OVERRIDE; virtual void SetLoadFlags(int load_flags) OVERRIDE; virtual int GetLoadFlags() const OVERRIDE; virtual void SetReferrer(const std::string& referrer) OVERRIDE; virtual void SetReferrerPolicy( URLRequest::ReferrerPolicy referrer_policy) OVERRIDE; virtual void SetExtraRequestHeaders( const std::string& extra_request_headers) OVERRIDE; virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE; virtual void SetRequestContext( URLRequestContextGetter* request_context_getter) OVERRIDE; virtual void SetFirstPartyForCookies( const GURL& first_party_for_cookies) OVERRIDE; virtual void SetURLRequestUserData( const void* key, const CreateDataCallback& create_data_callback) OVERRIDE; virtual void SetStopOnRedirect(bool stop_on_redirect) OVERRIDE; virtual void SetAutomaticallyRetryOn5xx(bool retry) OVERRIDE; virtual void SetMaxRetriesOn5xx(int max_retries) OVERRIDE; virtual int GetMaxRetriesOn5xx() const OVERRIDE; virtual base::TimeDelta GetBackoffDelay() const OVERRIDE; virtual void SetAutomaticallyRetryOnNetworkChanges(int max_retries) OVERRIDE; virtual void SaveResponseToFileAtPath( const base::FilePath& file_path, scoped_refptr<base::SequencedTaskRunner> file_task_runner) OVERRIDE; virtual void SaveResponseToTemporaryFile( scoped_refptr<base::SequencedTaskRunner> file_task_runner) OVERRIDE; virtual void SaveResponseWithWriter( scoped_ptr<URLFetcherResponseWriter> response_writer) OVERRIDE; virtual HttpResponseHeaders* GetResponseHeaders() const OVERRIDE; virtual HostPortPair GetSocketAddress() const OVERRIDE; virtual bool WasFetchedViaProxy() const OVERRIDE; virtual void Start() OVERRIDE; // URL we were created with. Because of how we're using URLFetcher GetURL() // always returns an empty URL. Chances are you'll want to use // GetOriginalURL() in your tests. virtual const GURL& GetOriginalURL() const OVERRIDE; virtual const GURL& GetURL() const OVERRIDE; virtual const URLRequestStatus& GetStatus() const OVERRIDE; virtual int GetResponseCode() const OVERRIDE; virtual const ResponseCookies& GetCookies() const OVERRIDE; virtual void ReceivedContentWasMalformed() OVERRIDE; // Override response access functions to return fake data. virtual bool GetResponseAsString( std::string* out_response_string) const OVERRIDE; virtual bool GetResponseAsFilePath( bool take_ownership, base::FilePath* out_response_path) const OVERRIDE; void GetExtraRequestHeaders(HttpRequestHeaders* headers) const; // Sets owner of this class. Set it to a non-NULL value if you want // to automatically unregister this fetcher from the owning factory // upon destruction. void set_owner(TestURLFetcherFactory* owner) { owner_ = owner; } // Unique ID in our factory. int id() const { return id_; } // Returns the data uploaded on this URLFetcher. const std::string& upload_data() const { return upload_data_; } const base::FilePath& upload_file_path() const { return upload_file_path_; } // Returns the chunks of data uploaded on this URLFetcher. const std::list<std::string>& upload_chunks() const { return chunks_; } // Checks whether the last call to |AppendChunkToUpload(...)| was final. bool did_receive_last_chunk() const { return did_receive_last_chunk_; } // Returns the delegate installed on the URLFetcher. URLFetcherDelegate* delegate() const { return delegate_; } void set_url(const GURL& url) { fake_url_ = url; } void set_status(const URLRequestStatus& status); void set_response_code(int response_code) { fake_response_code_ = response_code; } void set_cookies(const ResponseCookies& c) { fake_cookies_ = c; } void set_was_fetched_via_proxy(bool flag); void set_response_headers(scoped_refptr<HttpResponseHeaders> headers); void set_backoff_delay(base::TimeDelta backoff_delay); void SetDelegateForTests(DelegateForTests* delegate_for_tests); // Set string data. void SetResponseString(const std::string& response); // Set File data. void SetResponseFilePath(const base::FilePath& path); private: enum ResponseDestinationType { STRING, // Default: In a std::string TEMP_FILE // Write to a temp file }; TestURLFetcherFactory* owner_; const int id_; const GURL original_url_; URLFetcherDelegate* delegate_; DelegateForTests* delegate_for_tests_; std::string upload_data_; base::FilePath upload_file_path_; 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. int fake_load_flags_; GURL fake_url_; URLRequestStatus fake_status_; int fake_response_code_; ResponseCookies fake_cookies_; ResponseDestinationType fake_response_destination_; std::string fake_response_string_; base::FilePath fake_response_file_path_; bool fake_was_fetched_via_proxy_; scoped_refptr<HttpResponseHeaders> fake_response_headers_; HttpRequestHeaders fake_extra_request_headers_; int fake_max_retries_; base::TimeDelta fake_backoff_delay_; scoped_ptr<URLFetcherResponseWriter> response_writer_; DISALLOW_COPY_AND_ASSIGN(TestURLFetcher); }; typedef TestURLFetcher::DelegateForTests TestURLFetcherDelegateForTests; // Simple URLFetcherFactory method that creates TestURLFetchers. All fetchers // are registered in a map by the id passed to the create method. // Optionally, a fetcher may be automatically unregistered from the map upon // its destruction. class TestURLFetcherFactory : public URLFetcherFactory, public ScopedURLFetcherFactory { public: TestURLFetcherFactory(); virtual ~TestURLFetcherFactory(); virtual URLFetcher* CreateURLFetcher( int id, const GURL& url, URLFetcher::RequestType request_type, URLFetcherDelegate* d) OVERRIDE; TestURLFetcher* GetFetcherByID(int id) const; void RemoveFetcherFromMap(int id); void SetDelegateForTests(TestURLFetcherDelegateForTests* delegate_for_tests); void set_remove_fetcher_on_delete(bool remove_fetcher_on_delete) { remove_fetcher_on_delete_ = remove_fetcher_on_delete; } private: // Maps from id passed to create to the returned URLFetcher. typedef std::map<int, TestURLFetcher*> Fetchers; Fetchers fetchers_; TestURLFetcherDelegateForTests* delegate_for_tests_; // Whether to automatically unregister a fetcher from this factory upon its // destruction, false by default. bool remove_fetcher_on_delete_; 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. // FakeURLFetcher can be used to create a URLFetcher that will emit a fake // response when started. This class can be used in place of an actual // URLFetcher. // // Example usage: // FakeURLFetcher fake_fetcher("http://a.com", some_delegate, // "<html><body>hello world</body></html>", // HTTP_OK); // // // Will schedule a call to some_delegate->OnURLFetchComplete(&fake_fetcher). // fake_fetcher.Start(); class FakeURLFetcher : public TestURLFetcher { public: // Normal URL fetcher constructor but also takes in a pre-baked response. FakeURLFetcher(const GURL& url, URLFetcherDelegate* d, const std::string& response_data, HttpStatusCode response_code, URLRequestStatus::Status status); // Start the request. This will call the given delegate asynchronously // with the pre-baked response as parameter. virtual void Start() OVERRIDE; virtual const GURL& GetURL() const OVERRIDE; virtual ~FakeURLFetcher(); private: // This is the method which actually calls the delegate that is passed in the // constructor. void RunDelegate(); base::WeakPtrFactory<FakeURLFetcher> weak_factory_; DISALLOW_COPY_AND_ASSIGN(FakeURLFetcher); }; // FakeURLFetcherFactory is a factory for FakeURLFetcher objects. When // instantiated, it sets itself up as the default URLFetcherFactory. Fake // responses for given URLs can be set using SetFakeResponse. // // 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 clearing 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; // // // You know that class SomeService will request http://a.com/success and you // // want to respond with a simple html page and an HTTP/200 code. // factory.SetFakeResponse("http://a.com/success", // "<html><body>hello world</body></html>", // HTTP_OK, // URLRequestStatus::SUCCESS); // // You know that class SomeService will request url http://a.com/servererror // // and you want to test the service class by returning a server error. // factory.SetFakeResponse("http://a.com/servererror", // "", // HTTP_INTERNAL_SERVER_ERROR, // URLRequestStatus::SUCCESS); // // You know that class SomeService will request url http://a.com/autherror // // and you want to test the service class by returning a specific error // // code, say, a HTTP/401 error. // factory.SetFakeResponse("http://a.com/autherror", // "some_response", // HTTP_UNAUTHORIZED, // URLRequestStatus::SUCCESS); // // // You know that class SomeService will request url http://a.com/failure // // and you want to test the service class by returning a failure in the // // network layer. // factory.SetFakeResponse("http://a.com/failure", // "", // HTTP_INTERNAL_SERVER_ERROR, // URLRequestStatus::FAILURE); // // SomeService service; // service.Run(); // Will eventually request these three URLs. class FakeURLFetcherFactory : public URLFetcherFactory, public ScopedURLFetcherFactory { public: // Parameters to FakeURLFetcherCreator: url, delegate, response_data, // response_code // |url| URL for instantiated FakeURLFetcher // |delegate| Delegate for FakeURLFetcher // |response_data| response data for FakeURLFetcher // |response_code| response code for FakeURLFetcher // |status| URL fetch status for FakeURLFetcher // These arguments should by default be used in instantiating FakeURLFetcher // like so: // new FakeURLFetcher(url, delegate, response_data, response_code, status) typedef base::Callback<scoped_ptr<FakeURLFetcher>( const GURL&, URLFetcherDelegate*, const std::string&, HttpStatusCode, URLRequestStatus::Status)> FakeURLFetcherCreator; // |default_factory|, which can be NULL, is a URLFetcherFactory that // will be used to construct a URLFetcher in case the URL being created // has no pre-baked response. If it is NULL, a URLFetcherImpl will be // created in this case. explicit FakeURLFetcherFactory(URLFetcherFactory* default_factory); // |default_factory|, which can be NULL, is a URLFetcherFactory that // will be used to construct a URLFetcher in case the URL being created // has no pre-baked response. If it is NULL, a URLFetcherImpl will be // created in this case. // |creator| is a callback that returns will be called to create a // FakeURLFetcher if a response is found to a given URL. It can be // set to MakeFakeURLFetcher. FakeURLFetcherFactory(URLFetcherFactory* default_factory, const FakeURLFetcherCreator& creator); 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, URLFetcherDelegate* d) OVERRIDE; // Sets the fake response for a given URL. The |response_data| may be empty. // The |response_code| may be any HttpStatusCode. For instance, HTTP_OK will // return an HTTP/200 and HTTP_INTERNAL_SERVER_ERROR will return an HTTP/500. // The |status| argument may be any URLRequestStatus::Status value. Typically, // requests that return a valid HttpStatusCode have the SUCCESS status, while // requests that indicate a failure to connect to the server have the FAILED // status. void SetFakeResponse(const GURL& url, const std::string& response_data, HttpStatusCode response_code, URLRequestStatus::Status status); // Clear all the fake responses that were previously set via // SetFakeResponse(). void ClearFakeResponses(); private: struct FakeURLResponse { std::string response_data; HttpStatusCode response_code; URLRequestStatus::Status status; }; typedef std::map<GURL, FakeURLResponse> FakeResponseMap; const FakeURLFetcherCreator creator_; FakeResponseMap fake_responses_; URLFetcherFactory* const default_factory_; static scoped_ptr<FakeURLFetcher> DefaultFakeURLFetcherCreator( const GURL& url, URLFetcherDelegate* delegate, const std::string& response_data, HttpStatusCode response_code, URLRequestStatus::Status status); DISALLOW_COPY_AND_ASSIGN(FakeURLFetcherFactory); }; // This is an implementation of URLFetcherFactory that will create a // URLFetcherImpl. 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 URLFetcherImplFactory : public URLFetcherFactory { public: URLFetcherImplFactory(); virtual ~URLFetcherImplFactory(); // This method will create a real URLFetcher. virtual URLFetcher* CreateURLFetcher( int id, const GURL& url, URLFetcher::RequestType request_type, URLFetcherDelegate* d) OVERRIDE; }; } // namespace net #endif // NET_URL_REQUEST_TEST_URL_FETCHER_FACTORY_H_