| // 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. |
| |
| #include "net/url_request/url_fetcher_impl.h" |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/post_task.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/base/upload_element_reader.h" |
| #include "net/base/upload_file_element_reader.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_with_scoped_task_environment.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_fetcher_delegate.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/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| using base::Time; |
| using base::TimeDelta; |
| using net::test::IsError; |
| using net::test::IsOk; |
| |
| // TODO(eroman): Add a regression test for http://crbug.com/40505. |
| |
| namespace { |
| |
| // TODO(akalin): Move all the test data to somewhere under net/. |
| const base::FilePath::CharType kDocRoot[] = |
| FILE_PATH_LITERAL("net/data/url_fetcher_impl_unittest"); |
| const char kTestServerFilePrefix[] = "/"; |
| |
| // Test server path and response body for the default URL used by many of the |
| // tests. |
| const char kDefaultResponsePath[] = "/defaultresponse"; |
| const char kDefaultResponseBody[] = |
| "Default response given for path: /defaultresponse"; |
| |
| // Request body for streams created by CreateUploadStream. |
| const char kCreateUploadStreamBody[] = "rosebud"; |
| |
| base::FilePath GetUploadFileTestPath() { |
| base::FilePath path; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| return path.Append( |
| FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt")); |
| } |
| |
| // Simple URLRequestDelegate that waits for the specified fetcher to complete. |
| // Can only be used once. |
| class WaitingURLFetcherDelegate : public URLFetcherDelegate { |
| public: |
| WaitingURLFetcherDelegate() : did_complete_(false) {} |
| |
| void CreateFetcher( |
| const GURL& url, |
| URLFetcher::RequestType request_type, |
| scoped_refptr<net::URLRequestContextGetter> context_getter) { |
| if (!on_complete_or_cancel_) { |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| on_complete_or_cancel_ = run_loop_->QuitClosure(); |
| } |
| fetcher_.reset(new URLFetcherImpl(url, request_type, this, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| fetcher_->SetRequestContext(context_getter.get()); |
| } |
| |
| URLFetcher* fetcher() const { return fetcher_.get(); } |
| |
| // Wait until the request has completed or been canceled. |
| void StartFetcherAndWait() { |
| fetcher_->Start(); |
| WaitForComplete(); |
| } |
| |
| // Wait until the request has completed or been canceled. Does not start the |
| // request. |
| void WaitForComplete() { |
| EXPECT_TRUE(task_runner_->RunsTasksInCurrentSequence()); |
| run_loop_->Run(); |
| } |
| |
| // Cancels the fetch by deleting the fetcher. |
| void CancelFetch() { |
| EXPECT_TRUE(fetcher_); |
| fetcher_.reset(); |
| std::move(on_complete_or_cancel_).Run(); |
| } |
| |
| // URLFetcherDelegate: |
| void OnURLFetchComplete(const URLFetcher* source) override { |
| EXPECT_FALSE(did_complete_); |
| EXPECT_TRUE(fetcher_); |
| EXPECT_EQ(fetcher_.get(), source); |
| did_complete_ = true; |
| std::move(on_complete_or_cancel_).Run(); |
| } |
| |
| void OnURLFetchDownloadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total, |
| int64_t current_network_bytes) override { |
| // Note that the current progress may be greater than the previous progress, |
| // in the case of retrying the request. |
| EXPECT_FALSE(did_complete_); |
| EXPECT_TRUE(fetcher_); |
| EXPECT_EQ(source, fetcher_.get()); |
| |
| EXPECT_LE(0, current); |
| // If file size is not known, |total| is -1. |
| if (total >= 0) |
| EXPECT_LE(current, total); |
| } |
| |
| void OnURLFetchUploadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total) override { |
| // Note that the current progress may be greater than the previous progress, |
| // in the case of retrying the request. |
| EXPECT_FALSE(did_complete_); |
| EXPECT_TRUE(fetcher_); |
| EXPECT_EQ(source, fetcher_.get()); |
| |
| EXPECT_LE(0, current); |
| // If file size is not known, |total| is -1. |
| if (total >= 0) |
| EXPECT_LE(current, total); |
| } |
| |
| bool did_complete() const { return did_complete_; } |
| |
| void set_on_complete_or_cancel_closure(base::OnceClosure closure) { |
| on_complete_or_cancel_ = std::move(closure); |
| } |
| |
| private: |
| bool did_complete_; |
| |
| std::unique_ptr<URLFetcherImpl> fetcher_; |
| const scoped_refptr<base::SequencedTaskRunner> task_runner_ = |
| base::SequencedTaskRunnerHandle::Get(); |
| std::unique_ptr<base::RunLoop> run_loop_; |
| base::OnceClosure on_complete_or_cancel_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaitingURLFetcherDelegate); |
| }; |
| |
| // A TestURLRequestContext with a ThrottleManager and a MockHostResolver. |
| class FetcherTestURLRequestContext : public TestURLRequestContext { |
| public: |
| // All requests for |hanging_domain| will hang on host resolution until the |
| // mock_resolver()->ResolveAllPending() is called. |
| FetcherTestURLRequestContext( |
| const std::string& hanging_domain, |
| std::unique_ptr<ProxyResolutionService> proxy_resolution_service) |
| : TestURLRequestContext(true), mock_resolver_(new MockHostResolver()) { |
| mock_resolver_->set_ondemand_mode(true); |
| mock_resolver_->rules()->AddRule(hanging_domain, "127.0.0.1"); |
| // Pass ownership to ContextStorage to ensure correct destruction order. |
| context_storage_.set_host_resolver( |
| std::unique_ptr<HostResolver>(mock_resolver_)); |
| context_storage_.set_throttler_manager( |
| std::make_unique<URLRequestThrottlerManager>()); |
| context_storage_.set_proxy_resolution_service( |
| std::move(proxy_resolution_service)); |
| Init(); |
| } |
| |
| MockHostResolver* mock_resolver() { return mock_resolver_; } |
| |
| private: |
| MockHostResolver* mock_resolver_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FetcherTestURLRequestContext); |
| }; |
| |
| class FetcherTestURLRequestContextGetter : public URLRequestContextGetter { |
| public: |
| FetcherTestURLRequestContextGetter( |
| scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
| const std::string& hanging_domain) |
| : network_task_runner_(network_task_runner), |
| hanging_domain_(hanging_domain), |
| shutting_down_(false) {} |
| |
| // Sets callback to be invoked when the getter is destroyed. |
| void set_on_destruction_callback(base::OnceClosure on_destruction_callback) { |
| on_destruction_callback_ = std::move(on_destruction_callback); |
| } |
| |
| // URLRequestContextGetter: |
| FetcherTestURLRequestContext* GetURLRequestContext() override { |
| // Calling this on the wrong thread may be either a bug in the test or a bug |
| // in production code. |
| EXPECT_TRUE(network_task_runner_->BelongsToCurrentThread()); |
| |
| if (shutting_down_) |
| return nullptr; |
| |
| if (!context_) { |
| context_.reset(new FetcherTestURLRequestContext( |
| hanging_domain_, std::move(proxy_resolution_service_))); |
| } |
| |
| return context_.get(); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() |
| const override { |
| return network_task_runner_; |
| } |
| |
| // Adds a throttler entry with the specified parameters. Does this |
| // synchronously if the context lives on the current thread, or posts a task |
| // to the relevant thread otherwise. |
| // |
| // If |reserve_sending_time_for_next_request|, will start backoff early, as |
| // if there has already been a request for |url|. |
| void AddThrottlerEntry(const GURL& url, |
| const std::string& url_id, |
| int sliding_window_period_ms, |
| int max_send_threshold, |
| int initial_backoff_ms, |
| double multiply_factor, |
| double jitter_factor, |
| int maximum_backoff_ms, |
| bool reserve_sending_time_for_next_request) { |
| if (!network_task_runner_->RunsTasksInCurrentSequence()) { |
| network_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FetcherTestURLRequestContextGetter::AddThrottlerEntry, |
| this, url, url_id, sliding_window_period_ms, |
| max_send_threshold, initial_backoff_ms, |
| multiply_factor, jitter_factor, maximum_backoff_ms, |
| reserve_sending_time_for_next_request)); |
| return; |
| } |
| scoped_refptr<URLRequestThrottlerEntry> entry(new URLRequestThrottlerEntry( |
| GetURLRequestContext()->throttler_manager(), url_id, |
| sliding_window_period_ms, max_send_threshold, initial_backoff_ms, |
| multiply_factor, jitter_factor, maximum_backoff_ms)); |
| |
| GetURLRequestContext()->throttler_manager()->OverrideEntryForTests( |
| url, entry.get()); |
| |
| if (reserve_sending_time_for_next_request) |
| entry->ReserveSendingTimeForNextRequest(base::TimeTicks()); |
| } |
| |
| // Tells the getter to act as if the URLRequestContext is about to be shut |
| // down. |
| void Shutdown() { |
| if (!network_task_runner_->RunsTasksInCurrentSequence()) { |
| network_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FetcherTestURLRequestContextGetter::Shutdown, this)); |
| return; |
| } |
| |
| shutting_down_ = true; |
| NotifyContextShuttingDown(); |
| // Should now be safe to destroy the context. Context will check it has no |
| // pending requests. |
| context_.reset(); |
| } |
| |
| // Convenience method to access the context as a FetcherTestURLRequestContext |
| // without going through GetURLRequestContext. |
| FetcherTestURLRequestContext* context() { |
| DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| return context_.get(); |
| } |
| |
| void set_proxy_resolution_service( |
| std::unique_ptr<ProxyResolutionService> proxy_resolution_service) { |
| DCHECK(proxy_resolution_service); |
| proxy_resolution_service_ = std::move(proxy_resolution_service); |
| } |
| |
| protected: |
| ~FetcherTestURLRequestContextGetter() override { |
| // |context_| may only be deleted on the network thread. Fortunately, |
| // the parent class already ensures it's deleted on the network thread. |
| DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| if (!on_destruction_callback_.is_null()) |
| std::move(on_destruction_callback_).Run(); |
| } |
| |
| private: |
| scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; |
| const std::string hanging_domain_; |
| |
| // May be null. |
| std::unique_ptr<ProxyResolutionService> proxy_resolution_service_; |
| |
| std::unique_ptr<FetcherTestURLRequestContext> context_; |
| bool shutting_down_; |
| |
| base::OnceClosure on_destruction_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FetcherTestURLRequestContextGetter); |
| }; |
| |
| } // namespace |
| |
| class URLFetcherTest : public TestWithScopedTaskEnvironment { |
| public: |
| URLFetcherTest() : num_upload_streams_created_(0) {} |
| |
| static int GetNumFetcherCores() { |
| return URLFetcherImpl::GetNumFetcherCores(); |
| } |
| |
| // Creates a URLRequestContextGetter with a URLRequestContext that lives on |
| // the current thread. |
| scoped_refptr<FetcherTestURLRequestContextGetter> |
| CreateSameThreadContextGetter() { |
| return scoped_refptr<FetcherTestURLRequestContextGetter>( |
| new FetcherTestURLRequestContextGetter( |
| base::ThreadTaskRunnerHandle::Get(), hanging_url().host())); |
| } |
| |
| // Creates a URLRequestContextGetter with a URLRequestContext that lives on |
| // a separate network thread. |
| scoped_refptr<FetcherTestURLRequestContextGetter> |
| CreateCrossThreadContextGetter() { |
| if (!network_thread_) { |
| network_thread_.reset(new base::Thread("network thread")); |
| base::Thread::Options network_thread_options; |
| network_thread_options.message_loop_type = base::MessageLoop::TYPE_IO; |
| bool result = network_thread_->StartWithOptions(network_thread_options); |
| CHECK(result); |
| } |
| |
| return scoped_refptr<FetcherTestURLRequestContextGetter>( |
| new FetcherTestURLRequestContextGetter(network_thread_->task_runner(), |
| hanging_url().host())); |
| } |
| |
| // Callback passed to URLFetcher to create upload stream by some tests. |
| std::unique_ptr<UploadDataStream> CreateUploadStream() { |
| ++num_upload_streams_created_; |
| std::vector<char> buffer( |
| kCreateUploadStreamBody, |
| kCreateUploadStreamBody + strlen(kCreateUploadStreamBody)); |
| return ElementsUploadDataStream::CreateWithReader( |
| std::unique_ptr<UploadElementReader>( |
| new UploadOwnedBytesElementReader(&buffer)), |
| 0); |
| } |
| |
| // Number of streams created by CreateUploadStream. |
| size_t num_upload_streams_created() const { |
| return num_upload_streams_created_; |
| } |
| |
| // Downloads |file_to_fetch| and checks the contents when done. If |
| // |save_to_temporary_file| is true, saves it to a temporary file, and |
| // |requested_out_path| is ignored. Otherwise, saves it to |
| // |requested_out_path|. Takes ownership of the file if |take_ownership| is |
| // true. Deletes file when done. |
| void SaveFileTest(const char* file_to_fetch, |
| bool save_to_temporary_file, |
| const base::FilePath& requested_out_path, |
| bool take_ownership) { |
| std::unique_ptr<WaitingURLFetcherDelegate> delegate( |
| new WaitingURLFetcherDelegate()); |
| delegate->CreateFetcher( |
| test_server_->GetURL(std::string(kTestServerFilePrefix) + |
| file_to_fetch), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| if (save_to_temporary_file) { |
| delegate->fetcher()->SaveResponseToTemporaryFile( |
| scoped_refptr<base::SequencedTaskRunner>( |
| base::SequencedTaskRunnerHandle::Get())); |
| } else { |
| delegate->fetcher()->SaveResponseToFileAtPath( |
| requested_out_path, scoped_refptr<base::SequencedTaskRunner>( |
| base::SequencedTaskRunnerHandle::Get())); |
| } |
| delegate->StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate->fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate->fetcher()->GetResponseCode()); |
| |
| base::FilePath out_path; |
| EXPECT_TRUE( |
| delegate->fetcher()->GetResponseAsFilePath(take_ownership, &out_path)); |
| if (!save_to_temporary_file) { |
| EXPECT_EQ(requested_out_path, out_path); |
| } |
| |
| base::FilePath server_root; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &server_root); |
| |
| EXPECT_TRUE(base::ContentsEqual( |
| server_root.Append(kDocRoot).AppendASCII(file_to_fetch), out_path)); |
| |
| // Delete the delegate and run the message loop to give the fetcher's |
| // destructor a chance to delete the file. |
| delegate.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // File should only exist if |take_ownership| was true. |
| EXPECT_EQ(take_ownership, base::PathExists(out_path)); |
| |
| // Cleanup. |
| if (base::PathExists(out_path)) |
| base::DeleteFile(out_path, false); |
| } |
| |
| // Returns a URL that hangs on DNS resolution when using a context created by |
| // the test fixture. |
| const GURL& hanging_url() const { return hanging_url_; } |
| |
| // testing::Test: |
| void SetUp() override { |
| SetUpServer(); |
| ASSERT_TRUE(test_server_->Start()); |
| |
| // URL that will hang when lookups reach the host resolver. |
| hanging_url_ = GURL(base::StringPrintf( |
| "http://example.com:%d%s", test_server_->host_port_pair().port(), |
| kDefaultResponsePath)); |
| ASSERT_TRUE(hanging_url_.is_valid()); |
| } |
| |
| // Initializes |test_server_| without starting it. Allows subclasses to use |
| // their own server configuration. |
| virtual void SetUpServer() { |
| test_server_.reset(new EmbeddedTestServer); |
| test_server_->AddDefaultHandlers(base::FilePath(kDocRoot)); |
| } |
| |
| // Network thread for cross-thread tests. Most threads just use the main |
| // thread for network activity. |
| std::unique_ptr<base::Thread> network_thread_; |
| |
| std::unique_ptr<EmbeddedTestServer> test_server_; |
| GURL hanging_url_; |
| |
| size_t num_upload_streams_created_; |
| }; |
| |
| namespace { |
| |
| // Version of URLFetcherTest that tests bad HTTPS requests. |
| class URLFetcherBadHTTPSTest : public URLFetcherTest { |
| public: |
| URLFetcherBadHTTPSTest() = default; |
| |
| // URLFetcherTest: |
| void SetUpServer() override { |
| test_server_.reset( |
| new EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS)); |
| test_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| test_server_->ServeFilesFromSourceDirectory("net/data/ssl"); |
| } |
| }; |
| |
| // Verifies that the fetcher succesfully fetches resources over proxy, and |
| // correctly returns the value of the proxy server used. |
| TEST_F(URLFetcherTest, FetchedUsingProxy) { |
| WaitingURLFetcherDelegate delegate; |
| |
| scoped_refptr<net::FetcherTestURLRequestContextGetter> context_getter = |
| CreateSameThreadContextGetter(); |
| |
| const net::ProxyServer proxy_server(ProxyServer::SCHEME_HTTP, |
| test_server_->host_port_pair()); |
| |
| std::unique_ptr<ProxyResolutionService> proxy_resolution_service = |
| ProxyResolutionService::CreateFixedFromPacResult( |
| proxy_server.ToPacString(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| context_getter->set_proxy_resolution_service( |
| std::move(proxy_resolution_service)); |
| |
| delegate.CreateFetcher( |
| GURL(std::string("http://does.not.resolve.test") + kDefaultResponsePath), |
| URLFetcher::GET, context_getter); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kDefaultResponseBody, data); |
| |
| EXPECT_EQ(proxy_server, delegate.fetcher()->ProxyServerUsed()); |
| } |
| |
| // Create the fetcher on the main thread. Since network IO will happen on the |
| // main thread, this will test URLFetcher's ability to do everything on one |
| // thread. |
| TEST_F(URLFetcherTest, SameThreadTest) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL(kDefaultResponsePath), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kDefaultResponseBody, data); |
| |
| EXPECT_EQ(static_cast<int64_t>(strlen(kDefaultResponseBody)), |
| delegate.fetcher()->GetReceivedResponseContentLength()); |
| std::string parsed_headers; |
| base::ReplaceChars(delegate.fetcher()->GetResponseHeaders()->raw_headers(), |
| std::string("\0", 1), "\n\r", &parsed_headers); |
| EXPECT_EQ(static_cast<int64_t>(parsed_headers.size() + |
| strlen(kDefaultResponseBody)), |
| delegate.fetcher()->GetTotalReceivedBytes()); |
| EXPECT_EQ(ProxyServer::SCHEME_DIRECT, |
| delegate.fetcher()->ProxyServerUsed().scheme()); |
| } |
| |
| // Create a separate thread that will create the URLFetcher. A separate thread |
| // acts as the network thread. |
| TEST_F(URLFetcherTest, DifferentThreadsTest) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL(kDefaultResponsePath), |
| URLFetcher::GET, CreateCrossThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kDefaultResponseBody, data); |
| } |
| |
| // Verifies that a URLFetcher works correctly on a TaskScheduler Sequence. |
| TEST_F(URLFetcherTest, SequencedTaskTest) { |
| auto sequenced_task_runner = base::CreateSequencedTaskRunnerWithTraits({}); |
| |
| // Since we cannot use StartFetchAndWait(), which runs a nested RunLoop owned |
| // by the Delegate, on a SchedulerWorker Sequence, this test is split into |
| // two Callbacks, both run on |sequenced_task_runner_|. The test main thread |
| // then runs its own RunLoop, which the second of the Callbacks will quit. |
| base::RunLoop run_loop; |
| |
| // Actually start the test fetch, on the Sequence. |
| sequenced_task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| [](scoped_refptr<FetcherTestURLRequestContextGetter> context_getter, |
| const GURL& response_path, base::OnceClosure quit_closure) { |
| std::unique_ptr<WaitingURLFetcherDelegate> delegate = |
| std::make_unique<WaitingURLFetcherDelegate>(); |
| WaitingURLFetcherDelegate* raw_delegate = delegate.get(); |
| |
| // Configure the delegate to run our |on_complete_closure_| rather |
| // than quitting its own |run_loop_|, on completion. |
| raw_delegate->set_on_complete_or_cancel_closure(base::BindOnce( |
| [](base::OnceClosure quit_closure, |
| std::unique_ptr<WaitingURLFetcherDelegate> delegate) { |
| EXPECT_TRUE(delegate->fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate->fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate->fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kDefaultResponseBody, data); |
| std::move(quit_closure).Run(); |
| }, |
| base::Passed(&quit_closure), base::Passed(&delegate))); |
| |
| raw_delegate->CreateFetcher(response_path, URLFetcher::GET, |
| context_getter); |
| raw_delegate->fetcher()->Start(); |
| }, |
| CreateCrossThreadContextGetter(), |
| test_server_->GetURL(kDefaultResponsePath), run_loop.QuitClosure())); |
| |
| run_loop.Run(); |
| RunUntilIdle(); |
| } |
| |
| // Tests to make sure CancelAll() will successfully cancel existing URLFetchers. |
| TEST_F(URLFetcherTest, CancelAll) { |
| EXPECT_EQ(0, GetNumFetcherCores()); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| // Force context creation. |
| context_getter->GetURLRequestContext(); |
| MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate.fetcher()->Start(); |
| // Wait for the request to reach the mock resolver and hang, to ensure the |
| // request has actually started. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| |
| EXPECT_EQ(1, URLFetcherTest::GetNumFetcherCores()); |
| URLFetcherImpl::CancelAll(); |
| EXPECT_EQ(0, URLFetcherTest::GetNumFetcherCores()); |
| } |
| |
| TEST_F(URLFetcherTest, DontRetryOnNetworkChangedByDefault) { |
| EXPECT_EQ(0, GetNumFetcherCores()); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| // Force context creation. |
| context_getter->GetURLRequestContext(); |
| MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // This posts a task to start the fetcher. |
| delegate.fetcher()->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The fetcher is now running, but is pending the host resolve. |
| EXPECT_EQ(1, GetNumFetcherCores()); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| ASSERT_FALSE(delegate.did_complete()); |
| |
| // A network change notification aborts the connect job. |
| NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| delegate.WaitForComplete(); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // And the owner of the fetcher gets the ERR_NETWORK_CHANGED error. |
| EXPECT_EQ(hanging_url(), delegate.fetcher()->GetOriginalURL()); |
| ASSERT_FALSE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), |
| IsError(ERR_NETWORK_CHANGED)); |
| } |
| |
| TEST_F(URLFetcherTest, RetryOnNetworkChangedAndFail) { |
| EXPECT_EQ(0, GetNumFetcherCores()); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| // Force context creation. |
| context_getter->GetURLRequestContext(); |
| MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate.fetcher()->SetAutomaticallyRetryOnNetworkChanges(3); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // This posts a task to start the fetcher. |
| delegate.fetcher()->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The fetcher is now running, but is pending the host resolve. |
| EXPECT_EQ(1, GetNumFetcherCores()); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| ASSERT_FALSE(delegate.did_complete()); |
| |
| // Make it fail 3 times. |
| for (int i = 0; i < 3; ++i) { |
| // A network change notification aborts the connect job. |
| NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // But the fetcher retries automatically. |
| EXPECT_EQ(1, GetNumFetcherCores()); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| ASSERT_FALSE(delegate.did_complete()); |
| } |
| |
| // A 4th failure doesn't trigger another retry, and propagates the error |
| // to the owner of the fetcher. |
| NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| delegate.WaitForComplete(); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // And the owner of the fetcher gets the ERR_NETWORK_CHANGED error. |
| EXPECT_EQ(hanging_url(), delegate.fetcher()->GetOriginalURL()); |
| ASSERT_FALSE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), |
| IsError(ERR_NETWORK_CHANGED)); |
| } |
| |
| TEST_F(URLFetcherTest, RetryOnNetworkChangedAndSucceed) { |
| EXPECT_EQ(0, GetNumFetcherCores()); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| // Force context creation. |
| context_getter->GetURLRequestContext(); |
| MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate.fetcher()->SetAutomaticallyRetryOnNetworkChanges(3); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // This posts a task to start the fetcher. |
| delegate.fetcher()->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The fetcher is now running, but is pending the host resolve. |
| EXPECT_EQ(1, GetNumFetcherCores()); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| ASSERT_FALSE(delegate.did_complete()); |
| |
| // Make it fail 3 times. |
| for (int i = 0; i < 3; ++i) { |
| // A network change notification aborts the connect job. |
| NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // But the fetcher retries automatically. |
| EXPECT_EQ(1, GetNumFetcherCores()); |
| EXPECT_TRUE(mock_resolver->has_pending_requests()); |
| ASSERT_FALSE(delegate.did_complete()); |
| } |
| |
| // Now let it succeed by resolving the pending request. |
| mock_resolver->ResolveAllPending(); |
| delegate.WaitForComplete(); |
| EXPECT_FALSE(mock_resolver->has_pending_requests()); |
| |
| // This time the request succeeded. |
| EXPECT_EQ(hanging_url(), delegate.fetcher()->GetOriginalURL()); |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kDefaultResponseBody, data); |
| } |
| |
| TEST_F(URLFetcherTest, PostString) { |
| const char kUploadData[] = "bobsyeruncle"; |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetUploadData("application/x-www-form-urlencoded", |
| kUploadData); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kUploadData, data); |
| } |
| |
| TEST_F(URLFetcherTest, PostEmptyString) { |
| const char kUploadData[] = ""; |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetUploadData("application/x-www-form-urlencoded", |
| kUploadData); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kUploadData, data); |
| } |
| |
| TEST_F(URLFetcherTest, PostEntireFile) { |
| base::FilePath upload_path = GetUploadFileTestPath(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetUploadFilePath("application/x-www-form-urlencoded", |
| upload_path, 0, |
| std::numeric_limits<uint64_t>::max(), |
| base::SequencedTaskRunnerHandle::Get()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| |
| std::string expected; |
| ASSERT_TRUE(base::ReadFileToString(upload_path, &expected)); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(expected, data); |
| } |
| |
| TEST_F(URLFetcherTest, PostFileRange) { |
| const size_t kRangeStart = 30; |
| const size_t kRangeLength = 100; |
| base::FilePath upload_path = GetUploadFileTestPath(); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetUploadFilePath("application/x-www-form-urlencoded", |
| upload_path, kRangeStart, kRangeLength, |
| base::SequencedTaskRunnerHandle::Get()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| |
| std::string expected; |
| ASSERT_TRUE(base::ReadFileToString(upload_path, &expected)); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(expected.substr(kRangeStart, kRangeLength), data); |
| } |
| |
| TEST_F(URLFetcherTest, PostWithUploadStreamFactory) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetUploadStreamFactory( |
| "text/plain", |
| base::Bind(&URLFetcherTest::CreateUploadStream, base::Unretained(this))); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kCreateUploadStreamBody, data); |
| EXPECT_EQ(1u, num_upload_streams_created()); |
| } |
| |
| TEST_F(URLFetcherTest, PostWithUploadStreamFactoryAndRetries) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo?status=500"), |
| URLFetcher::POST, CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetAutomaticallyRetryOn5xx(true); |
| delegate.fetcher()->SetMaxRetriesOn5xx(1); |
| delegate.fetcher()->SetUploadStreamFactory( |
| "text/plain", |
| base::Bind(&URLFetcherTest::CreateUploadStream, base::Unretained(this))); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(500, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kCreateUploadStreamBody, data); |
| EXPECT_EQ(2u, num_upload_streams_created()); |
| } |
| |
| // Tests simple chunked POST case. |
| TEST_F(URLFetcherTest, PostChunked) { |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateCrossThreadContextGetter()); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateCrossThreadContextGetter()); |
| |
| delegate.fetcher()->SetChunkedUpload("text/plain"); |
| |
| // This posts a task to start the fetcher. |
| delegate.fetcher()->Start(); |
| |
| delegate.fetcher()->AppendChunkToUpload(kCreateUploadStreamBody, false); |
| delegate.fetcher()->AppendChunkToUpload(kCreateUploadStreamBody, true); |
| |
| delegate.WaitForComplete(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(std::string(kCreateUploadStreamBody) + |
| std::string(kCreateUploadStreamBody), |
| data); |
| } |
| |
| // Tests that data can be appended to a request after it fails. This is needed |
| // because the consumer may try to append data to a request after it failed, but |
| // before the consumer learns that it failed. |
| TEST_F(URLFetcherTest, PostAppendChunkAfterError) { |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateCrossThreadContextGetter()); |
| |
| WaitingURLFetcherDelegate delegate; |
| // Request that will fail almost immediately after being started, due to using |
| // a reserved port. |
| delegate.CreateFetcher(GURL("http://127.0.0.1:7"), URLFetcher::POST, |
| context_getter); |
| |
| delegate.fetcher()->SetChunkedUpload("text/plain"); |
| |
| // This posts a task to start the fetcher. |
| delegate.fetcher()->Start(); |
| |
| // Give the request a chance to fail, and inform the fetcher of the failure, |
| // while blocking the current thread so the error doesn't reach the delegate. |
| base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
| |
| // Try to append data. |
| delegate.fetcher()->AppendChunkToUpload("kCreateUploadStreamBody", false); |
| delegate.fetcher()->AppendChunkToUpload("kCreateUploadStreamBody", true); |
| |
| delegate.WaitForComplete(); |
| |
| // Make sure the request failed, as expected. |
| EXPECT_FALSE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), |
| IsError(ERR_UNSAFE_PORT)); |
| } |
| |
| // Checks that upload progress increases over time, never exceeds what's already |
| // been sent, and adds a chunk whenever all previously appended chunks have |
| // been uploaded. |
| class CheckUploadProgressDelegate : public WaitingURLFetcherDelegate { |
| public: |
| CheckUploadProgressDelegate() |
| : chunk_(1 << 16, 'a'), num_chunks_appended_(0), last_seen_progress_(0) {} |
| ~CheckUploadProgressDelegate() override = default; |
| |
| void OnURLFetchUploadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total) override { |
| // Run default checks. |
| WaitingURLFetcherDelegate::OnURLFetchUploadProgress(source, current, total); |
| |
| EXPECT_LE(last_seen_progress_, current); |
| EXPECT_LE(current, bytes_appended()); |
| last_seen_progress_ = current; |
| MaybeAppendChunk(); |
| } |
| |
| // Append the next chunk if all previously appended chunks have been sent. |
| void MaybeAppendChunk() { |
| const int kNumChunks = 5; |
| if (last_seen_progress_ == bytes_appended() && |
| num_chunks_appended_ < kNumChunks) { |
| ++num_chunks_appended_; |
| fetcher()->AppendChunkToUpload(chunk_, |
| num_chunks_appended_ == kNumChunks); |
| } |
| } |
| |
| private: |
| int64_t bytes_appended() const { |
| return num_chunks_appended_ * chunk_.size(); |
| } |
| |
| const std::string chunk_; |
| |
| int64_t num_chunks_appended_; |
| int64_t last_seen_progress_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CheckUploadProgressDelegate); |
| }; |
| |
| TEST_F(URLFetcherTest, UploadProgress) { |
| CheckUploadProgressDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| // Use a chunked upload so that the upload can be paused after uploading data. |
| // Since upload progress uses a timer, the delegate may not receive any |
| // notification otherwise. |
| delegate.fetcher()->SetChunkedUpload("application/x-www-form-urlencoded"); |
| |
| delegate.fetcher()->Start(); |
| // Append the first chunk. Others will be appended automatically in response |
| // to OnURLFetchUploadProgress events. |
| delegate.MaybeAppendChunk(); |
| delegate.WaitForComplete(); |
| |
| // Make sure there are no pending events that cause problems when run. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| EXPECT_TRUE(delegate.did_complete()); |
| } |
| |
| // Checks that download progress never decreases, never exceeds file size, and |
| // that file size is correctly reported. |
| class CheckDownloadProgressDelegate : public WaitingURLFetcherDelegate { |
| public: |
| CheckDownloadProgressDelegate(int64_t file_size) |
| : file_size_(file_size), last_seen_progress_(0) {} |
| ~CheckDownloadProgressDelegate() override = default; |
| |
| void OnURLFetchDownloadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total, |
| int64_t current_network_bytes) override { |
| // Run default checks. |
| WaitingURLFetcherDelegate::OnURLFetchDownloadProgress( |
| source, current, total, current_network_bytes); |
| |
| EXPECT_LE(last_seen_progress_, current); |
| EXPECT_EQ(file_size_, total); |
| last_seen_progress_ = current; |
| } |
| |
| private: |
| int64_t file_size_; |
| int64_t last_seen_progress_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CheckDownloadProgressDelegate); |
| }; |
| |
| TEST_F(URLFetcherTest, DownloadProgress) { |
| // Get a file large enough to require more than one read into |
| // URLFetcher::Core's IOBuffer. |
| const char kFileToFetch[] = "animate1.gif"; |
| |
| std::string file_contents; |
| |
| base::FilePath server_root; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &server_root); |
| |
| ASSERT_TRUE(base::ReadFileToString( |
| server_root.Append(kDocRoot).AppendASCII(kFileToFetch), &file_contents)); |
| |
| CheckDownloadProgressDelegate delegate(file_contents.size()); |
| delegate.CreateFetcher( |
| test_server_->GetURL(std::string(kTestServerFilePrefix) + kFileToFetch), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(file_contents, data); |
| } |
| |
| class CancelOnUploadProgressDelegate : public WaitingURLFetcherDelegate { |
| public: |
| CancelOnUploadProgressDelegate() = default; |
| ~CancelOnUploadProgressDelegate() override = default; |
| |
| void OnURLFetchUploadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total) override { |
| CancelFetch(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CancelOnUploadProgressDelegate); |
| }; |
| |
| // Check that a fetch can be safely cancelled/deleted during an upload progress |
| // callback. |
| TEST_F(URLFetcherTest, CancelInUploadProgressCallback) { |
| CancelOnUploadProgressDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL("/echo"), URLFetcher::POST, |
| CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetChunkedUpload("application/x-www-form-urlencoded"); |
| delegate.fetcher()->Start(); |
| // Use a chunked upload so that the upload can be paused after uploading data. |
| // Since uploads progress uses a timer, may not receive any notification, |
| // otherwise. |
| std::string upload_data(1 << 16, 'a'); |
| delegate.fetcher()->AppendChunkToUpload(upload_data, false); |
| delegate.WaitForComplete(); |
| |
| // Make sure there are no pending events that cause problems when run. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(delegate.did_complete()); |
| EXPECT_FALSE(delegate.fetcher()); |
| } |
| |
| class CancelOnDownloadProgressDelegate : public WaitingURLFetcherDelegate { |
| public: |
| CancelOnDownloadProgressDelegate() = default; |
| ~CancelOnDownloadProgressDelegate() override = default; |
| |
| void OnURLFetchDownloadProgress(const URLFetcher* source, |
| int64_t current, |
| int64_t total, |
| int64_t current_network_bytes) override { |
| CancelFetch(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CancelOnDownloadProgressDelegate); |
| }; |
| |
| // Check that a fetch can be safely cancelled/deleted during a download progress |
| // callback. |
| TEST_F(URLFetcherTest, CancelInDownloadProgressCallback) { |
| // Get a file large enough to require more than one read into |
| // URLFetcher::Core's IOBuffer. |
| static const char kFileToFetch[] = "animate1.gif"; |
| CancelOnDownloadProgressDelegate delegate; |
| delegate.CreateFetcher( |
| test_server_->GetURL(std::string(kTestServerFilePrefix) + kFileToFetch), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| // Make sure there are no pending events that cause problems when run. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(delegate.did_complete()); |
| EXPECT_FALSE(delegate.fetcher()); |
| } |
| |
| TEST_F(URLFetcherTest, Headers) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher( |
| test_server_->GetURL("/set-header?cache-control: private"), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string header; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseHeaders()->GetNormalizedHeader( |
| "cache-control", &header)); |
| EXPECT_EQ("private", header); |
| } |
| |
| TEST_F(URLFetcherTest, SocketAddress) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL(kDefaultResponsePath), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| EXPECT_EQ(test_server_->host_port_pair().port(), |
| delegate.fetcher()->GetSocketAddress().port()); |
| EXPECT_EQ(test_server_->host_port_pair().host(), |
| delegate.fetcher()->GetSocketAddress().ToStringWithoutPort()); |
| } |
| |
| TEST_F(URLFetcherTest, StopOnRedirect) { |
| const char kRedirectTarget[] = "http://redirect.target.com"; |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher( |
| test_server_->GetURL(std::string("/server-redirect?") + kRedirectTarget), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.fetcher()->SetStopOnRedirect(true); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_EQ(GURL(kRedirectTarget), delegate.fetcher()->GetURL()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, |
| delegate.fetcher()->GetStatus().status()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), IsError(ERR_ABORTED)); |
| EXPECT_EQ(301, delegate.fetcher()->GetResponseCode()); |
| } |
| |
| TEST_F(URLFetcherTest, ThrottleOnRepeatedFetches) { |
| base::Time start_time = Time::Now(); |
| GURL url(test_server_->GetURL(kDefaultResponsePath)); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| |
| // Registers an entry for test url. It only allows 3 requests to be sent |
| // in 200 milliseconds. |
| context_getter->AddThrottlerEntry( |
| url, std::string() /* url_id */, 200 /* sliding_window_period_ms */, |
| 3 /* max_send_threshold */, 1 /* initial_backoff_ms */, |
| 2.0 /* multiply_factor */, 0.0 /* jitter_factor */, |
| 256 /* maximum_backoff_ms */, |
| false /* reserve_sending_time_for_next_request*/); |
| |
| for (int i = 0; i < 20; ++i) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(url, URLFetcher::GET, context_getter); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| } |
| |
| // 20 requests were sent. Due to throttling, they should have collectively |
| // taken over 1 second. |
| EXPECT_GE(Time::Now() - start_time, base::TimeDelta::FromSeconds(1)); |
| } |
| |
| // If throttling kicks in for a chunked upload, there should be no crash. |
| TEST_F(URLFetcherTest, ThrottleChunkedUpload) { |
| GURL url(test_server_->GetURL("/echo")); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| |
| // Registers an entry for test url. It only allows 3 requests to be sent |
| // in 200 milliseconds. |
| context_getter->AddThrottlerEntry( |
| url, std::string() /* url_id */, 200 /* sliding_window_period_ms */, |
| 3 /* max_send_threshold */, 1 /* initial_backoff_ms */, |
| 2.0 /* multiply_factor */, 0.0 /* jitter_factor */, |
| 256 /* maximum_backoff_ms */, |
| false /* reserve_sending_time_for_next_request*/); |
| |
| for (int i = 0; i < 20; ++i) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(url, URLFetcher::POST, context_getter); |
| delegate.fetcher()->SetChunkedUpload("text/plain"); |
| delegate.fetcher()->Start(); |
| delegate.fetcher()->AppendChunkToUpload(kCreateUploadStreamBody, true); |
| delegate.WaitForComplete(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ(kCreateUploadStreamBody, data); |
| } |
| } |
| |
| TEST_F(URLFetcherTest, ThrottleOn5xxRetries) { |
| base::Time start_time = Time::Now(); |
| GURL url(test_server_->GetURL("/server-unavailable.html")); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| |
| // 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. |
| context_getter->AddThrottlerEntry( |
| url, std::string() /* url_id */, 200 /* sliding_window_period_ms */, |
| 3 /* max_send_threshold */, 1 /* initial_backoff_ms */, |
| 2.0 /* multiply_factor */, 0.0 /* jitter_factor */, |
| 256 /* maximum_backoff_ms */, |
| false /* reserve_sending_time_for_next_request*/); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(url, URLFetcher::GET, context_getter); |
| delegate.fetcher()->SetAutomaticallyRetryOn5xx(true); |
| delegate.fetcher()->SetMaxRetriesOn5xx(11); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(503, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_FALSE(data.empty()); |
| |
| // The request should have been retried 11 times (12 times including the first |
| // attempt). Due to throttling, they should have collectively taken over 1 |
| // second. |
| EXPECT_GE(Time::Now() - start_time, base::TimeDelta::FromSeconds(1)); |
| } |
| |
| // Tests overload protection, when responses passed through. |
| TEST_F(URLFetcherTest, ProtectTestPassedThrough) { |
| base::Time start_time = Time::Now(); |
| GURL url(test_server_->GetURL("/server-unavailable.html")); |
| |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| |
| // 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. |
| // Total time if *not* for not doing automatic backoff would be 150s. |
| // In reality it should be "as soon as server responds". |
| context_getter->AddThrottlerEntry( |
| url, std::string() /* url_id */, 200 /* sliding_window_period_ms */, |
| 3 /* max_send_threshold */, 10000 /* initial_backoff_ms */, |
| 2.0 /* multiply_factor */, 0.0 /* jitter_factor */, |
| 150000 /* maximum_backoff_ms */, |
| false /* reserve_sending_time_for_next_request*/); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(url, URLFetcher::GET, context_getter); |
| delegate.fetcher()->SetAutomaticallyRetryOn5xx(false); |
| delegate.fetcher()->SetMaxRetriesOn5xx(11); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(503, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_FALSE(data.empty()); |
| EXPECT_GT(delegate.fetcher()->GetBackoffDelay().InMicroseconds(), 0); |
| |
| // The request should not have been retried at all. If it had attempted all |
| // 11 retries, that should have taken 2.5 minutes. |
| EXPECT_TRUE(Time::Now() - start_time < TimeDelta::FromMinutes(1)); |
| } |
| |
| // Used to check if a callback has been invoked. |
| void SetBoolToTrue(bool* ptr) { |
| *ptr = true; |
| } |
| |
| // Make sure that the URLFetcher cancels the URLRequest and releases its context |
| // getter pointer synchronously when the fetcher and request context live on |
| // the same thread. |
| TEST_F(URLFetcherTest, CancelSameThread) { |
| WaitingURLFetcherDelegate delegate; |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| bool getter_was_destroyed = false; |
| context_getter->set_on_destruction_callback( |
| base::Bind(&SetBoolToTrue, &getter_was_destroyed)); |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| |
| // The getter won't be destroyed if the test holds on to a reference to it. |
| context_getter = nullptr; |
| |
| delegate.fetcher()->Start(); |
| // Give the fetcher a chance to start the request. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(1, URLFetcherTest::GetNumFetcherCores()); |
| |
| // On same-thread cancel, the request should be canceled and getter destroyed |
| // synchronously, for safe shutdown. |
| delegate.CancelFetch(); |
| EXPECT_EQ(0, URLFetcherTest::GetNumFetcherCores()); |
| EXPECT_TRUE(getter_was_destroyed); |
| } |
| |
| // Make sure that the URLFetcher releases its context getter pointer on |
| // cancellation, cross-thread case. |
| TEST_F(URLFetcherTest, CancelDifferentThreads) { |
| base::RunLoop run_loop; |
| |
| WaitingURLFetcherDelegate delegate; |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateCrossThreadContextGetter()); |
| context_getter->set_on_destruction_callback( |
| base::BindOnce(base::IgnoreResult(&base::SequencedTaskRunner::PostTask), |
| base::SequencedTaskRunnerHandle::Get(), FROM_HERE, |
| run_loop.QuitClosure())); |
| delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| |
| // The getter won't be destroyed if the test holds on to a reference to it. |
| context_getter = nullptr; |
| |
| delegate.fetcher()->Start(); |
| delegate.CancelFetch(); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(delegate.did_complete()); |
| } |
| |
| TEST_F(URLFetcherTest, CancelWhileDelayedByThrottleDifferentThreads) { |
| GURL url = test_server_->GetURL(kDefaultResponsePath); |
| base::RunLoop run_loop; |
| |
| WaitingURLFetcherDelegate delegate; |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateCrossThreadContextGetter()); |
| context_getter->set_on_destruction_callback( |
| base::BindOnce(base::IgnoreResult(&base::SequencedTaskRunner::PostTask), |
| base::SequencedTaskRunnerHandle::Get(), FROM_HERE, |
| run_loop.QuitClosure())); |
| delegate.CreateFetcher(url, URLFetcher::GET, context_getter); |
| |
| // Register an entry for test url using a sliding window of 400 seconds, and |
| // max of 1 request. Also simulate a request having just started, so the |
| // next request will be affected by backoff of ~400 seconds. |
| context_getter->AddThrottlerEntry( |
| url, std::string() /* url_id */, 400000 /* sliding_window_period_ms */, |
| 1 /* max_send_threshold */, 200000 /* initial_backoff_ms */, |
| 2.0 /* multiply_factor */, 0.0 /* jitter_factor */, |
| 400000 /* maximum_backoff_ms */, |
| true /* reserve_sending_time_for_next_request*/); |
| |
| // The getter won't be destroyed if the test holds on to a reference to it. |
| context_getter = nullptr; |
| |
| delegate.fetcher()->Start(); |
| delegate.CancelFetch(); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(delegate.did_complete()); |
| } |
| |
| // A URLFetcherDelegate that expects to receive a response body of "request1" |
| // and then reuses the fetcher for the same URL, setting the "test" request |
| // header to "request2". |
| class ReuseFetcherDelegate : public WaitingURLFetcherDelegate { |
| public: |
| // |second_request_context_getter| is the context getter used for the second |
| // request. Can't reuse the old one because fetchers release it on completion. |
| ReuseFetcherDelegate( |
| scoped_refptr<URLRequestContextGetter> second_request_context_getter) |
| : first_request_complete_(false), |
| second_request_context_getter_(second_request_context_getter) {} |
| |
| ~ReuseFetcherDelegate() override = default; |
| |
| void OnURLFetchComplete(const URLFetcher* source) override { |
| EXPECT_EQ(fetcher(), source); |
| if (!first_request_complete_) { |
| first_request_complete_ = true; |
| EXPECT_TRUE(fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ("request1", data); |
| |
| fetcher()->SetRequestContext(second_request_context_getter_.get()); |
| fetcher()->SetExtraRequestHeaders("test: request2"); |
| fetcher()->Start(); |
| return; |
| } |
| WaitingURLFetcherDelegate::OnURLFetchComplete(source); |
| } |
| |
| private: |
| bool first_request_complete_; |
| scoped_refptr<URLRequestContextGetter> second_request_context_getter_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ReuseFetcherDelegate); |
| }; |
| |
| TEST_F(URLFetcherTest, ReuseFetcherForSameURL) { |
| // TODO(mmenke): It's really weird that this is supported, particularly |
| // some fields can be modified between requests, but some (Like upload body) |
| // cannot be. Can we get rid of support for this? |
| scoped_refptr<URLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| ReuseFetcherDelegate delegate(context_getter); |
| delegate.CreateFetcher(test_server_->GetURL("/echoheader?test"), |
| URLFetcher::GET, context_getter); |
| delegate.fetcher()->SetExtraRequestHeaders("test: request1"); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_EQ(200, delegate.fetcher()->GetResponseCode()); |
| std::string data; |
| ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_EQ("request2", data); |
| } |
| |
| TEST_F(URLFetcherTest, ShutdownSameThread) { |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateSameThreadContextGetter()); |
| |
| // Create a fetcher and wait for it to create a request. |
| WaitingURLFetcherDelegate delegate1; |
| delegate1.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate1.fetcher()->Start(); |
| // Need to spin the loop to ensure the URLRequest is created and started. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Create and start another fetcher, but don't wait for it to start. The task |
| // to start the request should be in the message loop. |
| WaitingURLFetcherDelegate delegate2; |
| delegate2.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate2.fetcher()->Start(); |
| |
| // Check that shutting down the getter cancels the request synchronously, |
| // allowing the context to be destroyed. |
| context_getter->Shutdown(); |
| |
| // Wait for the first fetcher, make sure it failed. |
| delegate1.WaitForComplete(); |
| EXPECT_FALSE(delegate1.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate1.fetcher()->GetStatus().error(), |
| IsError(ERR_CONTEXT_SHUT_DOWN)); |
| |
| // Wait for the second fetcher, make sure it failed. |
| delegate2.WaitForComplete(); |
| EXPECT_FALSE(delegate2.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate2.fetcher()->GetStatus().error(), |
| IsError(ERR_CONTEXT_SHUT_DOWN)); |
| |
| // New fetchers should automatically fail without making new requests. This |
| // should follow the same path as the second fetcher, but best to be safe. |
| WaitingURLFetcherDelegate delegate3; |
| delegate3.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate3.fetcher()->Start(); |
| delegate3.WaitForComplete(); |
| EXPECT_FALSE(delegate3.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate3.fetcher()->GetStatus().error(), |
| IsError(ERR_CONTEXT_SHUT_DOWN)); |
| } |
| |
| TEST_F(URLFetcherTest, ShutdownCrossThread) { |
| scoped_refptr<FetcherTestURLRequestContextGetter> context_getter( |
| CreateCrossThreadContextGetter()); |
| |
| WaitingURLFetcherDelegate delegate1; |
| delegate1.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate1.fetcher()->Start(); |
| // Check that shutting the context getter lets the context be destroyed safely |
| // and cancels the request. |
| context_getter->Shutdown(); |
| delegate1.WaitForComplete(); |
| EXPECT_FALSE(delegate1.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate1.fetcher()->GetStatus().error(), |
| IsError(ERR_CONTEXT_SHUT_DOWN)); |
| |
| // New requests should automatically fail without making new requests. |
| WaitingURLFetcherDelegate delegate2; |
| delegate2.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); |
| delegate2.StartFetcherAndWait(); |
| EXPECT_FALSE(delegate2.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate2.fetcher()->GetStatus().error(), |
| IsError(ERR_CONTEXT_SHUT_DOWN)); |
| } |
| |
| // Get a small file. |
| TEST_F(URLFetcherTest, FileTestSmallGet) { |
| const char kFileToFetch[] = "simple.html"; |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath out_path = temp_dir.GetPath().AppendASCII(kFileToFetch); |
| SaveFileTest(kFileToFetch, false, out_path, false); |
| } |
| |
| // Get a file large enough to require more than one read into URLFetcher::Core's |
| // IOBuffer. |
| TEST_F(URLFetcherTest, FileTestLargeGet) { |
| const char kFileToFetch[] = "animate1.gif"; |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath out_path = temp_dir.GetPath().AppendASCII(kFileToFetch); |
| SaveFileTest(kFileToFetch, false, out_path, false); |
| } |
| |
| // If the caller takes the ownership of the output file, the file should persist |
| // even after URLFetcher is gone. |
| TEST_F(URLFetcherTest, FileTestTakeOwnership) { |
| const char kFileToFetch[] = "simple.html"; |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath out_path = temp_dir.GetPath().AppendASCII(kFileToFetch); |
| SaveFileTest(kFileToFetch, false, out_path, true); |
| } |
| |
| // Test that an existing file can be overwritten be a fetcher. |
| TEST_F(URLFetcherTest, FileTestOverwriteExisting) { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| // Create a file before trying to fetch. |
| const char kFileToFetch[] = "simple.html"; |
| std::string data(10000, '?'); // Meant to be larger than simple.html. |
| base::FilePath out_path = temp_dir.GetPath().AppendASCII(kFileToFetch); |
| ASSERT_EQ(static_cast<int>(data.size()), |
| base::WriteFile(out_path, data.data(), data.size())); |
| ASSERT_TRUE(base::PathExists(out_path)); |
| |
| SaveFileTest(kFileToFetch, false, out_path, true); |
| } |
| |
| // Test trying to overwrite a directory with a file when using a fetcher fails. |
| TEST_F(URLFetcherTest, FileTestTryToOverwriteDirectory) { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| // Create a directory before trying to fetch. |
| static const char kFileToFetch[] = "simple.html"; |
| base::FilePath out_path = temp_dir.GetPath().AppendASCII(kFileToFetch); |
| ASSERT_TRUE(base::CreateDirectory(out_path)); |
| ASSERT_TRUE(base::PathExists(out_path)); |
| |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher( |
| test_server_->GetURL(std::string(kTestServerFilePrefix) + kFileToFetch), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.fetcher()->SaveResponseToFileAtPath( |
| out_path, scoped_refptr<base::SequencedTaskRunner>( |
| base::SequencedTaskRunnerHandle::Get())); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_FALSE(delegate.fetcher()->GetStatus().is_success()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), |
| IsError(ERR_ACCESS_DENIED)); |
| } |
| |
| // Get a small file and save it to a temp file. |
| TEST_F(URLFetcherTest, TempFileTestSmallGet) { |
| SaveFileTest("simple.html", true, base::FilePath(), false); |
| } |
| |
| // Get a file large enough to require more than one read into URLFetcher::Core's |
| // IOBuffer and save it to a temp file. |
| TEST_F(URLFetcherTest, TempFileTestLargeGet) { |
| SaveFileTest("animate1.gif", true, base::FilePath(), false); |
| } |
| |
| // If the caller takes the ownership of the temp file, check that the file |
| // persists even after URLFetcher is gone. |
| TEST_F(URLFetcherTest, TempFileTestTakeOwnership) { |
| SaveFileTest("simple.html", true, base::FilePath(), true); |
| } |
| |
| TEST_F(URLFetcherBadHTTPSTest, BadHTTPS) { |
| WaitingURLFetcherDelegate delegate; |
| delegate.CreateFetcher(test_server_->GetURL(kDefaultResponsePath), |
| URLFetcher::GET, CreateSameThreadContextGetter()); |
| delegate.StartFetcherAndWait(); |
| |
| EXPECT_EQ(URLRequestStatus::CANCELED, |
| delegate.fetcher()->GetStatus().status()); |
| EXPECT_THAT(delegate.fetcher()->GetStatus().error(), IsError(ERR_ABORTED)); |
| EXPECT_EQ(-1, delegate.fetcher()->GetResponseCode()); |
| EXPECT_FALSE(delegate.fetcher()->GetResponseHeaders()); |
| std::string data; |
| EXPECT_TRUE(delegate.fetcher()->GetResponseAsString(&data)); |
| EXPECT_TRUE(data.empty()); |
| } |
| |
| } // namespace |
| |
| } // namespace net |