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