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