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