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