Index: content/browser/loader/mojo_async_resource_handler_unittest.cc |
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c1118bf330a236d599139911badc4eb4569bb3c1 |
--- /dev/null |
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc |
@@ -0,0 +1,839 @@ |
+// Copyright 2016 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/browser/loader/mojo_async_resource_handler.h" |
+ |
+#include <string.h> |
+ |
+#include <utility> |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "base/files/file_path.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/memory/scoped_vector.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/run_loop.h" |
+#include "content/browser/loader/mojo_async_resource_handler_test_util.h" |
+#include "content/browser/loader/resource_dispatcher_host_impl.h" |
+#include "content/browser/loader/resource_request_info_impl.h" |
+#include "content/common/resource_request_completion_status.h" |
+#include "content/common/url_loader.mojom.h" |
+#include "content/public/browser/appcache_service.h" |
+#include "content/public/browser/navigation_data.h" |
+#include "content/public/browser/resource_context.h" |
+#include "content/public/browser/resource_controller.h" |
+#include "content/public/browser/resource_dispatcher_host_delegate.h" |
+#include "content/public/browser/resource_throttle.h" |
+#include "content/public/browser/stream_info.h" |
+#include "content/public/common/resource_response.h" |
+#include "content/public/common/resource_type.h" |
+#include "content/public/test/test_browser_context.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
+#include "mojo/public/c/system/data_pipe.h" |
+#include "mojo/public/c/system/types.h" |
+#include "mojo/public/cpp/system/data_pipe.h" |
+#include "net/base/auth.h" |
+#include "net/base/net_errors.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_response_info.h" |
+#include "net/http/http_status_code.h" |
+#include "net/http/http_util.h" |
+#include "net/ssl/client_cert_store.h" |
+#include "net/test/url_request/url_request_failed_job.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_filter.h" |
+#include "net/url_request/url_request_status.h" |
+#include "net/url_request/url_request_test_util.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/base/page_transition_types.h" |
+ |
+namespace content { |
+namespace { |
+ |
+class FakeResourceDispatcherHostDelegate |
+ : public ResourceDispatcherHostDelegate { |
+ public: |
+ FakeResourceDispatcherHostDelegate() {} |
+ ~FakeResourceDispatcherHostDelegate() override {} |
+ |
+ bool ShouldBeginRequest(const std::string& method, |
+ const GURL& url, |
+ ResourceType resource_type, |
+ ResourceContext* resource_context) override { |
+ ADD_FAILURE() << "ShouldBeginRequest should not be called."; |
+ return false; |
+ } |
+ |
+ void RequestBeginning(net::URLRequest* request, |
+ ResourceContext* resource_context, |
+ AppCacheService* appcache_service, |
+ ResourceType resource_type, |
+ ScopedVector<ResourceThrottle>* throttles) override { |
+ ADD_FAILURE() << "RequestBeginning should not be called."; |
+ } |
+ |
+ void DownloadStarting(net::URLRequest* request, |
+ ResourceContext* resource_context, |
+ int child_id, |
+ int route_id, |
+ bool is_content_initiated, |
+ bool must_download, |
+ ScopedVector<ResourceThrottle>* throttles) override { |
+ ADD_FAILURE() << "DownloadStarting should not be called."; |
+ } |
+ |
+ ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( |
+ net::AuthChallengeInfo* auth_info, |
+ net::URLRequest* request) override { |
+ ADD_FAILURE() << "CreateLoginDelegate should not be called."; |
+ return nullptr; |
+ } |
+ |
+ bool HandleExternalProtocol( |
+ const GURL& url, |
+ int child_id, |
+ const ResourceRequestInfo::WebContentsGetter& web_contents_getter, |
+ bool is_main_frame, |
+ ui::PageTransition page_transition, |
+ bool has_user_gesture, |
+ ResourceContext* resource_context) override { |
+ ADD_FAILURE() << "HandleExternalProtocol should not be called."; |
+ return false; |
+ } |
+ |
+ bool ShouldForceDownloadResource(const GURL& url, |
+ const std::string& mime_type) override { |
+ ADD_FAILURE() << "ShouldForceDownloadResource should not be called."; |
+ return false; |
+ } |
+ |
+ bool ShouldInterceptResourceAsStream(net::URLRequest* request, |
+ const base::FilePath& plugin_path, |
+ const std::string& mime_type, |
+ GURL* origin, |
+ std::string* payload) override { |
+ ADD_FAILURE() << "ShouldInterceptResourceAsStream should not be called."; |
+ return false; |
+ } |
+ |
+ void OnStreamCreated(net::URLRequest* request, |
+ std::unique_ptr<content::StreamInfo> stream) override { |
+ ADD_FAILURE() << "OnStreamCreated should not be called."; |
+ } |
+ |
+ void OnResponseStarted(net::URLRequest* request, |
+ ResourceContext* resource_context, |
+ ResourceResponse* response) override { |
+ is_on_response_started_called_ = true; |
+ } |
+ |
+ void OnRequestRedirected(const GURL& redirect_url, |
+ net::URLRequest* request, |
+ ResourceContext* resource_context, |
+ ResourceResponse* response) override { |
+ ADD_FAILURE() << "OnRequestRedirected should not be called."; |
+ } |
+ |
+ void RequestComplete(net::URLRequest* url_request) override { |
+ ADD_FAILURE() << "RequestComplete should not be called."; |
+ } |
+ |
+ bool ShouldEnableLoFiMode( |
+ const net::URLRequest& url_request, |
+ content::ResourceContext* resource_context) override { |
+ ADD_FAILURE() << "ShouldEnableLoFiMode should not be called."; |
+ return false; |
+ } |
+ |
+ NavigationData* GetNavigationData(net::URLRequest* request) const override { |
+ ADD_FAILURE() << "GetNavigationData should not be called."; |
+ return nullptr; |
+ } |
+ |
+ std::unique_ptr<net::ClientCertStore> CreateClientCertStore( |
+ ResourceContext* resource_context) override { |
+ ADD_FAILURE() << "CreateClientCertStore should not be called."; |
+ return nullptr; |
+ } |
+ |
+ bool is_on_response_started_called() const { |
+ return is_on_response_started_called_; |
+ } |
+ |
+ private: |
+ bool is_on_response_started_called_ = false; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FakeResourceDispatcherHostDelegate); |
+}; |
+ |
+class FakeResourceController : public ResourceController { |
+ public: |
+ FakeResourceController() {} |
+ ~FakeResourceController() override {} |
+ |
+ void Cancel() override { ADD_FAILURE() << "Cancel should not be called."; } |
+ void CancelAndIgnore() override { |
+ ADD_FAILURE() << "CancelAndIgnore should not be called."; |
+ } |
+ void CancelWithError(int error_code) override { |
+ is_cancel_with_error_called_ = true; |
+ error_ = error_code; |
+ if (quit_closure_) |
+ quit_closure_.Run(); |
+ } |
+ void Resume() override { |
+ ++num_resume_calls_; |
+ if (quit_closure_) |
+ quit_closure_.Run(); |
+ } |
+ void set_quit_closure(const base::Closure& quit_closure) { |
+ quit_closure_ = quit_closure; |
+ } |
+ |
+ bool is_cancel_with_error_called() const { |
+ return is_cancel_with_error_called_; |
+ } |
+ int error() const { return error_; } |
+ unsigned num_resume_calls() const { return num_resume_calls_; } |
+ |
+ private: |
+ bool is_cancel_with_error_called_ = false; |
+ int error_ = net::OK; |
+ int num_resume_calls_ = 0; |
+ base::Closure quit_closure_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FakeResourceController); |
+}; |
+ |
+class MojoAsyncResourceHandlerWithCustomDataPipeOperations |
+ : public MojoAsyncResourceHandler { |
+ public: |
+ MojoAsyncResourceHandlerWithCustomDataPipeOperations( |
+ net::URLRequest* request, |
+ ResourceDispatcherHostImpl* rdh, |
+ mojo::InterfaceRequest<mojom::URLLoader> mojo_request, |
+ mojom::URLLoaderClientPtr url_loader_client) |
+ : MojoAsyncResourceHandler(request, |
+ rdh, |
+ std::move(mojo_request), |
+ std::move(url_loader_client)) {} |
+ ~MojoAsyncResourceHandlerWithCustomDataPipeOperations() override {} |
+ |
+ void set_begin_write_expectation(MojoResult begin_write_expectation) { |
+ is_begin_write_expectation_set_ = true; |
+ begin_write_expectation_ = begin_write_expectation; |
+ } |
+ void set_end_write_expectation(MojoResult end_write_expectation) { |
+ is_end_write_expectation_set_ = true; |
+ end_write_expectation_ = end_write_expectation; |
+ } |
+ |
+ private: |
+ MojoResult BeginWrite(void** data, uint32_t* available) override { |
+ if (is_begin_write_expectation_set_) |
+ return begin_write_expectation_; |
+ return MojoAsyncResourceHandler::BeginWrite(data, available); |
+ } |
+ MojoResult EndWrite(uint32_t written) override { |
+ if (is_end_write_expectation_set_) |
+ return end_write_expectation_; |
+ return MojoAsyncResourceHandler::EndWrite(written); |
+ } |
+ |
+ bool is_begin_write_expectation_set_ = false; |
+ bool is_end_write_expectation_set_ = false; |
+ MojoResult begin_write_expectation_ = MOJO_RESULT_UNKNOWN; |
+ MojoResult end_write_expectation_ = MOJO_RESULT_UNKNOWN; |
+ |
+ DISALLOW_COPY_AND_ASSIGN( |
+ MojoAsyncResourceHandlerWithCustomDataPipeOperations); |
+}; |
+ |
+class MojoAsyncResourceHandlerTestBase { |
+ public: |
+ MojoAsyncResourceHandlerTestBase() |
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), |
+ browser_context_(new TestBrowserContext()) { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(32 * 1024); |
+ |
+ // Calling this function creates a request context. |
+ browser_context_->GetResourceContext()->GetRequestContext(); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ url_request_delegate_.reset(new net::TestDelegate()); |
+ net::URLRequestContext* request_context = |
+ browser_context_->GetResourceContext()->GetRequestContext(); |
+ request_ = request_context->CreateRequest( |
+ net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_TIMED_OUT), |
+ net::DEFAULT_PRIORITY, url_request_delegate_.get()); |
+ ResourceRequestInfo::AllocateForTesting( |
+ request_.get(), RESOURCE_TYPE_XHR, |
+ browser_context_->GetResourceContext(), 2, 0, 0, true, false, false, |
+ true, false); |
+ handler_.reset(new MojoAsyncResourceHandlerWithCustomDataPipeOperations( |
+ request_.get(), &rdh_, nullptr, |
+ url_loader_client_.CreateInterfacePtrAndBind())); |
+ handler_->SetController(&resource_controller_); |
+ } |
+ |
+ virtual ~MojoAsyncResourceHandlerTestBase() { |
+ rdh_.SetDelegate(nullptr); |
+ net::URLRequestFilter::GetInstance()->ClearHandlers(); |
+ } |
+ |
+ void GetContexts(ResourceType resource_type, |
+ int origin_pid, |
+ ResourceContext** resource_context, |
+ net::URLRequestContext** request_context) { |
+ *resource_context = browser_context_->GetResourceContext(); |
+ *request_context = |
+ browser_context_->GetResourceContext()->GetRequestContext(); |
+ } |
+ |
+ void RunUntilNextNotification() { |
+ base::RunLoop run_loop; |
+ url_loader_client_.set_quit_closure(run_loop.QuitClosure()); |
+ resource_controller_.set_quit_closure(run_loop.QuitClosure()); |
+ run_loop.Run(); |
+ } |
+ |
+ TestBrowserThreadBundle thread_bundle_; |
+ ResourceDispatcherHostImpl rdh_; |
+ FakeURLLoaderClient url_loader_client_; |
+ FakeResourceController resource_controller_; |
+ std::unique_ptr<TestBrowserContext> browser_context_; |
+ std::unique_ptr<net::TestDelegate> url_request_delegate_; |
+ std::unique_ptr<net::URLRequest> request_; |
+ std::unique_ptr<MojoAsyncResourceHandlerWithCustomDataPipeOperations> |
+ handler_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MojoAsyncResourceHandlerTestBase); |
+}; |
+ |
+class MojoAsyncResourceHandlerTest : public MojoAsyncResourceHandlerTestBase, |
+ public ::testing::Test {}; |
+ |
+// This test class is parametrized with MojoAsyncResourceHandler's allocation |
+// size. |
+class MojoAsyncResourceHandlerWithAllocationSizeTest |
+ : public MojoAsyncResourceHandlerTestBase, |
+ public ::testing::TestWithParam<size_t> { |
+ protected: |
+ MojoAsyncResourceHandlerWithAllocationSizeTest() { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(GetParam()); |
+ } |
+}; |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, InFlightRequests) { |
+ EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
+ handler_ = nullptr; |
+ EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnResponseStarted) { |
+ scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
+ FakeResourceDispatcherHostDelegate rdh_delegate; |
+ response->head.content_length = 99; |
+ response->head.request_start = |
+ base::TimeTicks::UnixEpoch() + base::TimeDelta::FromDays(14); |
+ response->head.response_start = |
+ base::TimeTicks::UnixEpoch() + base::TimeDelta::FromDays(28); |
+ |
+ bool defer = false; |
+ |
+ base::TimeTicks now1 = base::TimeTicks::Now(); |
+ rdh_.SetDelegate(&rdh_delegate); |
+ ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
+ rdh_.SetDelegate(nullptr); |
+ base::TimeTicks now2 = base::TimeTicks::Now(); |
+ |
+ EXPECT_FALSE(defer); |
+ EXPECT_EQ(request_->creation_time(), response->head.request_start); |
+ EXPECT_LE(now1, response->head.response_start); |
+ EXPECT_LE(response->head.response_start, now2); |
+ EXPECT_TRUE(rdh_delegate.is_on_response_started_called()); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_response()); |
+ EXPECT_EQ(response->head.request_start, |
+ url_loader_client_.response_head().request_start); |
+ EXPECT_EQ(response->head.response_start, |
+ url_loader_client_.response_head().response_start); |
+ EXPECT_EQ(99, url_loader_client_.response_head().content_length); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnWillStart) { |
+ bool defer = false; |
+ EXPECT_TRUE(handler_->OnWillStart(request_->url(), &defer)); |
+ EXPECT_FALSE(defer); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnBeforeNetworkStart) { |
+ bool defer = false; |
+ EXPECT_TRUE(handler_->OnBeforeNetworkStart(request_->url(), &defer)); |
+ EXPECT_FALSE(defer); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnWillReadAndInFlightRequests) { |
+ EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
+ scoped_refptr<net::IOBuffer> buffer; |
+ int buf_size; |
+ EXPECT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
+ EXPECT_EQ(1, rdh_.num_in_flight_requests_for_testing()); |
+ handler_ = nullptr; |
+ EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnWillReadWithInsufficientResource) { |
+ rdh_.set_max_num_in_flight_requests_per_process(0); |
+ |
+ scoped_refptr<net::IOBuffer> buffer; |
+ int buf_size = 0; |
+ EXPECT_FALSE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
+ EXPECT_FALSE(buffer); |
+ EXPECT_EQ(0, buf_size); |
+ EXPECT_EQ(1, rdh_.num_in_flight_requests_for_testing()); |
+ EXPECT_TRUE(resource_controller_.is_cancel_with_error_called()); |
+ EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, resource_controller_.error()); |
+ handler_ = nullptr; |
+ EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnWillReadAndOnReadCompleted) { |
+ bool defer = false; |
+ scoped_refptr<net::IOBuffer> buffer; |
+ int buf_size; |
+ |
+ ASSERT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
+ ASSERT_TRUE(buffer); |
+ // The buffer size that the mime sniffer requires implicitly. |
+ ASSERT_GE(buf_size, 2 * 1024); |
+ |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ |
+ buffer->data()[0] = 'A'; |
+ buffer->data()[1] = 'B'; |
+ ASSERT_TRUE(handler_->OnReadCompleted(2, &defer)); |
+ EXPECT_FALSE(defer); |
+ |
+ std::string contents; |
+ do { |
+ char buffer[16]; |
+ uint32_t read = sizeof(buffer); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ if (r == MOJO_RESULT_SHOULD_WAIT) |
+ continue; |
+ contents += std::string(buffer, read); |
+ } while (contents.size() < 2); |
+ EXPECT_EQ("AB", contents); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, |
+ OnWillReadAndOnReadCompletedWithInsufficientInitialCapacity) { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
+ |
+ bool defer = false; |
+ scoped_refptr<net::IOBuffer> buffer; |
+ int buf_size; |
+ |
+ ASSERT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
+ ASSERT_TRUE(buffer); |
+ // The buffer size that the mime sniffer requires implicitly. |
+ ASSERT_GE(buf_size, 2 * 1024); |
+ |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ |
+ const std::string data("abcdefgh"); |
+ strcpy(buffer->data(), data.c_str()); |
+ ASSERT_TRUE(handler_->OnReadCompleted(data.size(), &defer)); |
+ EXPECT_TRUE(defer); |
+ |
+ std::string contents; |
+ do { |
+ // This is needed for ResumeIfDeferred to be called. |
+ base::RunLoop().RunUntilIdle(); |
+ char buffer[16]; |
+ uint32_t read = sizeof(buffer); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ if (r == MOJO_RESULT_SHOULD_WAIT) |
+ continue; |
+ ASSERT_EQ(MOJO_RESULT_OK, r); |
+ contents += std::string(buffer, read); |
+ } while (contents.size() < data.size()); |
+ EXPECT_EQ(data, contents); |
+ EXPECT_EQ(0u, resource_controller_.num_resume_calls()); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, |
+ IOBufferFromOnWillReadShouldRemainValidEvenIfHandlerIsGone) { |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size; |
+ |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ ASSERT_TRUE(io_buffer); |
+ // The io_buffer size that the mime sniffer requires implicitly. |
+ ASSERT_GE(buf_size, 2 * 1024); |
+ handler_ = nullptr; |
+ |
mmenke
2016/07/14 21:23:05
Any reason to delete url_loader_client_, too? Gue
|
+ // Hopefully ASAN checks this operation's validity. |
+ io_buffer->data()[0] = 'A'; |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompleted) { |
+ scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
+ EXPECT_FALSE(defer); |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_response()); |
+ |
+ handler_->GetRequestInfoForTesting()->set_was_ignored_by_handler(false); |
+ net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); |
+ std::string security_info = "info0"; |
+ |
+ base::TimeTicks now1 = base::TimeTicks::Now(); |
+ handler_->OnResponseCompleted(status, security_info, &defer); |
+ base::TimeTicks now2 = base::TimeTicks::Now(); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code); |
+ EXPECT_FALSE(url_loader_client_.completion_status().was_ignored_by_handler); |
+ EXPECT_EQ("info0", url_loader_client_.completion_status().security_info); |
+ EXPECT_LE(now1, url_loader_client_.completion_status().completion_time); |
+ EXPECT_LE(url_loader_client_.completion_status().completion_time, now2); |
+ EXPECT_EQ(request_->GetTotalReceivedBytes(), |
+ url_loader_client_.completion_status().encoded_data_length); |
+} |
+ |
+// This test case sets different status values from OnResponseCompleted. |
+TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompleted2) { |
+ scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
+ EXPECT_FALSE(defer); |
+ |
+ handler_->GetRequestInfoForTesting()->set_was_ignored_by_handler(true); |
+ net::URLRequestStatus status(net::URLRequestStatus::CANCELED, |
+ net::ERR_ABORTED); |
+ std::string security_info = "info1"; |
+ |
+ base::TimeTicks now1 = base::TimeTicks::Now(); |
+ handler_->OnResponseCompleted(status, security_info, &defer); |
+ base::TimeTicks now2 = base::TimeTicks::Now(); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::ERR_ABORTED, |
+ url_loader_client_.completion_status().error_code); |
+ EXPECT_TRUE(url_loader_client_.completion_status().was_ignored_by_handler); |
+ EXPECT_EQ("info1", url_loader_client_.completion_status().security_info); |
+ EXPECT_LE(now1, url_loader_client_.completion_status().completion_time); |
+ EXPECT_LE(url_loader_client_.completion_status().completion_time, now2); |
+ EXPECT_EQ(request_->GetTotalReceivedBytes(), |
+ url_loader_client_.completion_status().encoded_data_length); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompletedWithCanceledTimedOut) { |
+ net::URLRequestStatus status(net::URLRequestStatus::CANCELED, |
+ net::ERR_TIMED_OUT); |
+ bool defer = false; |
+ |
+ handler_->OnResponseCompleted(status, "security_info", &defer); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::ERR_TIMED_OUT, |
+ url_loader_client_.completion_status().error_code); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompletedWithFailedTimedOut) { |
+ net::URLRequestStatus status(net::URLRequestStatus::FAILED, |
+ net::ERR_TIMED_OUT); |
+ bool defer = false; |
+ |
+ handler_->OnResponseCompleted(status, "security_info", &defer); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::ERR_TIMED_OUT, |
+ url_loader_client_.completion_status().error_code); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, ResponseCompletionShouldCloseDataPipe) { |
+ scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
+ EXPECT_FALSE(defer); |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_response()); |
+ |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
+ EXPECT_FALSE(defer); |
+ |
+ net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); |
+ handler_->OnResponseCompleted(status, "security_info", &defer); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code); |
+ |
+ // This is needed because |*io_buffer| may keep the data producer alive. |
+ io_buffer = nullptr; |
+ |
+ char buffer[16]; |
+ uint32_t read = sizeof(buffer); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, |
+ mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, &read, |
+ MOJO_READ_DATA_FLAG_NONE)); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, ResponseErrorDuringBodyTransmission) { |
+ scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
+ EXPECT_FALSE(defer); |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_response()); |
+ |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ ASSERT_GT(buf_size, 0); |
+ memset(io_buffer->data(), 'a', buf_size); |
+ ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
+ |
+ defer = false; |
+ net::URLRequestStatus status(net::URLRequestStatus::FAILED, net::ERR_FAILED); |
+ handler_->OnResponseCompleted(status, "security_info", &defer); |
+ EXPECT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ EXPECT_TRUE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::ERR_FAILED, url_loader_client_.completion_status().error_code); |
+ |
+ // This is needed because |*io_buffer| may keep the data producer alive. |
+ io_buffer = nullptr; |
+ |
+ std::string actual; |
+ while (true) { |
+ char buf[16]; |
+ uint32_t read = sizeof(buf); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ if (r == MOJO_RESULT_FAILED_PRECONDITION) |
+ break; |
+ if (r == MOJO_RESULT_SHOULD_WAIT) |
+ continue; |
+ EXPECT_EQ(MOJO_RESULT_OK, r); |
+ actual += std::string(buf, read); |
+ } |
+ EXPECT_EQ(std::string(buf_size, 'a'), actual); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, BeginWriteFailsOnWillRead) { |
+ handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ EXPECT_FALSE(resource_controller_.is_cancel_with_error_called()); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, BeginWriteReturnsShouldWaitOnWillRead) { |
+ handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ EXPECT_TRUE(io_buffer); |
+ EXPECT_GT(buf_size, 0); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, |
+ EndWriteFailsOnWillReadWithInsufficientInitialCapacity) { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
+ handler_->set_end_write_expectation(MOJO_RESULT_UNKNOWN); |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, EndWriteFailsOnReadCompleted) { |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ |
+ handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
+ ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, |
+ EndWriteFailsOnReadCompletedWithInsufficientInitialCapacity) { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ |
+ handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
+ ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
+} |
+ |
+TEST_F(MojoAsyncResourceHandlerTest, |
+ EndWriteFailsOnResumeIfDeferredWithInsufficientInitialCapacity) { |
+ MojoAsyncResourceHandler::SetAllocationSizeForTesting(8); |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ |
+ while (true) { |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
+ ASSERT_GE(buf_size, 0); |
+ if (defer) |
+ break; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ } |
+ |
+ while (true) { |
+ char buf[16]; |
+ uint32_t read = sizeof(buf); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ if (r == MOJO_RESULT_SHOULD_WAIT) |
+ break; |
+ ASSERT_EQ(MOJO_RESULT_OK, r); |
+ } |
+ |
+ handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
+ RunUntilNextNotification(); |
+ EXPECT_FALSE(url_loader_client_.has_received_completion()); |
+ EXPECT_TRUE(resource_controller_.is_cancel_with_error_called()); |
+ EXPECT_EQ(net::ERR_FAILED, resource_controller_.error()); |
+} |
+ |
+TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ OnWillReadWithLongContents) { |
+ bool defer = false; |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ ASSERT_TRUE(io_buffer); |
+ // The io_buffer size that the mime sniffer requires implicitly. |
+ ASSERT_GE(buf_size, 2 * 1024); |
+ const size_t contents_size = 3 * buf_size + 2; |
+ ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
+ ASSERT_FALSE(defer); |
+ |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ |
+ size_t written = 0; |
+ std::string actual; |
+ while (actual.size() < contents_size) { |
+ while (written < contents_size && !defer) { |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ const size_t to_be_written = |
+ std::min(static_cast<size_t>(buf_size), contents_size - written); |
+ memset(io_buffer->data(), 'a', to_be_written); |
+ ASSERT_TRUE(handler_->OnReadCompleted(to_be_written, &defer)); |
+ written += to_be_written; |
+ } |
+ |
+ char buf[16]; |
+ uint32_t read = sizeof(buf); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ if (r != MOJO_RESULT_SHOULD_WAIT) { |
+ ASSERT_EQ(MOJO_RESULT_OK, r); |
+ actual += std::string(buf, read); |
+ } |
+ unsigned resume_count = resource_controller_.num_resume_calls(); |
+ base::RunLoop().RunUntilIdle(); |
+ defer = (resume_count == resource_controller_.num_resume_calls()); |
+ } |
+ EXPECT_EQ(std::string(contents_size, 'a'), actual); |
+} |
+ |
+TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ BeginWriteFailsOnReadCompleted) { |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ |
+ handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
+ ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
+} |
+ |
+TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ BeginWriteReturnsShouldWaitOnReadCompleted) { |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ int buf_size = 0; |
+ bool defer = false; |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ |
+ handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
+ ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
+ EXPECT_TRUE(defer); |
+} |
+ |
+TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ BeginWriteFailsOnResumeIfDeferred) { |
+ bool defer = false; |
+ int buf_size = 0; |
+ scoped_refptr<net::IOBuffer> io_buffer; |
+ |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
+ ASSERT_FALSE(defer); |
+ RunUntilNextNotification(); |
+ ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
+ |
+ while (!defer) { |
+ ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
+ ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
+ } |
+ handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
+ |
+ while (!resource_controller_.is_cancel_with_error_called()) { |
+ char buf[256]; |
+ uint32_t read = sizeof(buf); |
+ MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
+ &read, MOJO_READ_DATA_FLAG_NONE); |
+ ASSERT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_SHOULD_WAIT); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ EXPECT_FALSE(url_loader_client_.has_received_completion()); |
+ EXPECT_EQ(net::ERR_FAILED, resource_controller_.error()); |
+ EXPECT_EQ(0u, resource_controller_.num_resume_calls()); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ MojoAsyncResourceHandlerWithAllocationSizeTest, |
+ ::testing::Values(8, 32 * 2014)); |
+} // namespace |
+} // namespace content |