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