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

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

Powered by Google App Engine
This is Rietveld 408576698