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

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