Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(199)

Side by Side Diff: content/browser/loader/async_revalidation_manager_unittest.cc

Issue 1041993004: content::ResourceDispatcherHostImpl changes for stale-while-revalidate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@s-w-r-yhirano-patch
Patch Set: Remove unnecessary deferral logic from AsyncRevalidationDriver Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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_revalidation_manager.h"
6
7 #include <queue>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/macros.h"
13 #include "base/memory/shared_memory_handle.h"
14 #include "base/pickle.h"
15 #include "base/run_loop.h"
16 #include "content/browser/child_process_security_policy_impl.h"
17 #include "content/browser/loader/resource_dispatcher_host_impl.h"
18 #include "content/browser/loader/resource_message_filter.h"
19 #include "content/common/child_process_host_impl.h"
20 #include "content/common/resource_messages.h"
21 #include "content/public/browser/resource_context.h"
22 #include "content/public/common/appcache_info.h"
23 #include "content/public/common/process_type.h"
24 #include "content/public/common/resource_type.h"
25 #include "content/public/test/test_browser_context.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "ipc/ipc_param_traits.h"
28 #include "net/base/load_flags.h"
29 #include "net/base/network_delegate.h"
30 #include "net/http/http_util.h"
31 #include "net/url_request/url_request.h"
32 #include "net/url_request/url_request_job.h"
33 #include "net/url_request/url_request_job_factory.h"
34 #include "net/url_request/url_request_test_job.h"
35 #include "net/url_request/url_request_test_util.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "ui/base/page_transition_types.h"
38 #include "url/gurl.h"
39
40 namespace content {
41
42 namespace {
43
44 class TestURLRequestJobFactory : public net::URLRequestJobFactory {
45 public:
46 TestURLRequestJobFactory() = default;
47
48 void HandleScheme(const std::string& scheme) {
49 supported_schemes_.insert(scheme);
50 }
51
52 // Sets the contents of the response. |headers| should have "\n" as line
53 // breaks and end in "\n\n".
54 void SetResponse(const std::string& headers, const std::string& data) {
55 response_headers_ =
56 net::HttpUtil::AssembleRawHeaders(headers.data(), headers.size());
57 response_data_ = data;
58 }
59
60 typedef base::Callback<net::URLRequestJob*(net::URLRequest*,
Bence 2015/11/17 13:12:23 Use "using" instead of "typedef", see https://chro
Adam Rice 2015/11/17 17:45:53 Done.
61 net::NetworkDelegate*)>
62 URLRequestJobCreateCallback;
63
64 void SetCustomURLRequestJobCreateCallback(
65 const URLRequestJobCreateCallback& callback) {
66 custom_url_request_job_create_callback_ = callback;
67 }
68
69 net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
Bence 2015/11/17 13:12:23 Optional: feel free to inline function definition
Adam Rice 2015/11/17 17:45:53 Done.
70 const std::string& scheme,
71 net::URLRequest* request,
72 net::NetworkDelegate* network_delegate) const override;
73
74 net::URLRequestJob* MaybeInterceptRedirect(
75 net::URLRequest* request,
76 net::NetworkDelegate* network_delegate,
77 const GURL& location) const override {
78 return nullptr;
79 }
80
81 net::URLRequestJob* MaybeInterceptResponse(
82 net::URLRequest* request,
83 net::NetworkDelegate* network_delegate) const override {
84 return nullptr;
85 }
86
87 bool IsHandledProtocol(const std::string& scheme) const override {
88 return supported_schemes_.count(scheme) > 0;
89 }
90
91 bool IsHandledURL(const GURL& url) const override {
92 return supported_schemes_.count(url.scheme()) > 0;
93 }
94
95 bool IsSafeRedirectTarget(const GURL& location) const override {
96 return false;
97 }
98
99 private:
100 std::string response_headers_;
101 std::string response_data_;
102 std::set<std::string> supported_schemes_;
103 URLRequestJobCreateCallback custom_url_request_job_create_callback_;
104
105 DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory);
106 };
107
108 // This class is a variation on URLRequestTestJob that
109 // returns ERR_IO_PENDING before every read, not just the first one.
110 class URLRequestTestDelayedCompletionJob : public net::URLRequestTestJob {
111 public:
112 URLRequestTestDelayedCompletionJob(net::URLRequest* request,
113 net::NetworkDelegate* network_delegate,
114 const std::string& response_headers,
115 const std::string& response_data)
116 : net::URLRequestTestJob(request,
117 network_delegate,
118 response_headers,
119 response_data,
120 false) {}
121
122 private:
123 ~URLRequestTestDelayedCompletionJob() override {}
124
125 bool NextReadAsync() override { return true; }
126
127 DISALLOW_COPY_AND_ASSIGN(URLRequestTestDelayedCompletionJob);
128 };
129
130 net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
131 const std::string& scheme,
132 net::URLRequest* request,
133 net::NetworkDelegate* network_delegate) const {
134 if (!custom_url_request_job_create_callback_.is_null()) {
135 return custom_url_request_job_create_callback_.Run(request,
136 network_delegate);
137 }
138
139 return new URLRequestTestDelayedCompletionJob(
140 request, network_delegate, response_headers_, response_data_);
141 }
142
143 // On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not
144 // automatically released.
145 //
146 // See ResourceDispatcher::ReleaseResourcesInDataMessage.
147 //
148 // TODO(ricea): Maybe share this implementation with
149 // resource_dispatcher_host_unittest.cc
150 void ReleaseHandlesInMessage(const IPC::Message& message) {
151 if (message.type() == ResourceMsg_SetDataBuffer::ID) {
152 base::PickleIterator iter(message);
153 int request_id;
154 CHECK(iter.ReadInt(&request_id));
155 base::SharedMemoryHandle shm_handle;
156 if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message, &iter,
157 &shm_handle)) {
158 if (base::SharedMemory::IsHandleValid(shm_handle))
159 base::SharedMemory::CloseHandle(shm_handle);
160 }
161 }
162 }
163
164 // This filter just deletes any messages that are sent through it.
165 class BlackholeFilter : public ResourceMessageFilter {
166 public:
167 explicit BlackholeFilter(ResourceContext* resource_context)
168 : ResourceMessageFilter(
169 ChildProcessHostImpl::GenerateChildProcessUniqueId(),
170 PROCESS_TYPE_RENDERER,
171 NULL,
Bence 2015/11/17 13:12:23 Use nullptr instead of NULL.
Adam Rice 2015/11/17 17:45:53 Done.
172 NULL,
173 NULL,
174 NULL,
175 NULL,
176 base::Bind(&BlackholeFilter::GetContexts, base::Unretained(this))),
177 resource_context_(resource_context) {
178 ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
179 }
180
181 bool Send(IPC::Message* msg) override {
182 ReleaseHandlesInMessage(*msg);
183 delete msg;
184 return true;
185 }
186
187 private:
188 ~BlackholeFilter() override {
189 ChildProcessSecurityPolicyImpl::GetInstance()->Remove(child_id());
190 }
191
192 void GetContexts(const ResourceHostMsg_Request& request,
193 ResourceContext** resource_context,
194 net::URLRequestContext** request_context) {
195 *resource_context = resource_context_;
196 *request_context = resource_context_->GetRequestContext();
197 }
198
199 ResourceContext* resource_context_;
200
201 DISALLOW_COPY_AND_ASSIGN(BlackholeFilter);
202 };
203
204 ResourceHostMsg_Request CreateResourceRequest(const char* method,
205 ResourceType type,
206 const GURL& url) {
207 ResourceHostMsg_Request request;
208 request.method = std::string(method);
209 request.url = url;
210 request.first_party_for_cookies = url; // bypass third-party cookie blocking
211 request.referrer_policy = blink::WebReferrerPolicyDefault;
212 request.load_flags = 0;
213 request.origin_pid = 0;
214 request.resource_type = type;
215 request.request_context = 0;
216 request.appcache_host_id = kAppCacheNoHostId;
217 request.download_to_file = false;
218 request.should_reset_appcache = false;
219 request.is_main_frame = true;
220 request.parent_is_main_frame = false;
221 request.parent_render_frame_id = -1;
222 request.transition_type = ui::PAGE_TRANSITION_LINK;
223 request.allow_download = true;
224 return request;
225 }
226
227 } // namespace
228
229 class AsyncRevalidationManagerTest : public ::testing::Test {
230 protected:
231 AsyncRevalidationManagerTest(
232 scoped_ptr<net::TestNetworkDelegate> network_delegate)
233 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
234 network_delegate_(network_delegate.Pass()) {
235 browser_context_.reset(new TestBrowserContext());
236 BrowserContext::EnsureResourceContextInitialized(browser_context_.get());
237 base::RunLoop().RunUntilIdle();
238 ResourceContext* resource_context = browser_context_->GetResourceContext();
239 filter_ = new BlackholeFilter(resource_context);
240 net::URLRequestContext* request_context =
241 resource_context->GetRequestContext();
242 job_factory_.reset(new TestURLRequestJobFactory);
243 request_context->set_job_factory(job_factory_.get());
244 request_context->set_network_delegate(network_delegate_.get());
245 host_.EnableStaleWhileRevalidateForTesting();
246 }
247
248 AsyncRevalidationManagerTest()
249 : AsyncRevalidationManagerTest(
250 make_scoped_ptr(new net::TestNetworkDelegate)) {}
251
252 void TearDown() override {
253 host_.CancelRequestsForProcess(filter_->child_id());
254 host_.Shutdown();
255 host_.CancelRequestsForContext(browser_context_->GetResourceContext());
256 browser_context_.reset();
257 base::RunLoop().RunUntilIdle();
258 }
259
260 void HandleScheme(const std::string& scheme) {
261 job_factory_->HandleScheme(scheme);
262 EnsureSchemeIsAllowed(scheme);
263 }
264
265 void SetResponse(const std::string& headers, const std::string& data) {
266 job_factory_->SetResponse(headers, data);
267 }
268
269 void SetCustomURLRequestJobCreateCallback(
270 const TestURLRequestJobFactory::URLRequestJobCreateCallback& callback) {
271 job_factory_->SetCustomURLRequestJobCreateCallback(callback);
272 }
273
274 // Creates a request using the current test object as the filter and
275 // SubResource as the resource type.
276 void MakeTestRequest(int render_view_id, int request_id, const GURL& url);
Bence 2015/11/17 13:12:23 Optional: feel free to inline if you prefer, it sh
Adam Rice 2015/11/17 17:45:53 Done.
277
278 void EnsureSchemeIsAllowed(const std::string& scheme) {
279 ChildProcessSecurityPolicyImpl* policy =
280 ChildProcessSecurityPolicyImpl::GetInstance();
281 if (!policy->IsWebSafeScheme(scheme))
282 policy->RegisterWebSafeScheme(scheme);
283 }
284
285 net::TestNetworkDelegate* network_delegate() {
286 return network_delegate_.get();
287 }
288
289 content::TestBrowserThreadBundle thread_bundle_;
290 scoped_ptr<TestBrowserContext> browser_context_;
291 scoped_ptr<TestURLRequestJobFactory> job_factory_;
292 scoped_refptr<BlackholeFilter> filter_;
293 scoped_ptr<net::TestNetworkDelegate> network_delegate_;
294 ResourceDispatcherHostImpl host_;
295 };
296
297 void AsyncRevalidationManagerTest::MakeTestRequest(int render_view_id,
298 int request_id,
299 const GURL& url) {
300 ResourceHostMsg_Request request =
301 CreateResourceRequest("GET", RESOURCE_TYPE_SUB_RESOURCE, url);
302 ResourceHostMsg_RequestResource msg(render_view_id, request_id, request);
303 host_.OnMessageReceived(msg, filter_.get());
304 base::RunLoop().RunUntilIdle();
305 }
306
307 TEST_F(AsyncRevalidationManagerTest, SupportsAsyncRevalidation) {
308 // Scheme has to be HTTP or HTTPS to support async revalidation.
309 HandleScheme("http");
310 SetResponse(net::URLRequestTestJob::test_headers(), "delay complete");
311 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
312
313 net::URLRequest* url_request(
314 host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
315 ASSERT_TRUE(url_request);
316
317 EXPECT_TRUE(url_request->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION);
318 }
319
320 TEST_F(AsyncRevalidationManagerTest, AsyncRevalidationNotSupportedForPOST) {
321 // Scheme has to be HTTP or HTTPS to support async revalidation.
322 HandleScheme("http");
323 SetResponse(net::URLRequestTestJob::test_headers(), "delay complete");
324 // Create POST request.
325 ResourceHostMsg_Request request = CreateResourceRequest(
326 "POST", RESOURCE_TYPE_SUB_RESOURCE, GURL("http://example.com/baz.php"));
327 ResourceHostMsg_RequestResource msg(0, 1, request);
328 host_.OnMessageReceived(msg, filter_.get());
329 base::RunLoop().RunUntilIdle();
330
331 net::URLRequest* url_request(
332 host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
333 ASSERT_TRUE(url_request);
334
335 EXPECT_FALSE(url_request->load_flags() &
336 net::LOAD_SUPPORT_ASYNC_REVALIDATION);
337 }
338
339 TEST_F(AsyncRevalidationManagerTest, AsyncRevalidationNotSupportedForRedirect) {
340 static const char kRedirectHeaders[] =
341 "HTTP/1.1 302 MOVED\n"
342 "Location: http://example.com/var\n"
343 "\n";
344 // Scheme has to be HTTP or HTTPS to support async revalidation.
345 HandleScheme("http");
346 SetResponse(kRedirectHeaders, "");
347
348 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
349
350 net::URLRequest* url_request(
351 host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
352 ASSERT_TRUE(url_request);
353
354 EXPECT_FALSE(url_request->load_flags() &
355 net::LOAD_SUPPORT_ASYNC_REVALIDATION);
356 }
357
358 // A URLRequestJob implementation which sets the |async_revalidation_required|
359 // flag on the HttpResponseInfo object to true if the request has the
360 // LOAD_SUPPORT_ASYNC_REVALIDATION flag.
361 class AsyncRevalidationRequiredURLRequestTestJob
362 : public net::URLRequestTestJob {
363 public:
364 // The Create() method is useful for wrapping the construction of the object
365 // in a Callback.
366 static net::URLRequestJob* Create(net::URLRequest* request,
367 net::NetworkDelegate* network_delegate) {
368 return new AsyncRevalidationRequiredURLRequestTestJob(request,
369 network_delegate);
370 }
371
372 void GetResponseInfo(net::HttpResponseInfo* info) override {
373 if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION)
374 info->async_revalidation_required = true;
375 }
376
377 private:
378 AsyncRevalidationRequiredURLRequestTestJob(
379 net::URLRequest* request,
380 net::NetworkDelegate* network_delegate)
381 : URLRequestTestJob(request,
382 network_delegate,
383 net::URLRequestTestJob::test_headers(),
384 std::string(),
385 false) {}
386
387 ~AsyncRevalidationRequiredURLRequestTestJob() override {}
388
389 DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationRequiredURLRequestTestJob);
390 };
391
392 // A URLRequestJob implementation which serves a redirect and sets the
393 // |async_revalidation_required| flag on the HttpResponseInfo object to true if
394 // the request has the LOAD_SUPPORT_ASYNC_REVALIDATION flag.
395 class RedirectAndRevalidateURLRequestTestJob : public net::URLRequestTestJob {
396 public:
397 // The Create() method is useful for wrapping the construction of the object
398 // in a Callback.
399 static net::URLRequestJob* Create(net::URLRequest* request,
400 net::NetworkDelegate* network_delegate) {
401 return new RedirectAndRevalidateURLRequestTestJob(request,
402 network_delegate);
403 }
404
405 void GetResponseInfo(net::HttpResponseInfo* info) override {
406 URLRequestTestJob::GetResponseInfo(info);
407 if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION)
408 info->async_revalidation_required = true;
409 }
410
411 private:
412 RedirectAndRevalidateURLRequestTestJob(net::URLRequest* request,
413 net::NetworkDelegate* network_delegate)
414 : URLRequestTestJob(request,
415 network_delegate,
416 std::string(CreateRedirectHeaders()),
417 std::string(),
418 false) {}
419
420 ~RedirectAndRevalidateURLRequestTestJob() override {}
421
422 static std::string CreateRedirectHeaders() {
423 static const char kRedirectHeaders[] =
424 "HTTP/1.1 302 MOVED\0"
425 "Location: http://example.com/var\0"
426 "\0";
427 return std::string(kRedirectHeaders, arraysize(kRedirectHeaders));
428 }
429
430 DISALLOW_COPY_AND_ASSIGN(RedirectAndRevalidateURLRequestTestJob);
431 };
432
433 // A NetworkDelegate that records the URLRequests as they are created.
434 class URLRequestRecordingNetworkDelegate : public net::TestNetworkDelegate {
435 public:
436 URLRequestRecordingNetworkDelegate() : requests_() {}
437
438 net::URLRequest* NextRequest() {
439 if (requests_.empty())
440 return nullptr;
441 net::URLRequest* request = requests_.front();
442 requests_.pop();
443 return request;
444 }
445
446 bool IsEmpty() { return requests_.empty(); }
447
448 int OnBeforeURLRequest(net::URLRequest* request,
449 const net::CompletionCallback& callback,
450 GURL* new_url) override {
451 requests_.push(request);
452 return TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url);
453 }
454
455 private:
456 std::queue<net::URLRequest*> requests_;
457
458 DISALLOW_COPY_AND_ASSIGN(URLRequestRecordingNetworkDelegate);
459 };
460
461 class AsyncRevalidationManagerRecordingTest
462 : public AsyncRevalidationManagerTest {
463 public:
464 AsyncRevalidationManagerRecordingTest()
465 : AsyncRevalidationManagerTest(
466 make_scoped_ptr(new URLRequestRecordingNetworkDelegate)) {
467 // Scheme has to be HTTP or HTTPS to support async revalidation.
468 HandleScheme("http");
469 // Use the AsyncRevalidationRequiredURLRequestTestJob.
470 SetCustomURLRequestJobCreateCallback(
471 base::Bind(&AsyncRevalidationRequiredURLRequestTestJob::Create));
472 }
473
474 URLRequestRecordingNetworkDelegate* recording_network_delegate() {
475 return static_cast<URLRequestRecordingNetworkDelegate*>(network_delegate());
476 }
477
478 net::URLRequest* NextRequest() {
479 return recording_network_delegate()->NextRequest();
480 }
481
482 bool IsEmpty() { return recording_network_delegate()->IsEmpty(); }
483 };
484
485 // Verify that an async revalidation is actually created when needed.
486 TEST_F(AsyncRevalidationManagerRecordingTest, Issued) {
487 // Create the original request.
488 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
489
490 net::URLRequest* initial_request = NextRequest();
491 ASSERT_TRUE(initial_request);
492 EXPECT_TRUE(initial_request->load_flags() &
493 net::LOAD_SUPPORT_ASYNC_REVALIDATION);
494
495 net::URLRequest* async_request = NextRequest();
496 ASSERT_TRUE(async_request);
497 }
498
499 // Verify the the URL of the async revalidation matches the original request.
500 TEST_F(AsyncRevalidationManagerRecordingTest, URLMatches) {
501 // Create the original request.
502 MakeTestRequest(0, 1, GURL("http://example.com/special-baz"));
503
504 // Discard the original request.
505 NextRequest();
506
507 net::URLRequest* async_request = NextRequest();
508 ASSERT_TRUE(async_request);
509 EXPECT_EQ(GURL("http://example.com/special-baz"), async_request->url());
510 }
511
512 TEST_F(AsyncRevalidationManagerRecordingTest,
513 AsyncRevalidationsDoNotSupportAsyncRevalidation) {
514 // Create the original request.
515 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
516
517 // Discard the original request.
518 NextRequest();
519
520 // Get the async revalidation request.
521 net::URLRequest* async_request = NextRequest();
522 ASSERT_TRUE(async_request);
523 EXPECT_FALSE(async_request->load_flags() &
524 net::LOAD_SUPPORT_ASYNC_REVALIDATION);
525 }
526
527 TEST_F(AsyncRevalidationManagerRecordingTest, AsyncRevalidationsNotDuplicated) {
528 // Create the original request.
529 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
530
531 // Discard the original request.
532 NextRequest();
533
534 // Get the async revalidation request.
535 net::URLRequest* async_request = NextRequest();
536 EXPECT_TRUE(async_request);
537
538 // Start a second request to the same URL.
539 MakeTestRequest(0, 2, GURL("http://example.com/baz"));
540
541 // Discard the second request.
542 NextRequest();
543
544 // There should not be another async revalidation request.
545 EXPECT_TRUE(IsEmpty());
546 }
547
548 // Async revalidation to different URLs should not be treated as duplicates.
549 TEST_F(AsyncRevalidationManagerRecordingTest,
550 AsyncRevalidationsToSeparateURLsAreSeparate) {
551 // Create two requests to two URLs.
552 MakeTestRequest(0, 1, GURL("http://example.com/baz"));
553 MakeTestRequest(0, 2, GURL("http://example.com/far"));
554
555 net::URLRequest* initial_request = NextRequest();
556 ASSERT_TRUE(initial_request);
557 net::URLRequest* initial_async_revalidation = NextRequest();
558 ASSERT_TRUE(initial_async_revalidation);
559 net::URLRequest* second_request = NextRequest();
560 ASSERT_TRUE(second_request);
561 net::URLRequest* second_async_revalidation = NextRequest();
562 ASSERT_TRUE(second_async_revalidation);
563
564 EXPECT_EQ("http://example.com/baz", initial_request->url().spec());
565 EXPECT_EQ("http://example.com/baz", initial_async_revalidation->url().spec());
566 EXPECT_EQ("http://example.com/far", second_request->url().spec());
567 EXPECT_EQ("http://example.com/far", second_async_revalidation->url().spec());
568 }
569
570 // A stale-while-revalidate applicable redirect response should not result in an
571 // async revalidation.
572 TEST_F(AsyncRevalidationManagerRecordingTest, RedirectNotApplicable) {
573 // Use the appropriate URLRequestJob for the test.
574 SetCustomURLRequestJobCreateCallback(
575 base::Bind(&RedirectAndRevalidateURLRequestTestJob::Create));
576 MakeTestRequest(0, 1, GURL("http://example.com/redirect"));
577
578 net::URLRequest* initial_request = NextRequest();
579 EXPECT_TRUE(initial_request);
580
581 // There should not be an async revalidation request.
582 EXPECT_TRUE(IsEmpty());
583 }
584
585 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698