|  | // 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/resource_dispatcher_host_impl.h" | 
|  | #include "content/browser/loader/resource_request_info_impl.h" | 
|  | #include "content/browser/loader/test_url_loader_client.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 { | 
|  |  | 
|  | constexpr int kSizeMimeSnifferRequiresForFirstOnWillRead = 2048; | 
|  |  | 
|  | class TestResourceDispatcherHostDelegate final | 
|  | : public ResourceDispatcherHostDelegate { | 
|  | public: | 
|  | TestResourceDispatcherHostDelegate() = default; | 
|  | ~TestResourceDispatcherHostDelegate() override { | 
|  | EXPECT_EQ(num_on_response_started_calls_expectation_, | 
|  | num_on_response_started_calls_); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | 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 { | 
|  | ++num_on_response_started_calls_; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int num_on_response_started_calls() const { | 
|  | return num_on_response_started_calls_; | 
|  | } | 
|  | void set_num_on_response_started_calls_expectation(int expectation) { | 
|  | num_on_response_started_calls_expectation_ = expectation; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_on_response_started_calls_ = 0; | 
|  | int num_on_response_started_calls_expectation_ = 0; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate); | 
|  | }; | 
|  |  | 
|  | class TestResourceController : public ResourceController { | 
|  | public: | 
|  | TestResourceController() {} | 
|  | ~TestResourceController() 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_; } | 
|  |  | 
|  | void RunUntilCancelWithErrorCalled() { | 
|  | base::RunLoop run_loop; | 
|  | quit_closure_ = run_loop.QuitClosure(); | 
|  | run_loop.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_; } | 
|  | int 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(TestResourceController); | 
|  | }; | 
|  |  | 
|  | 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 ResetBeginWriteExpectation() { is_begin_write_expectation_set_ = false; } | 
|  |  | 
|  | 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); | 
|  | rdh_.SetDelegate(&rdh_delegate_); | 
|  |  | 
|  | 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(),                          // request | 
|  | RESOURCE_TYPE_XHR,                       // resource_type | 
|  | browser_context_->GetResourceContext(),  // context | 
|  | 2,                                       // render_process_id | 
|  | 0,                                       // render_view_id | 
|  | 0,                                       // render_frame_id | 
|  | true,                                    // is_main_frame | 
|  | false,                                   // parent_is_main_frame | 
|  | false,                                   // allow_download | 
|  | true,                                    // is_async | 
|  | false                                    // is_using_lofi | 
|  | ); | 
|  | handler_.reset(new MojoAsyncResourceHandlerWithCustomDataPipeOperations( | 
|  | request_.get(), &rdh_, nullptr, | 
|  | url_loader_client_.CreateInterfacePtrAndBind())); | 
|  | handler_->SetController(&resource_controller_); | 
|  | } | 
|  |  | 
|  | virtual ~MojoAsyncResourceHandlerTestBase() { | 
|  | net::URLRequestFilter::GetInstance()->ClearHandlers(); | 
|  | MojoAsyncResourceHandler::SetAllocationSizeForTesting( | 
|  | MojoAsyncResourceHandler::kDefaultAllocationSize); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Returns false if something bad happens. | 
|  | bool CallOnWillStart() { | 
|  | bool defer = false; | 
|  | if (!handler_->OnWillStart(request_->url(), &defer)) { | 
|  | ADD_FAILURE() << "OnWillStart returns false."; | 
|  | return false; | 
|  | } | 
|  | if (defer) { | 
|  | ADD_FAILURE() << "OnWillStart sets |defer| true."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Returns false if something bad happens. | 
|  | bool CallOnWillStartAndOnResponseStarted() { | 
|  | rdh_delegate_.set_num_on_response_started_calls_expectation(1); | 
|  | if (!CallOnWillStart()) | 
|  | return false; | 
|  |  | 
|  | scoped_refptr<ResourceResponse> response = new ResourceResponse(); | 
|  | bool defer = false; | 
|  | if (!handler_->OnResponseStarted(response.get(), &defer)) { | 
|  | ADD_FAILURE() << "OnResponseStarted returns false."; | 
|  | return false; | 
|  | } | 
|  | if (defer) { | 
|  | ADD_FAILURE() << "OnResponseStarted sets |defer| true."; | 
|  | return false; | 
|  | } | 
|  | if (url_loader_client_.has_received_response()) { | 
|  | ADD_FAILURE() << "URLLoaderClient unexpectedly gets a response."; | 
|  | return false; | 
|  | } | 
|  | url_loader_client_.RunUntilResponseReceived(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | TestBrowserThreadBundle thread_bundle_; | 
|  | TestResourceDispatcherHostDelegate rdh_delegate_; | 
|  | ResourceDispatcherHostImpl rdh_; | 
|  | TestURLLoaderClient url_loader_client_; | 
|  | TestResourceController 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 parameterized 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, OnWillStart) { | 
|  | bool defer = false; | 
|  | EXPECT_TRUE(handler_->OnWillStart(request_->url(), &defer)); | 
|  | EXPECT_FALSE(defer); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, OnResponseStarted) { | 
|  | rdh_delegate_.set_num_on_response_started_calls_expectation(1); | 
|  | ASSERT_TRUE(CallOnWillStart()); | 
|  |  | 
|  | scoped_refptr<ResourceResponse> response = new ResourceResponse(); | 
|  | 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; | 
|  |  | 
|  | EXPECT_EQ(0, rdh_delegate_.num_on_response_started_calls()); | 
|  | base::TimeTicks now1 = base::TimeTicks::Now(); | 
|  | ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); | 
|  | 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_EQ(1, rdh_delegate_.num_on_response_started_calls()); | 
|  |  | 
|  | url_loader_client_.RunUntilResponseReceived(); | 
|  | 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, OnWillReadAndInFlightRequests) { | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | EXPECT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_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); | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | EXPECT_FALSE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | EXPECT_FALSE(io_buffer); | 
|  | EXPECT_EQ(0, io_buffer_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> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | // The buffer size that the mime sniffer requires implicitly. | 
|  | ASSERT_GE(io_buffer_size, kSizeMimeSnifferRequiresForFirstOnWillRead); | 
|  |  | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | io_buffer->data()[0] = 'A'; | 
|  | io_buffer->data()[1] = 'B'; | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(2, &defer)); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | std::string contents; | 
|  | while (contents.size() < 2) { | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | continue; | 
|  | } | 
|  | contents.append(buffer, read_size); | 
|  | } | 
|  | EXPECT_EQ("AB", contents); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | OnWillReadAndOnReadCompletedWithInsufficientInitialCapacity) { | 
|  | MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); | 
|  |  | 
|  | bool defer = false; | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | // The buffer size that the mime sniffer requires implicitly. | 
|  | ASSERT_GE(io_buffer_size, kSizeMimeSnifferRequiresForFirstOnWillRead); | 
|  |  | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | const std::string data("abcdefgh"); | 
|  | strcpy(io_buffer->data(), data.c_str()); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(data.size(), &defer)); | 
|  | EXPECT_TRUE(defer); | 
|  |  | 
|  | std::string contents; | 
|  | while (contents.size() < data.size()) { | 
|  | // This is needed for Resume to be called. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) | 
|  | continue; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, result); | 
|  | contents.append(buffer, read_size); | 
|  | } | 
|  | EXPECT_EQ(data, contents); | 
|  | EXPECT_EQ(0, resource_controller_.num_resume_calls()); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | IOBufferFromOnWillReadShouldRemainValidEvenIfHandlerIsGone) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | // The io_buffer size that the mime sniffer requires implicitly. | 
|  | ASSERT_GE(io_buffer_size, kSizeMimeSnifferRequiresForFirstOnWillRead); | 
|  |  | 
|  | handler_ = nullptr; | 
|  | url_loader_client_.Unbind(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  |  | 
|  | // Hopefully ASAN checks this operation's validity. | 
|  | io_buffer->data()[0] = 'A'; | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompleted) { | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | ResourceRequestInfoImpl::ForRequest(request_.get()) | 
|  | ->set_was_ignored_by_handler(false); | 
|  | net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); | 
|  |  | 
|  | base::TimeTicks now1 = base::TimeTicks::Now(); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | base::TimeTicks now2 = base::TimeTicks::Now(); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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_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) { | 
|  | rdh_.SetDelegate(nullptr); | 
|  | bool defer = false; | 
|  | // Don't use CallOnWillStartAndOnResponseStarted as this test case manually | 
|  | // sets the null delegate. | 
|  | ASSERT_TRUE(CallOnWillStart()); | 
|  | scoped_refptr<ResourceResponse> response = new ResourceResponse(); | 
|  | ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  | ASSERT_FALSE(url_loader_client_.has_received_response()); | 
|  | url_loader_client_.RunUntilResponseReceived(); | 
|  |  | 
|  | ResourceRequestInfoImpl::ForRequest(request_.get()) | 
|  | ->set_was_ignored_by_handler(true); | 
|  | net::URLRequestStatus status(net::URLRequestStatus::CANCELED, | 
|  | net::ERR_ABORTED); | 
|  |  | 
|  | base::TimeTicks now1 = base::TimeTicks::Now(); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | base::TimeTicks now2 = base::TimeTicks::Now(); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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_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; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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) { | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | 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, &defer); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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; | 
|  |  | 
|  | while (true) { | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | ASSERT_EQ(result, MOJO_RESULT_SHOULD_WAIT); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, ResponseErrorDuringBodyTransmission) { | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  | ASSERT_GT(io_buffer_size, 0); | 
|  | memset(io_buffer->data(), 'a', io_buffer_size); | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | // We don't care |defer|'s value here. | 
|  |  | 
|  | defer = false; | 
|  | net::URLRequestStatus status(net::URLRequestStatus::FAILED, net::ERR_FAILED); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | continue; | 
|  | } | 
|  | EXPECT_EQ(MOJO_RESULT_OK, result); | 
|  | actual.append(buf, read_size); | 
|  | } | 
|  | EXPECT_EQ(std::string(io_buffer_size, 'a'), actual); | 
|  | } | 
|  |  | 
|  | // In this case, an error is notified after OnWillRead, before OnReadCompleted. | 
|  | TEST_F(MojoAsyncResourceHandlerTest, ResponseErrorDuringBodyTransmission2) { | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  | bool defer = false; | 
|  | net::URLRequestStatus status(net::URLRequestStatus::FAILED, net::ERR_FAILED); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | EXPECT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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; | 
|  |  | 
|  | while (true) { | 
|  | char buf[16]; | 
|  | uint32_t read_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, result); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, BeginWriteFailsOnWillRead) { | 
|  | handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &io_buffer_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 io_buffer_size = 0; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | EXPECT_TRUE(io_buffer); | 
|  | EXPECT_GT(io_buffer_size, 0); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | BeginWriteReturnsShouldWaitOnWillReadAndThenReturnsOK) { | 
|  | handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | size_t written = 0; | 
|  | while (true) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | EXPECT_TRUE(io_buffer); | 
|  | EXPECT_GT(io_buffer_size, 0); | 
|  | memset(io_buffer->data(), 'X', io_buffer_size); | 
|  | written += io_buffer_size; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | if (defer) | 
|  | break; | 
|  | } | 
|  |  | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  | handler_->ResetBeginWriteExpectation(); | 
|  | handler_->ResumeForTesting(); | 
|  |  | 
|  | std::string actual; | 
|  | while (actual.size() < written) { | 
|  | char buf[16]; | 
|  | uint32_t read_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | ASSERT_TRUE(result == MOJO_RESULT_OK || result == MOJO_RESULT_SHOULD_WAIT); | 
|  | if (result == MOJO_RESULT_OK) | 
|  | actual.append(buf, read_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(std::string(written, 'X'), actual); | 
|  | EXPECT_EQ(1, resource_controller_.num_resume_calls()); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | EndWriteFailsOnWillReadWithInsufficientInitialCapacity) { | 
|  | MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | handler_->set_end_write_expectation(MOJO_RESULT_UNKNOWN); | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, EndWriteFailsOnReadCompleted) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  |  | 
|  | handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); | 
|  | ASSERT_FALSE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | EndWriteFailsOnReadCompletedWithInsufficientInitialCapacity) { | 
|  | MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  |  | 
|  | handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); | 
|  | ASSERT_FALSE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | } | 
|  |  | 
|  | TEST_F(MojoAsyncResourceHandlerTest, | 
|  | EndWriteFailsOnResumeWithInsufficientInitialCapacity) { | 
|  | MojoAsyncResourceHandler::SetAllocationSizeForTesting(8); | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | while (true) { | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | ASSERT_GE(io_buffer_size, 0); | 
|  | if (defer) | 
|  | break; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | char buf[16]; | 
|  | uint32_t read_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) | 
|  | break; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, result); | 
|  | } | 
|  |  | 
|  | handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); | 
|  | resource_controller_.RunUntilCancelWithErrorCalled(); | 
|  | 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 io_buffer_size = 0; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | // The io_buffer size that the mime sniffer requires implicitly. | 
|  | ASSERT_GE(io_buffer_size, kSizeMimeSnifferRequiresForFirstOnWillRead); | 
|  | std::string expected; | 
|  | for (int i = 0; i < 3 * io_buffer_size + 2; ++i) | 
|  | expected += ('A' + i % 26); | 
|  |  | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | size_t written = 0; | 
|  | std::string actual; | 
|  | while (actual.size() < expected.size()) { | 
|  | while (written < expected.size() && !defer) { | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | const size_t to_be_written = std::min(static_cast<size_t>(io_buffer_size), | 
|  | expected.size() - written); | 
|  | memcpy(io_buffer->data(), &expected[written], to_be_written); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(to_be_written, &defer)); | 
|  | written += to_be_written; | 
|  | } | 
|  |  | 
|  | char buf[16]; | 
|  | uint32_t read_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result != MOJO_RESULT_SHOULD_WAIT) { | 
|  | ASSERT_EQ(MOJO_RESULT_OK, result); | 
|  | actual.append(buf, read_size); | 
|  | } | 
|  | int resume_count = resource_controller_.num_resume_calls(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | // Continue writing if controller->Resume() is called. | 
|  | defer = (resume_count == resource_controller_.num_resume_calls()); | 
|  | } | 
|  | EXPECT_EQ(expected, actual); | 
|  | } | 
|  |  | 
|  | TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | BeginWriteFailsOnReadCompleted) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  |  | 
|  | handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); | 
|  | ASSERT_FALSE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | } | 
|  |  | 
|  | TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | BeginWriteReturnsShouldWaitOnReadCompleted) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  |  | 
|  | handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | EXPECT_TRUE(defer); | 
|  | } | 
|  |  | 
|  | TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | BeginWriteFailsOnResume) { | 
|  | bool defer = false; | 
|  | int io_buffer_size = 0; | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  |  | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | while (!defer) { | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | } | 
|  | handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); | 
|  |  | 
|  | while (!resource_controller_.is_cancel_with_error_called()) { | 
|  | char buf[256]; | 
|  | uint32_t read_size = sizeof(buf); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buf, &read_size, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | ASSERT_TRUE(result == MOJO_RESULT_OK || result == 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(0, resource_controller_.num_resume_calls()); | 
|  | } | 
|  |  | 
|  | TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, CancelWhileWaiting) { | 
|  | bool defer = false; | 
|  | ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); | 
|  |  | 
|  | while (!defer) { | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | int io_buffer_size = 0; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(io_buffer_size, &defer)); | 
|  | } | 
|  |  | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | defer = false; | 
|  | net::URLRequestStatus status(net::URLRequestStatus::CANCELED, | 
|  | net::ERR_ABORTED); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.has_received_completion()); | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | EXPECT_EQ(net::ERR_ABORTED, | 
|  | url_loader_client_.completion_status().error_code); | 
|  |  | 
|  | while (true) { | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | DCHECK(result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_OK); | 
|  | } | 
|  |  | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0, resource_controller_.num_resume_calls()); | 
|  | } | 
|  |  | 
|  | // Typically ResourceHandler methods are called in this order. | 
|  | TEST_P( | 
|  | MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | OnWillStartThenOnResponseStartedThenOnWillReadThenOnReadCompletedThenOnResponseCompleted) { | 
|  | rdh_delegate_.set_num_on_response_started_calls_expectation(1); | 
|  | bool defer = false; | 
|  |  | 
|  | ASSERT_TRUE(handler_->OnWillStart(request_->url(), &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  | scoped_refptr<ResourceResponse> response = new ResourceResponse(); | 
|  | ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.has_received_response()); | 
|  | url_loader_client_.RunUntilResponseReceived(); | 
|  |  | 
|  | int io_buffer_size = 0; | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | ASSERT_GT(io_buffer_size, 0); | 
|  | io_buffer->data()[0] = 'A'; | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.response_body().is_valid()); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(1, &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  | net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.has_received_completion()); | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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; | 
|  |  | 
|  | std::string body; | 
|  | while (true) { | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } else { | 
|  | ASSERT_EQ(result, MOJO_RESULT_OK); | 
|  | body.append(buffer, read_size); | 
|  | } | 
|  | } | 
|  | EXPECT_EQ("A", body); | 
|  | } | 
|  |  | 
|  | // MimeResourceHandler calls delegated ResourceHandler's methods in this order. | 
|  | TEST_P( | 
|  | MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | OnWillStartThenOnWillReadThenOnResponseStartedThenOnReadCompletedThenOnResponseCompleted) { | 
|  | rdh_delegate_.set_num_on_response_started_calls_expectation(1); | 
|  | bool defer = false; | 
|  |  | 
|  | ASSERT_TRUE(handler_->OnWillStart(request_->url(), &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | int io_buffer_size = 0; | 
|  | scoped_refptr<net::IOBuffer> io_buffer; | 
|  | ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &io_buffer_size, -1)); | 
|  | ASSERT_TRUE(io_buffer); | 
|  | ASSERT_GT(io_buffer_size, 0); | 
|  | io_buffer->data()[0] = 'B'; | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.response_body().is_valid()); | 
|  | url_loader_client_.RunUntilResponseBodyArrived(); | 
|  | ASSERT_TRUE(url_loader_client_.response_body().is_valid()); | 
|  |  | 
|  | scoped_refptr<ResourceResponse> response = new ResourceResponse(); | 
|  | ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.has_received_response()); | 
|  | url_loader_client_.RunUntilResponseReceived(); | 
|  |  | 
|  | ASSERT_TRUE(handler_->OnReadCompleted(1, &defer)); | 
|  | ASSERT_FALSE(defer); | 
|  | net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); | 
|  | handler_->OnResponseCompleted(status, &defer); | 
|  | ASSERT_FALSE(defer); | 
|  |  | 
|  | ASSERT_FALSE(url_loader_client_.has_received_completion()); | 
|  | url_loader_client_.RunUntilComplete(); | 
|  | 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; | 
|  |  | 
|  | std::string body; | 
|  | while (true) { | 
|  | char buffer[16]; | 
|  | uint32_t read_size = sizeof(buffer); | 
|  | MojoResult result = | 
|  | mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, | 
|  | &read_size, MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_FAILED_PRECONDITION) | 
|  | break; | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } else { | 
|  | ASSERT_EQ(result, MOJO_RESULT_OK); | 
|  | body.append(buffer, read_size); | 
|  | } | 
|  | } | 
|  | EXPECT_EQ("B", body); | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P(MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | MojoAsyncResourceHandlerWithAllocationSizeTest, | 
|  | ::testing::Values(8, 32 * 2014)); | 
|  | }  // namespace | 
|  | }  // namespace content |