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

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

Powered by Google App Engine
This is Rietveld 408576698