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/url_loader_factory_holder.h" | |
6 | |
7 #include <memory> | |
8 #include <string> | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/callback.h" | |
13 #include "base/files/file_path.h" | |
14 #include "base/location.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/memory/weak_ptr.h" | |
17 #include "base/message_loop/message_loop.h" | |
18 #include "base/path_service.h" | |
19 #include "base/run_loop.h" | |
20 #include "content/browser/loader/mojo_async_resource_handler.h" | |
21 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
22 #include "content/browser/loader/resource_message_filter.h" | |
23 #include "content/common/resource_request.h" | |
24 #include "content/common/resource_request_completion_status.h" | |
25 #include "content/common/url_loader.mojom.h" | |
26 #include "content/common/url_loader_factory.mojom.h" | |
27 #include "content/public/browser/resource_context.h" | |
28 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
29 #include "content/public/common/content_paths.h" | |
30 #include "content/public/test/test_browser_context.h" | |
31 #include "content/public/test/test_browser_thread_bundle.h" | |
32 #include "mojo/public/c/system/data_pipe.h" | |
33 #include "mojo/public/c/system/types.h" | |
34 #include "mojo/public/cpp/bindings/binding.h" | |
35 #include "mojo/public/cpp/system/data_pipe.h" | |
36 #include "net/base/io_buffer.h" | |
37 #include "net/base/net_errors.h" | |
38 #include "net/http/http_response_headers.h" | |
39 #include "net/http/http_response_info.h" | |
40 #include "net/http/http_status_code.h" | |
41 #include "net/http/http_util.h" | |
42 #include "net/test/url_request/url_request_failed_job.h" | |
43 #include "net/test/url_request/url_request_mock_http_job.h" | |
44 #include "net/url_request/url_request_filter.h" | |
45 #include "testing/gtest/include/gtest/gtest.h" | |
46 #include "url/gurl.h" | |
47 | |
48 namespace content { | |
49 | |
50 namespace { | |
51 | |
52 class FakeURLLoaderClient final : public mojom::URLLoaderClient { | |
53 public: | |
54 FakeURLLoaderClient() : binding_(this) {} | |
55 | |
56 void OnReceiveResponse(const ResourceResponseHead& response_head) override { | |
57 has_received_response_ = true; | |
58 response_head_ = response_head; | |
59 if (!quit_closure_.Equals(base::Closure())) | |
mmenke
2016/06/13 20:56:58
There's either an is_empty() or is_null() method t
kinuko
2016/06/14 08:45:29
is_null() is most common I believe
yhirano
2016/07/12 10:49:11
operator bool() is simpler, I think.
| |
60 quit_closure_.Run(); | |
61 } | |
62 void OnStartLoadingResponseBody( | |
63 mojo::ScopedDataPipeConsumerHandle body) override { | |
64 response_body_ = std::move(body); | |
65 if (!quit_closure_.Equals(base::Closure())) | |
mmenke
2016/06/13 20:56:58
is_null()
yhirano
2016/07/12 10:49:11
Done.
| |
66 quit_closure_.Run(); | |
67 } | |
68 void OnComplete(const ResourceRequestCompletionStatus& status) override { | |
69 has_received_completion_ = true; | |
70 completion_status_ = status; | |
71 if (!quit_closure_.Equals(base::Closure())) | |
mmenke
2016/06/13 20:56:58
is_null()
yhirano
2016/07/12 10:49:11
Done.
| |
72 quit_closure_.Run(); | |
73 } | |
74 | |
75 bool has_received_response() const { return has_received_response_; } | |
76 bool has_received_completion() const { return has_received_completion_; } | |
77 const ResourceResponseHead& response_head() const { return response_head_; } | |
78 mojo::DataPipeConsumerHandle response_body() { return response_body_.get(); } | |
79 const ResourceRequestCompletionStatus& completion_status() const { | |
80 return completion_status_; | |
81 } | |
82 | |
83 mojom::URLLoaderClientPtr CreateInterfacePtrAndBind() { | |
84 return binding_.CreateInterfacePtrAndBind(); | |
85 } | |
86 | |
87 void set_quit_closure(const base::Closure& quit_closure) { | |
88 quit_closure_ = quit_closure; | |
89 } | |
90 | |
91 private: | |
92 mojo::Binding<mojom::URLLoaderClient> binding_; | |
93 ResourceResponseHead response_head_; | |
94 mojo::ScopedDataPipeConsumerHandle response_body_; | |
95 ResourceRequestCompletionStatus completion_status_; | |
96 bool has_received_response_ = false; | |
97 bool has_received_completion_ = false; | |
98 base::Closure quit_closure_; | |
99 | |
100 DISALLOW_COPY_AND_ASSIGN(FakeURLLoaderClient); | |
101 }; | |
102 | |
103 class RejectingResourceDispatcherHostDelegate final | |
104 : public ResourceDispatcherHostDelegate { | |
105 public: | |
106 RejectingResourceDispatcherHostDelegate() {} | |
107 bool ShouldBeginRequest(const std::string& method, | |
108 const GURL& url, | |
109 ResourceType resource_type, | |
110 ResourceContext* resource_context) override { | |
111 return false; | |
112 } | |
113 | |
114 DISALLOW_COPY_AND_ASSIGN(RejectingResourceDispatcherHostDelegate); | |
115 }; | |
116 | |
117 class TestURLRequestJobForBrokenBody final : public net::URLRequestJob { | |
118 public: | |
119 TestURLRequestJobForBrokenBody(net::URLRequest* request, | |
120 net::NetworkDelegate* network_delegate) | |
121 : URLRequestJob(request, network_delegate), weak_factory_(this) {} | |
122 | |
123 void Start() override { | |
124 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
125 FROM_HERE, base::Bind(&TestURLRequestJobForBrokenBody::StartAsync, | |
126 weak_factory_.GetWeakPtr())); | |
127 } | |
128 | |
129 int ReadRawData(net::IOBuffer* buf, int buf_size) override { | |
130 DCHECK_GT(buf_size, 0); | |
131 if (++counter_ > 1) | |
132 return net::ERR_FAILED; | |
133 buf->data()[0] = 'a'; | |
134 return 1; | |
135 } | |
136 | |
137 int GetResponseCode() const override { return 200; } | |
138 | |
139 void GetResponseInfo(net::HttpResponseInfo* info) override { | |
140 std::string raw_headers; | |
141 raw_headers.append( | |
142 "HTTP/1.1 200 OK\n" | |
143 "Content-type: text/plain\n"); | |
144 info->headers = | |
145 new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( | |
146 raw_headers.c_str(), static_cast<int>(raw_headers.length()))); | |
147 } | |
148 | |
149 private: | |
150 void StartAsync() { NotifyHeadersComplete(); } | |
151 | |
152 int counter_ = 0; | |
153 base::WeakPtrFactory<TestURLRequestJobForBrokenBody> weak_factory_; | |
154 | |
155 DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobForBrokenBody); | |
156 }; | |
157 | |
158 class TestInterceptorForBrokenBody final : public net::URLRequestInterceptor { | |
159 public: | |
160 explicit TestInterceptorForBrokenBody(const GURL& url) : url_(url) {} | |
161 | |
162 net::URLRequestJob* MaybeInterceptRequest( | |
163 net::URLRequest* request, | |
164 net::NetworkDelegate* network_delegate) const override { | |
165 if (request->url() != url_) | |
166 return nullptr; | |
167 | |
168 return new TestURLRequestJobForBrokenBody(request, network_delegate); | |
169 } | |
170 | |
171 private: | |
172 const GURL url_; | |
173 | |
174 DISALLOW_COPY_AND_ASSIGN(TestInterceptorForBrokenBody); | |
175 }; | |
176 | |
177 class URLLoaderFactoryImplTest : public ::testing::TestWithParam<size_t> { | |
mmenke
2016/06/13 20:56:59
You should document what the size_t is.
yhirano
2016/07/12 10:49:11
Done.
| |
178 public: | |
179 URLLoaderFactoryImplTest() | |
180 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), | |
181 browser_context_(new TestBrowserContext()), | |
182 resource_message_filter_(new ResourceMessageFilter( | |
183 0, | |
184 0, | |
185 nullptr, | |
186 nullptr, | |
187 nullptr, | |
188 nullptr, | |
189 nullptr, | |
190 base::Bind(&URLLoaderFactoryImplTest::GetContexts, | |
191 base::Unretained(this)))) { | |
192 MojoAsyncResourceHandler::SetAllocationSizeForTesting(GetParam()); | |
193 | |
194 factory_impl_holder_.reset(new URLLoaderFactoryHolder( | |
195 resource_message_filter_, mojo::GetProxy(&factory_))); | |
196 | |
197 // Calling this function creates a request context. | |
198 browser_context_->GetResourceContext()->GetRequestContext(); | |
199 base::RunLoop().RunUntilIdle(); | |
200 } | |
201 | |
202 ~URLLoaderFactoryImplTest() override { | |
203 rdh_.SetDelegate(nullptr); | |
204 net::URLRequestFilter::GetInstance()->ClearHandlers(); | |
205 | |
206 rdh_.CancelRequestsForProcess(resource_message_filter_->child_id()); | |
207 base::RunLoop().RunUntilIdle(); | |
208 } | |
209 | |
210 void GetContexts(ResourceType resource_type, | |
211 int origin_pid, | |
212 ResourceContext** resource_context, | |
213 net::URLRequestContext** request_context) { | |
214 *resource_context = browser_context_->GetResourceContext(); | |
215 *request_context = | |
216 browser_context_->GetResourceContext()->GetRequestContext(); | |
217 } | |
218 | |
219 void RunUntilNextNotification(FakeURLLoaderClient* client) { | |
220 base::RunLoop run_loop; | |
221 client->set_quit_closure(run_loop.QuitClosure()); | |
222 run_loop.Run(); | |
223 } | |
224 | |
225 TestBrowserThreadBundle thread_bundle_; | |
226 ResourceDispatcherHostImpl rdh_; | |
227 std::unique_ptr<TestBrowserContext> browser_context_; | |
228 scoped_refptr<ResourceMessageFilter> resource_message_filter_; | |
229 mojom::URLLoaderFactoryPtr factory_; | |
230 std::unique_ptr<URLLoaderFactoryHolder> factory_impl_holder_; | |
231 | |
232 DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryImplTest); | |
233 }; | |
234 | |
235 TEST_P(URLLoaderFactoryImplTest, GetResponse) { | |
236 mojom::URLLoaderPtr loader; | |
237 base::FilePath root; | |
238 PathService::Get(DIR_TEST_DATA, &root); | |
239 net::URLRequestMockHTTPJob::AddUrlHandlers(root, | |
240 BrowserThread::GetBlockingPool()); | |
mmenke
2016/06/13 20:56:59
Suggest adding all these mock handler setup calls
yhirano
2016/07/12 10:49:11
I'd prefer writing case-specific preparation in th
mmenke
2016/07/12 15:10:45
You call net::URLRequestFailedJob::AddUrlHandler()
yhirano
2016/07/13 12:04:57
I removed one of them at PS49, so there is one net
| |
241 ResourceRequest request; | |
242 FakeURLLoaderClient client; | |
243 // Assume the file contents is small enough to be stored in the data pipe. | |
244 request.url = net::URLRequestMockHTTPJob::GetMockUrl("hello.html"); | |
245 request.method = "GET"; | |
246 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
247 client.CreateInterfacePtrAndBind()); | |
248 | |
249 ASSERT_FALSE(client.has_received_response()); | |
250 ASSERT_FALSE(client.response_body().is_valid()); | |
251 ASSERT_FALSE(client.has_received_completion()); | |
252 | |
253 RunUntilNextNotification(&client); | |
254 ASSERT_TRUE(client.has_received_response()); | |
255 ASSERT_FALSE(client.has_received_completion()); | |
256 ASSERT_FALSE(client.has_received_completion()); | |
257 | |
258 RunUntilNextNotification(&client); | |
259 ASSERT_TRUE(client.has_received_response()); | |
260 ASSERT_TRUE(client.response_body().is_valid()); | |
261 ASSERT_FALSE(client.has_received_completion()); | |
262 | |
263 RunUntilNextNotification(&client); | |
264 ASSERT_TRUE(client.has_received_completion()); | |
265 | |
266 EXPECT_EQ(200, client.response_head().headers->response_code()); | |
267 std::string content_type; | |
268 client.response_head().headers->GetNormalizedHeader("content-type", | |
269 &content_type); | |
270 EXPECT_EQ("text/html", content_type); | |
271 EXPECT_EQ(0, client.completion_status().error_code); | |
272 | |
273 std::string contents; | |
274 while (true) { | |
275 char buffer[16]; | |
276 uint32_t read = sizeof(buffer); | |
277 MojoResult r = mojo::ReadDataRaw(client.response_body(), buffer, &read, | |
278 MOJO_READ_DATA_FLAG_NONE); | |
279 if (r == MOJO_RESULT_FAILED_PRECONDITION) | |
280 break; | |
281 if (r == MOJO_RESULT_SHOULD_WAIT) | |
282 continue; | |
283 ASSERT_EQ(MOJO_RESULT_OK, r); | |
284 contents += std::string(buffer, read); | |
285 } | |
286 EXPECT_EQ( | |
287 "<!doctype html>\n" | |
288 "<p>hello</p>\n", | |
289 contents); | |
290 } | |
291 | |
292 TEST_P(URLLoaderFactoryImplTest, GetFailedResponse) { | |
293 mojom::URLLoaderPtr loader; | |
294 ResourceRequest request; | |
295 FakeURLLoaderClient client; | |
296 net::URLRequestFailedJob::AddUrlHandler(); | |
297 request.url = net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase( | |
298 net::URLRequestFailedJob::START, net::ERR_TIMED_OUT); | |
299 request.method = "GET"; | |
300 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
301 client.CreateInterfacePtrAndBind()); | |
302 | |
303 RunUntilNextNotification(&client); | |
304 ASSERT_FALSE(client.has_received_response()); | |
305 ASSERT_FALSE(client.response_body().is_valid()); | |
306 ASSERT_TRUE(client.has_received_completion()); | |
307 | |
308 EXPECT_EQ(net::ERR_TIMED_OUT, client.completion_status().error_code); | |
309 } | |
310 | |
311 // This test tests a case where resource loading is cancelled before started. | |
312 TEST_P(URLLoaderFactoryImplTest, InvalidURL) { | |
313 mojom::URLLoaderPtr loader; | |
314 ResourceRequest request; | |
315 FakeURLLoaderClient client; | |
316 request.url = GURL(); | |
317 request.method = "GET"; | |
318 ASSERT_FALSE(request.url.is_valid()); | |
319 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
320 client.CreateInterfacePtrAndBind()); | |
321 | |
322 RunUntilNextNotification(&client); | |
323 ASSERT_FALSE(client.has_received_response()); | |
324 ASSERT_FALSE(client.response_body().is_valid()); | |
325 ASSERT_TRUE(client.has_received_completion()); | |
326 | |
327 EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); | |
328 } | |
329 | |
330 // This test tests a case where resource loading is cancelled before started. | |
331 TEST_P(URLLoaderFactoryImplTest, ShouldNotRequestURL) { | |
332 mojom::URLLoaderPtr loader; | |
333 RejectingResourceDispatcherHostDelegate rdh_delegate; | |
334 rdh_.SetDelegate(&rdh_delegate); | |
335 ResourceRequest request; | |
336 FakeURLLoaderClient client; | |
337 request.url = GURL("http://localhost/"); | |
338 request.method = "GET"; | |
339 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
340 client.CreateInterfacePtrAndBind()); | |
341 | |
342 RunUntilNextNotification(&client); | |
343 rdh_.SetDelegate(nullptr); | |
344 | |
345 ASSERT_FALSE(client.has_received_response()); | |
346 ASSERT_FALSE(client.response_body().is_valid()); | |
347 ASSERT_TRUE(client.has_received_completion()); | |
348 | |
349 EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); | |
350 } | |
351 | |
352 TEST_P(URLLoaderFactoryImplTest, FailedAfterResponseStarted) { | |
353 mojom::URLLoaderPtr loader; | |
354 ResourceRequest request; | |
355 FakeURLLoaderClient client; | |
356 net::URLRequestFailedJob::AddUrlHandler(); | |
357 request.url = net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase( | |
358 net::URLRequestFailedJob::READ_ASYNC, net::ERR_TIMED_OUT); | |
359 request.method = "GET"; | |
360 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
361 client.CreateInterfacePtrAndBind()); | |
362 | |
363 ASSERT_FALSE(client.has_received_response()); | |
364 ASSERT_FALSE(client.response_body().is_valid()); | |
365 ASSERT_FALSE(client.has_received_completion()); | |
366 | |
367 RunUntilNextNotification(&client); | |
368 ASSERT_FALSE(client.has_received_response()); | |
369 ASSERT_TRUE(client.response_body().is_valid()); | |
370 ASSERT_FALSE(client.has_received_completion()); | |
371 | |
372 RunUntilNextNotification(&client); | |
373 ASSERT_FALSE(client.has_received_response()); | |
374 ASSERT_TRUE(client.response_body().is_valid()); | |
375 ASSERT_TRUE(client.has_received_completion()); | |
376 | |
377 EXPECT_EQ(net::ERR_TIMED_OUT, client.completion_status().error_code); | |
378 } | |
379 | |
380 TEST_P(URLLoaderFactoryImplTest, BrokenBody) { | |
381 mojom::URLLoaderPtr loader; | |
382 ResourceRequest request; | |
383 FakeURLLoaderClient client; | |
384 request.url = GURL("http://www.example.com/test"); | |
385 request.method = "GET"; | |
386 net::URLRequestFilter::GetInstance()->AddUrlInterceptor( | |
387 request.url, | |
388 base::WrapUnique(new TestInterceptorForBrokenBody(request.url))); | |
389 factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 1, request, | |
390 client.CreateInterfacePtrAndBind()); | |
391 | |
392 ASSERT_FALSE(client.has_received_response()); | |
393 ASSERT_FALSE(client.response_body().is_valid()); | |
394 ASSERT_FALSE(client.has_received_completion()); | |
395 | |
396 RunUntilNextNotification(&client); | |
397 ASSERT_FALSE(client.has_received_response()); | |
398 ASSERT_TRUE(client.response_body().is_valid()); | |
399 ASSERT_TRUE(client.has_received_completion()); | |
400 | |
401 EXPECT_EQ(net::ERR_FAILED, client.completion_status().error_code); | |
402 } | |
403 | |
404 INSTANTIATE_TEST_CASE_P(URLLoaderFactoryImplTest, | |
405 URLLoaderFactoryImplTest, | |
406 ::testing::Values(128, 32 * 1024)); | |
mmenke
2016/06/13 20:56:58
None of these tests seem to return anything with a
yhirano
2016/07/12 10:49:11
The parameter is not for such cases. The mime snif
| |
407 | |
408 } // namespace | |
mmenke
2016/06/13 20:56:58
How about a test that fails the CheckForSufficient
mmenke
2016/06/13 20:56:58
Can we make BeginWriteDataRaw return sync/async/er
mmenke
2016/06/13 20:56:59
Should have tests that check each of the strings i
yhirano
2016/07/12 10:49:11
These are tested in mojo_async_resource_handler_un
| |
409 | |
410 } // namespace content | |
OLD | NEW |