OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/loader/async_resource_handler.h" |
| 6 |
| 7 #include <stddef.h> |
| 8 #include <stdint.h> |
| 9 #include <memory> |
| 10 #include <string> |
| 11 #include <tuple> |
| 12 #include <utility> |
| 13 #include <vector> |
| 14 |
| 15 #include "base/bind.h" |
| 16 #include "base/bind_helpers.h" |
| 17 #include "base/feature_list.h" |
| 18 #include "base/format_macros.h" |
| 19 #include "base/logging.h" |
| 20 #include "base/memory/ptr_util.h" |
| 21 #include "base/memory/ref_counted.h" |
| 22 #include "base/memory/weak_ptr.h" |
| 23 #include "base/process/process.h" |
| 24 #include "base/run_loop.h" |
| 25 #include "base/strings/stringprintf.h" |
| 26 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| 27 #include "content/browser/loader/resource_loader.h" |
| 28 #include "content/browser/loader/resource_loader_delegate.h" |
| 29 #include "content/browser/loader/resource_message_filter.h" |
| 30 #include "content/browser/loader/resource_request_info_impl.h" |
| 31 #include "content/common/resource_messages.h" |
| 32 #include "content/common/resource_request.h" |
| 33 #include "content/public/browser/resource_context.h" |
| 34 #include "content/public/browser/resource_request_info.h" |
| 35 #include "content/public/common/content_features.h" |
| 36 #include "content/public/common/process_type.h" |
| 37 #include "content/public/common/resource_type.h" |
| 38 #include "content/public/test/mock_resource_context.h" |
| 39 #include "content/public/test/test_browser_thread_bundle.h" |
| 40 #include "ipc/ipc_message.h" |
| 41 #include "ipc/ipc_message_macros.h" |
| 42 #include "net/http/http_response_headers.h" |
| 43 #include "net/http/http_util.h" |
| 44 #include "net/ssl/client_cert_store.h" |
| 45 #include "net/url_request/url_request.h" |
| 46 #include "net/url_request/url_request_context.h" |
| 47 #include "net/url_request/url_request_job_factory_impl.h" |
| 48 #include "net/url_request/url_request_test_job.h" |
| 49 #include "net/url_request/url_request_test_util.h" |
| 50 #include "testing/gtest/include/gtest/gtest.h" |
| 51 #include "ui/base/page_transition_types.h" |
| 52 #include "url/gurl.h" |
| 53 |
| 54 namespace content { |
| 55 |
| 56 namespace { |
| 57 |
| 58 std::string GenerateHeader(size_t response_data_size) { |
| 59 return base::StringPrintf( |
| 60 "HTTP/1.1 200 OK\n" |
| 61 "Content-type: text/html\n" |
| 62 "Content-Length: %" PRIuS "\n", |
| 63 response_data_size); |
| 64 } |
| 65 |
| 66 std::string GenerateData(size_t response_data_size) { |
| 67 return std::string(response_data_size, 'a'); |
| 68 } |
| 69 |
| 70 // This test job adds a Content-Length header and implements |
| 71 // GetTotalReceivedBytes(). |
| 72 class TestJob : public net::URLRequestTestJob { |
| 73 public: |
| 74 TestJob(net::URLRequest* request, |
| 75 net::NetworkDelegate* network_delegate, |
| 76 size_t response_data_size) |
| 77 : net::URLRequestTestJob(request, |
| 78 network_delegate, |
| 79 GenerateHeader(response_data_size), |
| 80 GenerateData(response_data_size), |
| 81 true) {} |
| 82 |
| 83 static TestJob* CreateJob(net::URLRequest* request, |
| 84 net::NetworkDelegate* network_delegate, |
| 85 size_t response_data_size) { |
| 86 return new TestJob(request, network_delegate, response_data_size); |
| 87 } |
| 88 |
| 89 // URLRequestJob implementation: |
| 90 // TODO(ricea): Move this to URLRequestTestJob. |
| 91 int64_t GetTotalReceivedBytes() const override { |
| 92 std::string http_headers = net::HttpUtil::ConvertHeadersBackToHTTPResponse( |
| 93 response_headers_->raw_headers()); |
| 94 return http_headers.size() + offset_; |
| 95 } |
| 96 |
| 97 private: |
| 98 DISALLOW_COPY_AND_ASSIGN(TestJob); |
| 99 }; |
| 100 |
| 101 class TestJobProtocolHandler |
| 102 : public net::URLRequestJobFactory::ProtocolHandler { |
| 103 public: |
| 104 TestJobProtocolHandler(size_t response_data_size) |
| 105 : response_data_size_(response_data_size) {} |
| 106 |
| 107 net::URLRequestJob* MaybeCreateJob( |
| 108 net::URLRequest* request, |
| 109 net::NetworkDelegate* network_delegate) const override { |
| 110 return TestJob::CreateJob(request, network_delegate, response_data_size_); |
| 111 } |
| 112 |
| 113 private: |
| 114 size_t response_data_size_; |
| 115 }; |
| 116 |
| 117 // A subclass of ResourceMessageFilter that records IPC messages that are sent. |
| 118 class RecordingResourceMessageFilter : public ResourceMessageFilter { |
| 119 public: |
| 120 RecordingResourceMessageFilter(ResourceContext* resource_context, |
| 121 net::URLRequestContext* request_context) |
| 122 : ResourceMessageFilter( |
| 123 0, |
| 124 PROCESS_TYPE_RENDERER, |
| 125 nullptr, |
| 126 nullptr, |
| 127 nullptr, |
| 128 nullptr, |
| 129 nullptr, |
| 130 base::Bind(&RecordingResourceMessageFilter::GetContexts, |
| 131 base::Unretained(this))), |
| 132 resource_context_(resource_context), |
| 133 request_context_(request_context) { |
| 134 set_peer_process_for_testing(base::Process::Current()); |
| 135 } |
| 136 |
| 137 const std::vector<std::unique_ptr<IPC::Message>>& messages() const { |
| 138 return messages_; |
| 139 } |
| 140 |
| 141 // IPC::Sender implementation: |
| 142 bool Send(IPC::Message* message) override { |
| 143 // Unpickle the base::SharedMemoryHandle to avoid warnings about |
| 144 // "MessageAttachmentSet destroyed with unconsumed descriptors". |
| 145 if (message->type() == ResourceMsg_SetDataBuffer::ID) { |
| 146 ResourceMsg_SetDataBuffer::Param params; |
| 147 ResourceMsg_SetDataBuffer::Read(message, ¶ms); |
| 148 } |
| 149 messages_.push_back(base::WrapUnique(message)); |
| 150 return true; |
| 151 } |
| 152 |
| 153 private: |
| 154 ~RecordingResourceMessageFilter() override {} |
| 155 |
| 156 void GetContexts(ResourceType resource_type, |
| 157 int origin_pid, |
| 158 ResourceContext** resource_context, |
| 159 net::URLRequestContext** request_context) { |
| 160 *resource_context = resource_context_; |
| 161 *request_context = request_context_; |
| 162 } |
| 163 |
| 164 ResourceContext* const resource_context_; |
| 165 net::URLRequestContext* const request_context_; |
| 166 std::vector<std::unique_ptr<IPC::Message>> messages_; |
| 167 }; |
| 168 |
| 169 class AsyncResourceHandlerTest : public ::testing::Test, |
| 170 public ResourceLoaderDelegate { |
| 171 protected: |
| 172 AsyncResourceHandlerTest() |
| 173 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), context_(true) {} |
| 174 |
| 175 void TearDown() override { |
| 176 // Prevent memory leaks. |
| 177 rdh_.Shutdown(); |
| 178 base::RunLoop().RunUntilIdle(); |
| 179 } |
| 180 |
| 181 void CreateRequestWithResponseDataSize(size_t response_data_size) { |
| 182 test_job_factory_.SetProtocolHandler( |
| 183 "test", base::MakeUnique<TestJobProtocolHandler>(response_data_size)); |
| 184 context_.set_job_factory(&test_job_factory_); |
| 185 context_.Init(); |
| 186 std::unique_ptr<net::URLRequest> request = context_.CreateRequest( |
| 187 GURL("test:test"), net::DEFAULT_PRIORITY, nullptr); |
| 188 resource_context_ = base::MakeUnique<MockResourceContext>(&context_); |
| 189 filter_ = |
| 190 new RecordingResourceMessageFilter(resource_context_.get(), &context_); |
| 191 ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl( |
| 192 PROCESS_TYPE_RENDERER, // process_type |
| 193 0, // child_id |
| 194 0, // route_id |
| 195 -1, // frame_tree_node_id |
| 196 0, // origin_pid |
| 197 0, // request_id |
| 198 0, // render_frame_id |
| 199 false, // is_main_frame |
| 200 false, // parent_is_main_frame |
| 201 RESOURCE_TYPE_IMAGE, // resource_type |
| 202 ui::PAGE_TRANSITION_LINK, // transition_type |
| 203 false, // should_replace_current_entry |
| 204 false, // is_download |
| 205 false, // is_stream |
| 206 false, // allow_download |
| 207 false, // has_user_gesture |
| 208 false, // enable load timing |
| 209 false, // enable upload progress |
| 210 false, // do_not_prompt_for_login |
| 211 blink::WebReferrerPolicyDefault, // referrer_policy |
| 212 blink::WebPageVisibilityStateVisible, // visibility_state |
| 213 resource_context_.get(), // context |
| 214 filter_->GetWeakPtr(), // filter |
| 215 false, // report_raw_headers |
| 216 true, // is_async |
| 217 false, // is_using_lofi |
| 218 std::string(), // original_headers |
| 219 nullptr, // body |
| 220 false); // initiated_in_secure_context |
| 221 info->AssociateWithRequest(request.get()); |
| 222 std::unique_ptr<AsyncResourceHandler> handler = |
| 223 base::MakeUnique<AsyncResourceHandler>(request.get(), &rdh_); |
| 224 loader_ = base::MakeUnique<ResourceLoader>( |
| 225 std::move(request), std::move(handler), nullptr, this); |
| 226 } |
| 227 |
| 228 void StartRequestAndWaitWithResponseDataSize(size_t response_data_size) { |
| 229 CreateRequestWithResponseDataSize(response_data_size); |
| 230 loader_->StartRequest(); |
| 231 finish_waiter_.reset(new base::RunLoop); |
| 232 finish_waiter_->Run(); |
| 233 } |
| 234 |
| 235 scoped_refptr<RecordingResourceMessageFilter> filter_; |
| 236 |
| 237 private: |
| 238 // ResourceLoaderDelegate implementation: |
| 239 ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( |
| 240 ResourceLoader* loader, |
| 241 net::AuthChallengeInfo* auth_info) override { |
| 242 return nullptr; |
| 243 } |
| 244 |
| 245 bool HandleExternalProtocol(ResourceLoader* loader, |
| 246 const GURL& url) override { |
| 247 return false; |
| 248 } |
| 249 void DidStartRequest(ResourceLoader* loader) override {} |
| 250 void DidReceiveRedirect(ResourceLoader* loader, |
| 251 const GURL& new_url) override {} |
| 252 void DidReceiveResponse(ResourceLoader* loader) override {} |
| 253 void DidFinishLoading(ResourceLoader* loader) override { |
| 254 loader_.reset(); |
| 255 finish_waiter_->Quit(); |
| 256 } |
| 257 std::unique_ptr<net::ClientCertStore> CreateClientCertStore( |
| 258 ResourceLoader* loader) override { |
| 259 return nullptr; |
| 260 } |
| 261 |
| 262 TestBrowserThreadBundle thread_bundle_; |
| 263 ResourceDispatcherHostImpl rdh_; |
| 264 net::TestURLRequestContext context_; |
| 265 net::URLRequestJobFactoryImpl test_job_factory_; |
| 266 std::unique_ptr<MockResourceContext> resource_context_; |
| 267 std::unique_ptr<ResourceLoader> loader_; |
| 268 std::unique_ptr<base::RunLoop> finish_waiter_; |
| 269 }; |
| 270 |
| 271 TEST_F(AsyncResourceHandlerTest, Construct) { |
| 272 CreateRequestWithResponseDataSize(1); |
| 273 } |
| 274 |
| 275 TEST_F(AsyncResourceHandlerTest, OneChunkLengths) { |
| 276 // Larger than kInlinedLeadingChunkSize and smaller than |
| 277 // kMaxAllocationSize. |
| 278 StartRequestAndWaitWithResponseDataSize(4096); |
| 279 const auto& messages = filter_->messages(); |
| 280 ASSERT_EQ(4u, messages.size()); |
| 281 ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2]->type()); |
| 282 ResourceMsg_DataReceived::Param params; |
| 283 ResourceMsg_DataReceived::Read(messages[2].get(), ¶ms); |
| 284 |
| 285 int encoded_data_length = std::get<3>(params); |
| 286 EXPECT_EQ(4162, encoded_data_length); |
| 287 int encoded_body_length = std::get<4>(params); |
| 288 EXPECT_EQ(4096, encoded_body_length); |
| 289 } |
| 290 |
| 291 TEST_F(AsyncResourceHandlerTest, InlinedChunkLengths) { |
| 292 // TODO(ricea): Remove this Feature-enabling code once the feature is on by |
| 293 // default. |
| 294 auto feature_list = base::MakeUnique<base::FeatureList>(); |
| 295 feature_list->InitializeFromCommandLine( |
| 296 features::kOptimizeLoadingIPCForSmallResources.name, ""); |
| 297 base::FeatureList::ClearInstanceForTesting(); |
| 298 base::FeatureList::SetInstance(std::move(feature_list)); |
| 299 |
| 300 // Smaller than kInlinedLeadingChunkSize. |
| 301 StartRequestAndWaitWithResponseDataSize(8); |
| 302 const auto& messages = filter_->messages(); |
| 303 ASSERT_EQ(3u, messages.size()); |
| 304 ASSERT_EQ(ResourceMsg_InlinedDataChunkReceived::ID, messages[1]->type()); |
| 305 ResourceMsg_InlinedDataChunkReceived::Param params; |
| 306 ResourceMsg_InlinedDataChunkReceived::Read(messages[1].get(), ¶ms); |
| 307 |
| 308 int encoded_data_length = std::get<2>(params); |
| 309 EXPECT_EQ(71, encoded_data_length); |
| 310 int encoded_body_length = std::get<3>(params); |
| 311 EXPECT_EQ(8, encoded_body_length); |
| 312 } |
| 313 |
| 314 TEST_F(AsyncResourceHandlerTest, TwoChunksLengths) { |
| 315 // Larger than kMaxAllocationSize. |
| 316 StartRequestAndWaitWithResponseDataSize(64*1024); |
| 317 const auto& messages = filter_->messages(); |
| 318 ASSERT_EQ(5u, messages.size()); |
| 319 ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2]->type()); |
| 320 ResourceMsg_DataReceived::Param params; |
| 321 ResourceMsg_DataReceived::Read(messages[2].get(), ¶ms); |
| 322 |
| 323 int encoded_data_length = std::get<3>(params); |
| 324 EXPECT_EQ(32835, encoded_data_length); |
| 325 int encoded_body_length = std::get<4>(params); |
| 326 EXPECT_EQ(32768, encoded_body_length); |
| 327 |
| 328 ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[3]->type()); |
| 329 ResourceMsg_DataReceived::Read(messages[3].get(), ¶ms); |
| 330 |
| 331 encoded_data_length = std::get<3>(params); |
| 332 EXPECT_EQ(32768, encoded_data_length); |
| 333 encoded_body_length = std::get<4>(params); |
| 334 EXPECT_EQ(32768, encoded_body_length); |
| 335 } |
| 336 |
| 337 } // namespace |
| 338 |
| 339 } // namespace content |
OLD | NEW |