appcache_request_handler_unittest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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 <stack> 6#include <string> 7#include <vector> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/callback.h" 12#include "base/message_loop/message_loop.h" 13#include "base/synchronization/waitable_event.h" 14#include "base/threading/thread.h" 15#include "content/browser/appcache/appcache.h" 16#include "content/browser/appcache/appcache_backend_impl.h" 17#include "content/browser/appcache/appcache_request_handler.h" 18#include "content/browser/appcache/appcache_url_request_job.h" 19#include "content/browser/appcache/mock_appcache_policy.h" 20#include "content/browser/appcache/mock_appcache_service.h" 21#include "net/base/net_errors.h" 22#include "net/base/request_priority.h" 23#include "net/http/http_response_headers.h" 24#include "net/url_request/url_request.h" 25#include "net/url_request/url_request_context.h" 26#include "net/url_request/url_request_error_job.h" 27#include "net/url_request/url_request_job_factory.h" 28#include "testing/gtest/include/gtest/gtest.h" 29 30namespace content { 31 32static const int kMockProcessId = 1; 33 34class AppCacheRequestHandlerTest : public testing::Test { 35 public: 36 class MockFrontend : public AppCacheFrontend { 37 public: 38 virtual void OnCacheSelected( 39 int host_id, const AppCacheInfo& info) OVERRIDE {} 40 41 virtual void OnStatusChanged(const std::vector<int>& host_ids, 42 AppCacheStatus status) OVERRIDE {} 43 44 virtual void OnEventRaised(const std::vector<int>& host_ids, 45 AppCacheEventID event_id) OVERRIDE {} 46 47 virtual void OnErrorEventRaised( 48 const std::vector<int>& host_ids, 49 const AppCacheErrorDetails& details) OVERRIDE {} 50 51 virtual void OnProgressEventRaised(const std::vector<int>& host_ids, 52 const GURL& url, 53 int num_total, 54 int num_complete) OVERRIDE { 55 } 56 57 virtual void OnLogMessage(int host_id, 58 AppCacheLogLevel log_level, 59 const std::string& message) OVERRIDE { 60 } 61 62 virtual void OnContentBlocked(int host_id, 63 const GURL& manifest_url) OVERRIDE {} 64 }; 65 66 // Helper callback to run a test on our io_thread. The io_thread is spun up 67 // once and reused for all tests. 68 template <class Method> 69 void MethodWrapper(Method method) { 70 SetUpTest(); 71 (this->*method)(); 72 } 73 74 // Subclasses to simulate particular responses so test cases can 75 // exercise fallback code paths. 76 77 class MockURLRequestDelegate : public net::URLRequest::Delegate { 78 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {} 79 virtual void OnReadCompleted(net::URLRequest* request, 80 int bytes_read) OVERRIDE { 81 } 82 }; 83 84 class MockURLRequestJob : public net::URLRequestJob { 85 public: 86 MockURLRequestJob(net::URLRequest* request, 87 net::NetworkDelegate* network_delegate, 88 int response_code) 89 : net::URLRequestJob(request, network_delegate), 90 response_code_(response_code), 91 has_response_info_(false) {} 92 MockURLRequestJob(net::URLRequest* request, 93 net::NetworkDelegate* network_delegate, 94 const net::HttpResponseInfo& info) 95 : net::URLRequestJob(request, network_delegate), 96 response_code_(info.headers->response_code()), 97 has_response_info_(true), 98 response_info_(info) {} 99 100 protected: 101 virtual ~MockURLRequestJob() {} 102 virtual void Start() OVERRIDE { 103 NotifyHeadersComplete(); 104 } 105 virtual int GetResponseCode() const OVERRIDE { 106 return response_code_; 107 } 108 virtual void GetResponseInfo( 109 net::HttpResponseInfo* info) OVERRIDE { 110 if (!has_response_info_) 111 return; 112 *info = response_info_; 113 } 114 115 private: 116 int response_code_; 117 bool has_response_info_; 118 net::HttpResponseInfo response_info_; 119 }; 120 121 class MockURLRequestJobFactory : public net::URLRequestJobFactory { 122 public: 123 MockURLRequestJobFactory() : job_(NULL) { 124 } 125 126 virtual ~MockURLRequestJobFactory() { 127 DCHECK(!job_); 128 } 129 130 void SetJob(net::URLRequestJob* job) { 131 job_ = job; 132 } 133 134 virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler( 135 const std::string& scheme, 136 net::URLRequest* request, 137 net::NetworkDelegate* network_delegate) const OVERRIDE { 138 if (job_) { 139 net::URLRequestJob* temp = job_; 140 job_ = NULL; 141 return temp; 142 } else { 143 // Some of these tests trigger UpdateJobs which start URLRequests. 144 // We short circuit those be returning error jobs. 145 return new net::URLRequestErrorJob(request, 146 network_delegate, 147 net::ERR_INTERNET_DISCONNECTED); 148 } 149 } 150 151 virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE { 152 return scheme == "http"; 153 }; 154 155 virtual bool IsHandledURL(const GURL& url) const OVERRIDE { 156 return url.SchemeIs("http"); 157 } 158 159 virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE { 160 return false; 161 } 162 163 private: 164 mutable net::URLRequestJob* job_; 165 }; 166 167 class MockURLRequest : public net::URLRequest { 168 public: 169 MockURLRequest(const GURL& url, net::URLRequestContext* context) 170 : net::URLRequest(url, net::DEFAULT_PRIORITY, NULL, context) {} 171 172 173 MockURLRequestDelegate delegate_; 174 }; 175 176 static void SetUpTestCase() { 177 io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread")); 178 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); 179 io_thread_->StartWithOptions(options); 180 } 181 182 static void TearDownTestCase() { 183 io_thread_.reset(NULL); 184 } 185 186 // Test harness -------------------------------------------------- 187 188 AppCacheRequestHandlerTest() : host_(NULL) {} 189 190 template <class Method> 191 void RunTestOnIOThread(Method method) { 192 test_finished_event_ .reset(new base::WaitableEvent(false, false)); 193 io_thread_->message_loop()->PostTask( 194 FROM_HERE, 195 base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>, 196 base::Unretained(this), method)); 197 test_finished_event_->Wait(); 198 } 199 200 void SetUpTest() { 201 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 202 mock_service_.reset(new MockAppCacheService); 203 mock_service_->set_request_context(&empty_context_); 204 mock_policy_.reset(new MockAppCachePolicy); 205 mock_service_->set_appcache_policy(mock_policy_.get()); 206 mock_frontend_.reset(new MockFrontend); 207 backend_impl_.reset(new AppCacheBackendImpl); 208 backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(), 209 kMockProcessId); 210 const int kHostId = 1; 211 backend_impl_->RegisterHost(kHostId); 212 host_ = backend_impl_->GetHost(kHostId); 213 job_factory_.reset(new MockURLRequestJobFactory()); 214 empty_context_.set_job_factory(job_factory_.get()); 215 } 216 217 void TearDownTest() { 218 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 219 job_ = NULL; 220 handler_.reset(); 221 request_.reset(); 222 backend_impl_.reset(); 223 mock_frontend_.reset(); 224 mock_service_.reset(); 225 mock_policy_.reset(); 226 job_factory_.reset(); 227 host_ = NULL; 228 } 229 230 void TestFinished() { 231 // We unwind the stack prior to finishing up to let stack 232 // based objects get deleted. 233 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 234 base::MessageLoop::current()->PostTask( 235 FROM_HERE, 236 base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound, 237 base::Unretained(this))); 238 } 239 240 void TestFinishedUnwound() { 241 TearDownTest(); 242 test_finished_event_->Signal(); 243 } 244 245 void PushNextTask(const base::Closure& task) { 246 task_stack_.push(task); 247 } 248 249 void ScheduleNextTask() { 250 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 251 if (task_stack_.empty()) { 252 TestFinished(); 253 return; 254 } 255 base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top()); 256 task_stack_.pop(); 257 } 258 259 // MainResource_Miss -------------------------------------------------- 260 261 void MainResource_Miss() { 262 PushNextTask( 263 base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss, 264 base::Unretained(this))); 265 266 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 267 handler_.reset(host_->CreateRequestHandler(request_.get(), 268 RESOURCE_TYPE_MAIN_FRAME)); 269 EXPECT_TRUE(handler_.get()); 270 271 job_ = handler_->MaybeLoadResource(request_.get(), 272 request_->context()->network_delegate()); 273 EXPECT_TRUE(job_.get()); 274 EXPECT_TRUE(job_->is_waiting()); 275 276 // We have to wait for completion of storage->FindResponseForMainRequest. 277 ScheduleNextTask(); 278 } 279 280 void Verify_MainResource_Miss() { 281 EXPECT_FALSE(job_->is_waiting()); 282 EXPECT_TRUE(job_->is_delivering_network_response()); 283 284 int64 cache_id = kAppCacheNoCacheId; 285 GURL manifest_url; 286 handler_->GetExtraResponseInfo(&cache_id, &manifest_url); 287 EXPECT_EQ(kAppCacheNoCacheId, cache_id); 288 EXPECT_EQ(GURL(), manifest_url); 289 EXPECT_EQ(0, handler_->found_group_id_); 290 291 AppCacheURLRequestJob* fallback_job; 292 fallback_job = handler_->MaybeLoadFallbackForRedirect( 293 request_.get(), 294 request_->context()->network_delegate(), 295 GURL("http://blah/redirect")); 296 EXPECT_FALSE(fallback_job); 297 fallback_job = handler_->MaybeLoadFallbackForResponse( 298 request_.get(), request_->context()->network_delegate()); 299 EXPECT_FALSE(fallback_job); 300 301 EXPECT_TRUE(host_->preferred_manifest_url().is_empty()); 302 303 TestFinished(); 304 } 305 306 // MainResource_Hit -------------------------------------------------- 307 308 void MainResource_Hit() { 309 PushNextTask( 310 base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit, 311 base::Unretained(this))); 312 313 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 314 handler_.reset(host_->CreateRequestHandler(request_.get(), 315 RESOURCE_TYPE_MAIN_FRAME)); 316 EXPECT_TRUE(handler_.get()); 317 318 mock_storage()->SimulateFindMainResource( 319 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), 320 GURL(), AppCacheEntry(), 321 1, 2, GURL("http://blah/manifest/")); 322 323 job_ = handler_->MaybeLoadResource(request_.get(), 324 request_->context()->network_delegate()); 325 EXPECT_TRUE(job_.get()); 326 EXPECT_TRUE(job_->is_waiting()); 327 328 // We have to wait for completion of storage->FindResponseForMainRequest. 329 ScheduleNextTask(); 330 } 331 332 void Verify_MainResource_Hit() { 333 EXPECT_FALSE(job_->is_waiting()); 334 EXPECT_TRUE(job_->is_delivering_appcache_response()); 335 336 int64 cache_id = kAppCacheNoCacheId; 337 GURL manifest_url; 338 handler_->GetExtraResponseInfo(&cache_id, &manifest_url); 339 EXPECT_EQ(1, cache_id); 340 EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url); 341 EXPECT_EQ(2, handler_->found_group_id_); 342 343 AppCacheURLRequestJob* fallback_job; 344 fallback_job = handler_->MaybeLoadFallbackForResponse( 345 request_.get(), request_->context()->network_delegate()); 346 EXPECT_FALSE(fallback_job); 347 348 EXPECT_EQ(GURL("http://blah/manifest/"), 349 host_->preferred_manifest_url()); 350 351 TestFinished(); 352 } 353 354 // MainResource_Fallback -------------------------------------------------- 355 356 void MainResource_Fallback() { 357 PushNextTask( 358 base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback, 359 base::Unretained(this))); 360 361 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 362 handler_.reset(host_->CreateRequestHandler(request_.get(), 363 RESOURCE_TYPE_MAIN_FRAME)); 364 EXPECT_TRUE(handler_.get()); 365 366 mock_storage()->SimulateFindMainResource( 367 AppCacheEntry(), 368 GURL("http://blah/fallbackurl"), 369 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), 370 1, 2, GURL("http://blah/manifest/")); 371 372 job_ = handler_->MaybeLoadResource(request_.get(), 373 request_->context()->network_delegate()); 374 EXPECT_TRUE(job_.get()); 375 EXPECT_TRUE(job_->is_waiting()); 376 377 // We have to wait for completion of storage->FindResponseForMainRequest. 378 ScheduleNextTask(); 379 } 380 381 void SimulateResponseCode(int response_code) { 382 job_factory_->SetJob( 383 new MockURLRequestJob( 384 request_.get(), 385 request_->context()->network_delegate(), 386 response_code)); 387 request_->Start(); 388 // All our simulation needs to satisfy are the following two DCHECKs 389 DCHECK(request_->status().is_success()); 390 DCHECK_EQ(response_code, request_->GetResponseCode()); 391 } 392 393 void SimulateResponseInfo(const net::HttpResponseInfo& info) { 394 job_factory_->SetJob( 395 new MockURLRequestJob( 396 request_.get(), 397 request_->context()->network_delegate(), info)); 398 request_->set_delegate(&request_->delegate_); 399 request_->Start(); 400 } 401 402 void Verify_MainResource_Fallback() { 403 EXPECT_FALSE(job_->is_waiting()); 404 EXPECT_TRUE(job_->is_delivering_network_response()); 405 406 // When the request is restarted, the existing job is dropped so a 407 // real network job gets created. We expect NULL here which will cause 408 // the net library to create a real job. 409 job_ = handler_->MaybeLoadResource(request_.get(), 410 request_->context()->network_delegate()); 411 EXPECT_FALSE(job_.get()); 412 413 // Simulate an http error of the real network job. 414 SimulateResponseCode(500); 415 416 job_ = handler_->MaybeLoadFallbackForResponse( 417 request_.get(), request_->context()->network_delegate()); 418 EXPECT_TRUE(job_.get()); 419 EXPECT_TRUE(job_->is_delivering_appcache_response()); 420 421 int64 cache_id = kAppCacheNoCacheId; 422 GURL manifest_url; 423 handler_->GetExtraResponseInfo(&cache_id, &manifest_url); 424 EXPECT_EQ(1, cache_id); 425 EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url); 426 EXPECT_TRUE(host_->main_resource_was_namespace_entry_); 427 EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_); 428 429 EXPECT_EQ(GURL("http://blah/manifest/"), 430 host_->preferred_manifest_url()); 431 432 TestFinished(); 433 } 434 435 // MainResource_FallbackOverride -------------------------------------------- 436 437 void MainResource_FallbackOverride() { 438 PushNextTask(base::Bind( 439 &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride, 440 base::Unretained(this))); 441 442 request_.reset(new MockURLRequest(GURL("http://blah/fallback-override"), 443 &empty_context_)); 444 handler_.reset(host_->CreateRequestHandler(request_.get(), 445 RESOURCE_TYPE_MAIN_FRAME)); 446 EXPECT_TRUE(handler_.get()); 447 448 mock_storage()->SimulateFindMainResource( 449 AppCacheEntry(), 450 GURL("http://blah/fallbackurl"), 451 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), 452 1, 2, GURL("http://blah/manifest/")); 453 454 job_ = handler_->MaybeLoadResource(request_.get(), 455 request_->context()->network_delegate()); 456 EXPECT_TRUE(job_.get()); 457 EXPECT_TRUE(job_->is_waiting()); 458 459 // We have to wait for completion of storage->FindResponseForMainRequest. 460 ScheduleNextTask(); 461 } 462 463 void Verify_MainResource_FallbackOverride() { 464 EXPECT_FALSE(job_->is_waiting()); 465 EXPECT_TRUE(job_->is_delivering_network_response()); 466 467 // When the request is restarted, the existing job is dropped so a 468 // real network job gets created. We expect NULL here which will cause 469 // the net library to create a real job. 470 job_ = handler_->MaybeLoadResource(request_.get(), 471 request_->context()->network_delegate()); 472 EXPECT_FALSE(job_.get()); 473 474 // Simulate an http error of the real network job, but with custom 475 // headers that override the fallback behavior. 476 const char kOverrideHeaders[] = 477 "HTTP/1.1 404 BOO HOO\0" 478 "x-chromium-appcache-fallback-override: disallow-fallback\0" 479 "\0"; 480 net::HttpResponseInfo info; 481 info.headers = new net::HttpResponseHeaders( 482 std::string(kOverrideHeaders, arraysize(kOverrideHeaders))); 483 SimulateResponseInfo(info); 484 485 job_ = handler_->MaybeLoadFallbackForResponse( 486 request_.get(), request_->context()->network_delegate()); 487 EXPECT_FALSE(job_.get()); 488 489 TestFinished(); 490 } 491 492 // SubResource_Miss_WithNoCacheSelected ---------------------------------- 493 494 void SubResource_Miss_WithNoCacheSelected() { 495 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 496 handler_.reset(host_->CreateRequestHandler(request_.get(), 497 RESOURCE_TYPE_SUB_RESOURCE)); 498 499 // We avoid creating handler when possible, sub-resource requests are not 500 // subject to retrieval from an appcache when there's no associated cache. 501 EXPECT_FALSE(handler_.get()); 502 503 TestFinished(); 504 } 505 506 // SubResource_Miss_WithCacheSelected ---------------------------------- 507 508 void SubResource_Miss_WithCacheSelected() { 509 // A sub-resource load where the resource is not in an appcache, or 510 // in a network or fallback namespace, should result in a failed request. 511 host_->AssociateCompleteCache(MakeNewCache()); 512 513 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 514 handler_.reset(host_->CreateRequestHandler(request_.get(), 515 RESOURCE_TYPE_SUB_RESOURCE)); 516 EXPECT_TRUE(handler_.get()); 517 518 job_ = handler_->MaybeLoadResource(request_.get(), 519 request_->context()->network_delegate()); 520 EXPECT_TRUE(job_.get()); 521 EXPECT_TRUE(job_->is_delivering_error_response()); 522 523 AppCacheURLRequestJob* fallback_job; 524 fallback_job = handler_->MaybeLoadFallbackForRedirect( 525 request_.get(), 526 request_->context()->network_delegate(), 527 GURL("http://blah/redirect")); 528 EXPECT_FALSE(fallback_job); 529 fallback_job = handler_->MaybeLoadFallbackForResponse( 530 request_.get(), request_->context()->network_delegate()); 531 EXPECT_FALSE(fallback_job); 532 533 TestFinished(); 534 } 535 536 // SubResource_Miss_WithWaitForCacheSelection ----------------------------- 537 538 void SubResource_Miss_WithWaitForCacheSelection() { 539 // Precondition, the host is waiting on cache selection. 540 scoped_refptr<AppCache> cache(MakeNewCache()); 541 host_->pending_selected_cache_id_ = cache->cache_id(); 542 host_->set_preferred_manifest_url(cache->owning_group()->manifest_url()); 543 544 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 545 handler_.reset(host_->CreateRequestHandler(request_.get(), 546 RESOURCE_TYPE_SUB_RESOURCE)); 547 EXPECT_TRUE(handler_.get()); 548 job_ = handler_->MaybeLoadResource(request_.get(), 549 request_->context()->network_delegate()); 550 EXPECT_TRUE(job_.get()); 551 EXPECT_TRUE(job_->is_waiting()); 552 553 host_->FinishCacheSelection(cache.get(), NULL); 554 EXPECT_FALSE(job_->is_waiting()); 555 EXPECT_TRUE(job_->is_delivering_error_response()); 556 557 AppCacheURLRequestJob* fallback_job; 558 fallback_job = handler_->MaybeLoadFallbackForRedirect( 559 request_.get(), 560 request_->context()->network_delegate(), 561 GURL("http://blah/redirect")); 562 EXPECT_FALSE(fallback_job); 563 fallback_job = handler_->MaybeLoadFallbackForResponse( 564 request_.get(), request_->context()->network_delegate()); 565 EXPECT_FALSE(fallback_job); 566 567 TestFinished(); 568 } 569 570 // SubResource_Hit ----------------------------- 571 572 void SubResource_Hit() { 573 host_->AssociateCompleteCache(MakeNewCache()); 574 575 mock_storage()->SimulateFindSubResource( 576 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false); 577 578 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 579 handler_.reset(host_->CreateRequestHandler(request_.get(), 580 RESOURCE_TYPE_SUB_RESOURCE)); 581 EXPECT_TRUE(handler_.get()); 582 job_ = handler_->MaybeLoadResource(request_.get(), 583 request_->context()->network_delegate()); 584 EXPECT_TRUE(job_.get()); 585 EXPECT_TRUE(job_->is_delivering_appcache_response()); 586 587 AppCacheURLRequestJob* fallback_job; 588 fallback_job = handler_->MaybeLoadFallbackForRedirect( 589 request_.get(), 590 request_->context()->network_delegate(), 591 GURL("http://blah/redirect")); 592 EXPECT_FALSE(fallback_job); 593 fallback_job = handler_->MaybeLoadFallbackForResponse( 594 request_.get(), request_->context()->network_delegate()); 595 EXPECT_FALSE(fallback_job); 596 597 TestFinished(); 598 } 599 600 // SubResource_RedirectFallback ----------------------------- 601 602 void SubResource_RedirectFallback() { 603 // Redirects to resources in the a different origin are subject to 604 // fallback namespaces. 605 host_->AssociateCompleteCache(MakeNewCache()); 606 607 mock_storage()->SimulateFindSubResource( 608 AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false); 609 610 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 611 handler_.reset(host_->CreateRequestHandler(request_.get(), 612 RESOURCE_TYPE_SUB_RESOURCE)); 613 EXPECT_TRUE(handler_.get()); 614 job_ = handler_->MaybeLoadResource(request_.get(), 615 request_->context()->network_delegate()); 616 EXPECT_FALSE(job_.get()); 617 618 job_ = handler_->MaybeLoadFallbackForRedirect( 619 request_.get(), 620 request_->context()->network_delegate(), 621 GURL("http://not_blah/redirect")); 622 EXPECT_TRUE(job_.get()); 623 EXPECT_TRUE(job_->is_delivering_appcache_response()); 624 625 AppCacheURLRequestJob* fallback_job; 626 fallback_job = handler_->MaybeLoadFallbackForResponse( 627 request_.get(), request_->context()->network_delegate()); 628 EXPECT_FALSE(fallback_job); 629 630 TestFinished(); 631 } 632 633 // SubResource_NoRedirectFallback ----------------------------- 634 635 void SubResource_NoRedirectFallback() { 636 // Redirects to resources in the same-origin are not subject to 637 // fallback namespaces. 638 host_->AssociateCompleteCache(MakeNewCache()); 639 640 mock_storage()->SimulateFindSubResource( 641 AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false); 642 643 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 644 handler_.reset(host_->CreateRequestHandler(request_.get(), 645 RESOURCE_TYPE_SUB_RESOURCE)); 646 EXPECT_TRUE(handler_.get()); 647 job_ = handler_->MaybeLoadResource(request_.get(), 648 request_->context()->network_delegate()); 649 EXPECT_FALSE(job_.get()); 650 651 AppCacheURLRequestJob* fallback_job; 652 fallback_job = handler_->MaybeLoadFallbackForRedirect( 653 request_.get(), 654 request_->context()->network_delegate(), 655 GURL("http://blah/redirect")); 656 EXPECT_FALSE(fallback_job); 657 658 SimulateResponseCode(200); 659 fallback_job = handler_->MaybeLoadFallbackForResponse( 660 request_.get(), request_->context()->network_delegate()); 661 EXPECT_FALSE(fallback_job); 662 663 TestFinished(); 664 } 665 666 // SubResource_Network ----------------------------- 667 668 void SubResource_Network() { 669 // A sub-resource load where the resource is in a network namespace, 670 // should result in the system using a 'real' job to do the network 671 // retrieval. 672 host_->AssociateCompleteCache(MakeNewCache()); 673 674 mock_storage()->SimulateFindSubResource( 675 AppCacheEntry(), AppCacheEntry(), true); 676 677 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 678 handler_.reset(host_->CreateRequestHandler(request_.get(), 679 RESOURCE_TYPE_SUB_RESOURCE)); 680 EXPECT_TRUE(handler_.get()); 681 job_ = handler_->MaybeLoadResource(request_.get(), 682 request_->context()->network_delegate()); 683 EXPECT_FALSE(job_.get()); 684 685 AppCacheURLRequestJob* fallback_job; 686 fallback_job = handler_->MaybeLoadFallbackForRedirect( 687 request_.get(), 688 request_->context()->network_delegate(), 689 GURL("http://blah/redirect")); 690 EXPECT_FALSE(fallback_job); 691 fallback_job = handler_->MaybeLoadFallbackForResponse( 692 request_.get(), request_->context()->network_delegate()); 693 EXPECT_FALSE(fallback_job); 694 695 TestFinished(); 696 } 697 698 // DestroyedHost ----------------------------- 699 700 void DestroyedHost() { 701 host_->AssociateCompleteCache(MakeNewCache()); 702 703 mock_storage()->SimulateFindSubResource( 704 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false); 705 706 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 707 handler_.reset(host_->CreateRequestHandler(request_.get(), 708 RESOURCE_TYPE_SUB_RESOURCE)); 709 EXPECT_TRUE(handler_.get()); 710 711 backend_impl_->UnregisterHost(1); 712 host_ = NULL; 713 714 EXPECT_FALSE(handler_->MaybeLoadResource( 715 request_.get(), request_->context()->network_delegate())); 716 EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( 717 request_.get(), 718 request_->context()->network_delegate(), 719 GURL("http://blah/redirect"))); 720 EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( 721 request_.get(), request_->context()->network_delegate())); 722 723 TestFinished(); 724 } 725 726 // DestroyedHostWithWaitingJob ----------------------------- 727 728 void DestroyedHostWithWaitingJob() { 729 // Precondition, the host is waiting on cache selection. 730 host_->pending_selected_cache_id_ = 1; 731 732 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 733 handler_.reset(host_->CreateRequestHandler(request_.get(), 734 RESOURCE_TYPE_SUB_RESOURCE)); 735 EXPECT_TRUE(handler_.get()); 736 737 job_ = handler_->MaybeLoadResource(request_.get(), 738 request_->context()->network_delegate()); 739 EXPECT_TRUE(job_.get()); 740 EXPECT_TRUE(job_->is_waiting()); 741 742 backend_impl_->UnregisterHost(1); 743 host_ = NULL; 744 EXPECT_TRUE(job_->has_been_killed()); 745 746 EXPECT_FALSE(handler_->MaybeLoadResource( 747 request_.get(), request_->context()->network_delegate())); 748 EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( 749 request_.get(), 750 request_->context()->network_delegate(), 751 GURL("http://blah/redirect"))); 752 EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( 753 request_.get(), request_->context()->network_delegate())); 754 755 TestFinished(); 756 } 757 758 // UnsupportedScheme ----------------------------- 759 760 void UnsupportedScheme() { 761 // Precondition, the host is waiting on cache selection. 762 host_->pending_selected_cache_id_ = 1; 763 764 request_.reset(new MockURLRequest(GURL("ftp://blah/"), &empty_context_)); 765 handler_.reset(host_->CreateRequestHandler(request_.get(), 766 RESOURCE_TYPE_SUB_RESOURCE)); 767 EXPECT_TRUE(handler_.get()); // we could redirect to http (conceivably) 768 769 EXPECT_FALSE(handler_->MaybeLoadResource( 770 request_.get(), request_->context()->network_delegate())); 771 EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( 772 request_.get(), 773 request_->context()->network_delegate(), 774 GURL("ftp://blah/redirect"))); 775 EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( 776 request_.get(), request_->context()->network_delegate())); 777 778 TestFinished(); 779 } 780 781 // CanceledRequest ----------------------------- 782 783 void CanceledRequest() { 784 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 785 handler_.reset(host_->CreateRequestHandler(request_.get(), 786 RESOURCE_TYPE_MAIN_FRAME)); 787 EXPECT_TRUE(handler_.get()); 788 789 job_ = handler_->MaybeLoadResource(request_.get(), 790 request_->context()->network_delegate()); 791 EXPECT_TRUE(job_.get()); 792 EXPECT_TRUE(job_->is_waiting()); 793 EXPECT_FALSE(job_->has_been_started()); 794 795 job_factory_->SetJob(job_); 796 request_->Start(); 797 EXPECT_TRUE(job_->has_been_started()); 798 799 request_->Cancel(); 800 EXPECT_TRUE(job_->has_been_killed()); 801 802 EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( 803 request_.get(), request_->context()->network_delegate())); 804 805 TestFinished(); 806 } 807 808 // WorkerRequest ----------------------------- 809 810 void WorkerRequest() { 811 EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( 812 RESOURCE_TYPE_MAIN_FRAME)); 813 EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( 814 RESOURCE_TYPE_SUB_FRAME)); 815 EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( 816 RESOURCE_TYPE_SHARED_WORKER)); 817 EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType( 818 RESOURCE_TYPE_WORKER)); 819 820 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 821 822 const int kParentHostId = host_->host_id(); 823 const int kWorkerHostId = 2; 824 const int kAbandonedWorkerHostId = 3; 825 const int kNonExsitingHostId = 700; 826 827 backend_impl_->RegisterHost(kWorkerHostId); 828 AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId); 829 worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId); 830 handler_.reset(worker_host->CreateRequestHandler( 831 request_.get(), RESOURCE_TYPE_SHARED_WORKER)); 832 EXPECT_TRUE(handler_.get()); 833 // Verify that the handler is associated with the parent host. 834 EXPECT_EQ(host_, handler_->host_); 835 836 // Create a new worker host, but associate it with a parent host that 837 // does not exists to simulate the host having been torn down. 838 backend_impl_->UnregisterHost(kWorkerHostId); 839 backend_impl_->RegisterHost(kAbandonedWorkerHostId); 840 worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId); 841 EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId)); 842 worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId); 843 handler_.reset(worker_host->CreateRequestHandler( 844 request_.get(), RESOURCE_TYPE_SHARED_WORKER)); 845 EXPECT_FALSE(handler_.get()); 846 847 TestFinished(); 848 } 849 850 // MainResource_Blocked -------------------------------------------------- 851 852 void MainResource_Blocked() { 853 PushNextTask( 854 base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked, 855 base::Unretained(this))); 856 857 request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); 858 handler_.reset(host_->CreateRequestHandler(request_.get(), 859 RESOURCE_TYPE_MAIN_FRAME)); 860 EXPECT_TRUE(handler_.get()); 861 862 mock_policy_->can_load_return_value_ = false; 863 mock_storage()->SimulateFindMainResource( 864 AppCacheEntry(AppCacheEntry::EXPLICIT, 1), 865 GURL(), AppCacheEntry(), 866 1, 2, GURL("http://blah/manifest/")); 867 868 job_ = handler_->MaybeLoadResource(request_.get(), 869 request_->context()->network_delegate()); 870 EXPECT_TRUE(job_.get()); 871 EXPECT_TRUE(job_->is_waiting()); 872 873 // We have to wait for completion of storage->FindResponseForMainRequest. 874 ScheduleNextTask(); 875 } 876 877 void Verify_MainResource_Blocked() { 878 EXPECT_FALSE(job_->is_waiting()); 879 EXPECT_FALSE(job_->is_delivering_appcache_response()); 880 881 EXPECT_EQ(0, handler_->found_cache_id_); 882 EXPECT_EQ(0, handler_->found_group_id_); 883 EXPECT_TRUE(handler_->found_manifest_url_.is_empty()); 884 EXPECT_TRUE(host_->preferred_manifest_url().is_empty()); 885 EXPECT_TRUE(host_->main_resource_blocked_); 886 EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/")); 887 888 TestFinished(); 889 } 890 891 // Test case helpers -------------------------------------------------- 892 893 AppCache* MakeNewCache() { 894 AppCache* cache = new AppCache( 895 mock_storage(), mock_storage()->NewCacheId()); 896 cache->set_complete(true); 897 AppCacheGroup* group = new AppCacheGroup( 898 mock_storage(), GURL("http://blah/manifest"), 899 mock_storage()->NewGroupId()); 900 group->AddCache(cache); 901 return cache; 902 } 903 904 MockAppCacheStorage* mock_storage() { 905 return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage()); 906 } 907 908 // Data members -------------------------------------------------- 909 910 scoped_ptr<base::WaitableEvent> test_finished_event_; 911 std::stack<base::Closure> task_stack_; 912 scoped_ptr<MockAppCacheService> mock_service_; 913 scoped_ptr<AppCacheBackendImpl> backend_impl_; 914 scoped_ptr<MockFrontend> mock_frontend_; 915 scoped_ptr<MockAppCachePolicy> mock_policy_; 916 AppCacheHost* host_; 917 net::URLRequestContext empty_context_; 918 scoped_ptr<MockURLRequestJobFactory> job_factory_; 919 scoped_ptr<MockURLRequest> request_; 920 scoped_ptr<AppCacheRequestHandler> handler_; 921 scoped_refptr<AppCacheURLRequestJob> job_; 922 923 static scoped_ptr<base::Thread> io_thread_; 924}; 925 926// static 927scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_; 928 929TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) { 930 RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss); 931} 932 933TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) { 934 RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit); 935} 936 937TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) { 938 RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback); 939} 940 941TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) { 942 RunTestOnIOThread( 943 &AppCacheRequestHandlerTest::MainResource_FallbackOverride); 944} 945 946TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) { 947 RunTestOnIOThread( 948 &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected); 949} 950 951TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) { 952 RunTestOnIOThread( 953 &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected); 954} 955 956TEST_F(AppCacheRequestHandlerTest, 957 SubResource_Miss_WithWaitForCacheSelection) { 958 RunTestOnIOThread( 959 &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection); 960} 961 962TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) { 963 RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit); 964} 965 966TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) { 967 RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback); 968} 969 970TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) { 971 RunTestOnIOThread( 972 &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback); 973} 974 975TEST_F(AppCacheRequestHandlerTest, SubResource_Network) { 976 RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network); 977} 978 979TEST_F(AppCacheRequestHandlerTest, DestroyedHost) { 980 RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost); 981} 982 983TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) { 984 RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob); 985} 986 987TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) { 988 RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme); 989} 990 991TEST_F(AppCacheRequestHandlerTest, CanceledRequest) { 992 RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest); 993} 994 995TEST_F(AppCacheRequestHandlerTest, WorkerRequest) { 996 RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest); 997} 998 999TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) { 1000 RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked); 1001} 1002 1003} // namespace content 1004