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