| Index: content/browser/loader/async_resource_handler_unittest.cc
|
| diff --git a/content/browser/loader/async_resource_handler_unittest.cc b/content/browser/loader/async_resource_handler_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..69f2f598d7fe29e8bd602b7b0a43ddba3210f536
|
| --- /dev/null
|
| +++ b/content/browser/loader/async_resource_handler_unittest.cc
|
| @@ -0,0 +1,339 @@
|
| +// 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/async_resource_handler.h"
|
| +
|
| +#include <stddef.h>
|
| +#include <stdint.h>
|
| +#include <memory>
|
| +#include <string>
|
| +#include <tuple>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/feature_list.h"
|
| +#include "base/format_macros.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/weak_ptr.h"
|
| +#include "base/process/process.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "content/browser/loader/resource_dispatcher_host_impl.h"
|
| +#include "content/browser/loader/resource_loader.h"
|
| +#include "content/browser/loader/resource_loader_delegate.h"
|
| +#include "content/browser/loader/resource_message_filter.h"
|
| +#include "content/browser/loader/resource_request_info_impl.h"
|
| +#include "content/common/resource_messages.h"
|
| +#include "content/common/resource_request.h"
|
| +#include "content/public/browser/resource_context.h"
|
| +#include "content/public/browser/resource_request_info.h"
|
| +#include "content/public/common/content_features.h"
|
| +#include "content/public/common/process_type.h"
|
| +#include "content/public/common/resource_type.h"
|
| +#include "content/public/test/mock_resource_context.h"
|
| +#include "content/public/test/test_browser_thread_bundle.h"
|
| +#include "ipc/ipc_message.h"
|
| +#include "ipc/ipc_message_macros.h"
|
| +#include "net/http/http_response_headers.h"
|
| +#include "net/http/http_util.h"
|
| +#include "net/ssl/client_cert_store.h"
|
| +#include "net/url_request/url_request.h"
|
| +#include "net/url_request/url_request_context.h"
|
| +#include "net/url_request/url_request_job_factory_impl.h"
|
| +#include "net/url_request/url_request_test_job.h"
|
| +#include "net/url_request/url_request_test_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "ui/base/page_transition_types.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +std::string GenerateHeader(size_t response_data_size) {
|
| + return base::StringPrintf(
|
| + "HTTP/1.1 200 OK\n"
|
| + "Content-type: text/html\n"
|
| + "Content-Length: %" PRIuS "\n",
|
| + response_data_size);
|
| +}
|
| +
|
| +std::string GenerateData(size_t response_data_size) {
|
| + return std::string(response_data_size, 'a');
|
| +}
|
| +
|
| +// This test job adds a Content-Length header and implements
|
| +// GetTotalReceivedBytes().
|
| +class TestJob : public net::URLRequestTestJob {
|
| + public:
|
| + TestJob(net::URLRequest* request,
|
| + net::NetworkDelegate* network_delegate,
|
| + size_t response_data_size)
|
| + : net::URLRequestTestJob(request,
|
| + network_delegate,
|
| + GenerateHeader(response_data_size),
|
| + GenerateData(response_data_size),
|
| + true) {}
|
| +
|
| + static TestJob* CreateJob(net::URLRequest* request,
|
| + net::NetworkDelegate* network_delegate,
|
| + size_t response_data_size) {
|
| + return new TestJob(request, network_delegate, response_data_size);
|
| + }
|
| +
|
| + // URLRequestJob implementation:
|
| + // TODO(ricea): Move this to URLRequestTestJob.
|
| + int64_t GetTotalReceivedBytes() const override {
|
| + std::string http_headers = net::HttpUtil::ConvertHeadersBackToHTTPResponse(
|
| + response_headers_->raw_headers());
|
| + return http_headers.size() + offset_;
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(TestJob);
|
| +};
|
| +
|
| +class TestJobProtocolHandler
|
| + : public net::URLRequestJobFactory::ProtocolHandler {
|
| + public:
|
| + TestJobProtocolHandler(size_t response_data_size)
|
| + : response_data_size_(response_data_size) {}
|
| +
|
| + net::URLRequestJob* MaybeCreateJob(
|
| + net::URLRequest* request,
|
| + net::NetworkDelegate* network_delegate) const override {
|
| + return TestJob::CreateJob(request, network_delegate, response_data_size_);
|
| + }
|
| +
|
| + private:
|
| + size_t response_data_size_;
|
| +};
|
| +
|
| +// A subclass of ResourceMessageFilter that records IPC messages that are sent.
|
| +class RecordingResourceMessageFilter : public ResourceMessageFilter {
|
| + public:
|
| + RecordingResourceMessageFilter(ResourceContext* resource_context,
|
| + net::URLRequestContext* request_context)
|
| + : ResourceMessageFilter(
|
| + 0,
|
| + PROCESS_TYPE_RENDERER,
|
| + nullptr,
|
| + nullptr,
|
| + nullptr,
|
| + nullptr,
|
| + nullptr,
|
| + base::Bind(&RecordingResourceMessageFilter::GetContexts,
|
| + base::Unretained(this))),
|
| + resource_context_(resource_context),
|
| + request_context_(request_context) {
|
| + set_peer_process_for_testing(base::Process::Current());
|
| + }
|
| +
|
| + const std::vector<std::unique_ptr<IPC::Message>>& messages() const {
|
| + return messages_;
|
| + }
|
| +
|
| + // IPC::Sender implementation:
|
| + bool Send(IPC::Message* message) override {
|
| + // Unpickle the base::SharedMemoryHandle to avoid warnings about
|
| + // "MessageAttachmentSet destroyed with unconsumed descriptors".
|
| + if (message->type() == ResourceMsg_SetDataBuffer::ID) {
|
| + ResourceMsg_SetDataBuffer::Param params;
|
| + ResourceMsg_SetDataBuffer::Read(message, ¶ms);
|
| + }
|
| + messages_.push_back(base::WrapUnique(message));
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| + ~RecordingResourceMessageFilter() override {}
|
| +
|
| + void GetContexts(ResourceType resource_type,
|
| + int origin_pid,
|
| + ResourceContext** resource_context,
|
| + net::URLRequestContext** request_context) {
|
| + *resource_context = resource_context_;
|
| + *request_context = request_context_;
|
| + }
|
| +
|
| + ResourceContext* const resource_context_;
|
| + net::URLRequestContext* const request_context_;
|
| + std::vector<std::unique_ptr<IPC::Message>> messages_;
|
| +};
|
| +
|
| +class AsyncResourceHandlerTest : public ::testing::Test,
|
| + public ResourceLoaderDelegate {
|
| + protected:
|
| + AsyncResourceHandlerTest()
|
| + : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), context_(true) {}
|
| +
|
| + void TearDown() override {
|
| + // Prevent memory leaks.
|
| + rdh_.Shutdown();
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + void CreateRequestWithResponseDataSize(size_t response_data_size) {
|
| + test_job_factory_.SetProtocolHandler(
|
| + "test", base::MakeUnique<TestJobProtocolHandler>(response_data_size));
|
| + context_.set_job_factory(&test_job_factory_);
|
| + context_.Init();
|
| + std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
|
| + GURL("test:test"), net::DEFAULT_PRIORITY, nullptr);
|
| + resource_context_ = base::MakeUnique<MockResourceContext>(&context_);
|
| + filter_ =
|
| + new RecordingResourceMessageFilter(resource_context_.get(), &context_);
|
| + ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
|
| + PROCESS_TYPE_RENDERER, // process_type
|
| + 0, // child_id
|
| + 0, // route_id
|
| + -1, // frame_tree_node_id
|
| + 0, // origin_pid
|
| + 0, // request_id
|
| + 0, // render_frame_id
|
| + false, // is_main_frame
|
| + false, // parent_is_main_frame
|
| + RESOURCE_TYPE_IMAGE, // resource_type
|
| + ui::PAGE_TRANSITION_LINK, // transition_type
|
| + false, // should_replace_current_entry
|
| + false, // is_download
|
| + false, // is_stream
|
| + false, // allow_download
|
| + false, // has_user_gesture
|
| + false, // enable load timing
|
| + false, // enable upload progress
|
| + false, // do_not_prompt_for_login
|
| + blink::WebReferrerPolicyDefault, // referrer_policy
|
| + blink::WebPageVisibilityStateVisible, // visibility_state
|
| + resource_context_.get(), // context
|
| + filter_->GetWeakPtr(), // filter
|
| + false, // report_raw_headers
|
| + true, // is_async
|
| + false, // is_using_lofi
|
| + std::string(), // original_headers
|
| + nullptr, // body
|
| + false); // initiated_in_secure_context
|
| + info->AssociateWithRequest(request.get());
|
| + std::unique_ptr<AsyncResourceHandler> handler =
|
| + base::MakeUnique<AsyncResourceHandler>(request.get(), &rdh_);
|
| + loader_ = base::MakeUnique<ResourceLoader>(
|
| + std::move(request), std::move(handler), nullptr, this);
|
| + }
|
| +
|
| + void StartRequestAndWaitWithResponseDataSize(size_t response_data_size) {
|
| + CreateRequestWithResponseDataSize(response_data_size);
|
| + loader_->StartRequest();
|
| + finish_waiter_.reset(new base::RunLoop);
|
| + finish_waiter_->Run();
|
| + }
|
| +
|
| + scoped_refptr<RecordingResourceMessageFilter> filter_;
|
| +
|
| + private:
|
| + // ResourceLoaderDelegate implementation:
|
| + ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
|
| + ResourceLoader* loader,
|
| + net::AuthChallengeInfo* auth_info) override {
|
| + return nullptr;
|
| + }
|
| +
|
| + bool HandleExternalProtocol(ResourceLoader* loader,
|
| + const GURL& url) override {
|
| + return false;
|
| + }
|
| + void DidStartRequest(ResourceLoader* loader) override {}
|
| + void DidReceiveRedirect(ResourceLoader* loader,
|
| + const GURL& new_url) override {}
|
| + void DidReceiveResponse(ResourceLoader* loader) override {}
|
| + void DidFinishLoading(ResourceLoader* loader) override {
|
| + loader_.reset();
|
| + finish_waiter_->Quit();
|
| + }
|
| + std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
|
| + ResourceLoader* loader) override {
|
| + return nullptr;
|
| + }
|
| +
|
| + TestBrowserThreadBundle thread_bundle_;
|
| + ResourceDispatcherHostImpl rdh_;
|
| + net::TestURLRequestContext context_;
|
| + net::URLRequestJobFactoryImpl test_job_factory_;
|
| + std::unique_ptr<MockResourceContext> resource_context_;
|
| + std::unique_ptr<ResourceLoader> loader_;
|
| + std::unique_ptr<base::RunLoop> finish_waiter_;
|
| +};
|
| +
|
| +TEST_F(AsyncResourceHandlerTest, Construct) {
|
| + CreateRequestWithResponseDataSize(1);
|
| +}
|
| +
|
| +TEST_F(AsyncResourceHandlerTest, OneChunkLengths) {
|
| + // Larger than kInlinedLeadingChunkSize and smaller than
|
| + // kMaxAllocationSize.
|
| + StartRequestAndWaitWithResponseDataSize(4096);
|
| + const auto& messages = filter_->messages();
|
| + ASSERT_EQ(4u, messages.size());
|
| + ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2]->type());
|
| + ResourceMsg_DataReceived::Param params;
|
| + ResourceMsg_DataReceived::Read(messages[2].get(), ¶ms);
|
| +
|
| + int encoded_data_length = std::get<3>(params);
|
| + EXPECT_EQ(4162, encoded_data_length);
|
| + int encoded_body_length = std::get<4>(params);
|
| + EXPECT_EQ(4096, encoded_body_length);
|
| +}
|
| +
|
| +TEST_F(AsyncResourceHandlerTest, InlinedChunkLengths) {
|
| + // TODO(ricea): Remove this Feature-enabling code once the feature is on by
|
| + // default.
|
| + auto feature_list = base::MakeUnique<base::FeatureList>();
|
| + feature_list->InitializeFromCommandLine(
|
| + features::kOptimizeLoadingIPCForSmallResources.name, "");
|
| + base::FeatureList::ClearInstanceForTesting();
|
| + base::FeatureList::SetInstance(std::move(feature_list));
|
| +
|
| + // Smaller than kInlinedLeadingChunkSize.
|
| + StartRequestAndWaitWithResponseDataSize(8);
|
| + const auto& messages = filter_->messages();
|
| + ASSERT_EQ(3u, messages.size());
|
| + ASSERT_EQ(ResourceMsg_InlinedDataChunkReceived::ID, messages[1]->type());
|
| + ResourceMsg_InlinedDataChunkReceived::Param params;
|
| + ResourceMsg_InlinedDataChunkReceived::Read(messages[1].get(), ¶ms);
|
| +
|
| + int encoded_data_length = std::get<2>(params);
|
| + EXPECT_EQ(71, encoded_data_length);
|
| + int encoded_body_length = std::get<3>(params);
|
| + EXPECT_EQ(8, encoded_body_length);
|
| +}
|
| +
|
| +TEST_F(AsyncResourceHandlerTest, TwoChunksLengths) {
|
| + // Larger than kMaxAllocationSize.
|
| + StartRequestAndWaitWithResponseDataSize(64*1024);
|
| + const auto& messages = filter_->messages();
|
| + ASSERT_EQ(5u, messages.size());
|
| + ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2]->type());
|
| + ResourceMsg_DataReceived::Param params;
|
| + ResourceMsg_DataReceived::Read(messages[2].get(), ¶ms);
|
| +
|
| + int encoded_data_length = std::get<3>(params);
|
| + EXPECT_EQ(32835, encoded_data_length);
|
| + int encoded_body_length = std::get<4>(params);
|
| + EXPECT_EQ(32768, encoded_body_length);
|
| +
|
| + ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[3]->type());
|
| + ResourceMsg_DataReceived::Read(messages[3].get(), ¶ms);
|
| +
|
| + encoded_data_length = std::get<3>(params);
|
| + EXPECT_EQ(32768, encoded_data_length);
|
| + encoded_body_length = std::get<4>(params);
|
| + EXPECT_EQ(32768, encoded_body_length);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace content
|
|
|