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 <utility> 7 8 #include "base/bind.h" 9 #include "base/bind_helpers.h" 10 #include "base/callback.h" 11 #include "base/compiler_specific.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/pickle.h" 15 #include "base/synchronization/waitable_event.h" 16 #include "base/threading/thread.h" 17 #include "content/browser/appcache/appcache_response.h" 18 #include "content/browser/appcache/appcache_url_request_job.h" 19 #include "content/browser/appcache/mock_appcache_service.h" 20 #include "net/base/io_buffer.h" 21 #include "net/base/net_errors.h" 22 #include "net/base/request_priority.h" 23 #include "net/http/http_response_headers.h" 24 #include "net/url_request/url_request.h" 25 #include "net/url_request/url_request_context.h" 26 #include "net/url_request/url_request_error_job.h" 27 #include "net/url_request/url_request_job_factory.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 #include "url/gurl.h" 30 31 using net::IOBuffer; 32 using net::WrappedIOBuffer; 33 34 namespace content { 35 36 namespace { 37 38 const char kHttpBasicHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 5\0\0"; 39 const char kHttpBasicBody[] = "Hello"; 40 41 const int kNumBlocks = 4; 42 const int kBlockSize = 1024; 43 44 class MockURLRequestJobFactory : public net::URLRequestJobFactory { 45 public: 46 MockURLRequestJobFactory() : job_(NULL) { 47 } 48 49 virtual ~MockURLRequestJobFactory() { 50 DCHECK(!job_); 51 } 52 53 void SetJob(net::URLRequestJob* job) { 54 job_ = job; 55 } 56 57 bool has_job() const { 58 return job_ != NULL; 59 } 60 61 // net::URLRequestJobFactory implementation. 62 virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler( 63 const std::string& scheme, 64 net::URLRequest* request, 65 net::NetworkDelegate* network_delegate) const OVERRIDE { 66 if (job_) { 67 net::URLRequestJob* temp = job_; 68 job_ = NULL; 69 return temp; 70 } else { 71 return new net::URLRequestErrorJob(request, 72 network_delegate, 73 net::ERR_INTERNET_DISCONNECTED); 74 } 75 } 76 77 virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE { 78 return scheme == "http"; 79 }; 80 81 virtual bool IsHandledURL(const GURL& url) const OVERRIDE { 82 return url.SchemeIs("http"); 83 } 84 85 virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE { 86 return false; 87 } 88 89 private: 90 mutable net::URLRequestJob* job_; 91 }; 92 93 class AppCacheURLRequestJobTest : public testing::Test { 94 public: 95 96 // Test Harness ------------------------------------------------------------- 97 // TODO(michaeln): share this test harness with AppCacheResponseTest 98 99 class MockStorageDelegate : public AppCacheStorage::Delegate { 100 public: 101 explicit MockStorageDelegate(AppCacheURLRequestJobTest* test) 102 : loaded_info_id_(0), test_(test) { 103 } 104 105 virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info, 106 int64 response_id) OVERRIDE { 107 loaded_info_ = info; 108 loaded_info_id_ = response_id; 109 test_->ScheduleNextTask(); 110 } 111 112 scoped_refptr<AppCacheResponseInfo> loaded_info_; 113 int64 loaded_info_id_; 114 AppCacheURLRequestJobTest* test_; 115 }; 116 117 class MockURLRequestDelegate : public net::URLRequest::Delegate { 118 public: 119 explicit MockURLRequestDelegate(AppCacheURLRequestJobTest* test) 120 : test_(test), 121 received_data_(new net::IOBuffer(kNumBlocks * kBlockSize)), 122 did_receive_headers_(false), amount_received_(0), 123 kill_after_amount_received_(0), kill_with_io_pending_(false) { 124 } 125 126 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { 127 amount_received_ = 0; 128 did_receive_headers_ = false; 129 if (request->status().is_success()) { 130 EXPECT_TRUE(request->response_headers()); 131 did_receive_headers_ = true; 132 received_info_ = request->response_info(); 133 ReadSome(request); 134 } else { 135 RequestComplete(); 136 } 137 } 138 139 virtual void OnReadCompleted(net::URLRequest* request, 140 int bytes_read) OVERRIDE { 141 if (bytes_read > 0) { 142 amount_received_ += bytes_read; 143 144 if (kill_after_amount_received_ && !kill_with_io_pending_) { 145 if (amount_received_ >= kill_after_amount_received_) { 146 request->Cancel(); 147 return; 148 } 149 } 150 151 ReadSome(request); 152 153 if (kill_after_amount_received_ && kill_with_io_pending_) { 154 if (amount_received_ >= kill_after_amount_received_) { 155 request->Cancel(); 156 return; 157 } 158 } 159 } else { 160 RequestComplete(); 161 } 162 } 163 164 void ReadSome(net::URLRequest* request) { 165 DCHECK(amount_received_ + kBlockSize <= kNumBlocks * kBlockSize); 166 scoped_refptr<IOBuffer> wrapped_buffer( 167 new net::WrappedIOBuffer(received_data_->data() + amount_received_)); 168 int bytes_read = 0; 169 EXPECT_FALSE( 170 request->Read(wrapped_buffer.get(), kBlockSize, &bytes_read)); 171 EXPECT_EQ(0, bytes_read); 172 } 173 174 void RequestComplete() { 175 test_->ScheduleNextTask(); 176 } 177 178 AppCacheURLRequestJobTest* test_; 179 net::HttpResponseInfo received_info_; 180 scoped_refptr<net::IOBuffer> received_data_; 181 bool did_receive_headers_; 182 int amount_received_; 183 int kill_after_amount_received_; 184 bool kill_with_io_pending_; 185 }; 186 187 // Helper callback to run a test on our io_thread. The io_thread is spun up 188 // once and reused for all tests. 189 template <class Method> 190 void MethodWrapper(Method method) { 191 SetUpTest(); 192 (this->*method)(); 193 } 194 195 static void SetUpTestCase() { 196 io_thread_.reset(new base::Thread("AppCacheURLRequestJobTest Thread")); 197 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); 198 io_thread_->StartWithOptions(options); 199 } 200 201 static void TearDownTestCase() { 202 io_thread_.reset(NULL); 203 } 204 205 AppCacheURLRequestJobTest() {} 206 207 template <class Method> 208 void RunTestOnIOThread(Method method) { 209 test_finished_event_ .reset(new base::WaitableEvent(false, false)); 210 io_thread_->message_loop()->PostTask( 211 FROM_HERE, base::Bind(&AppCacheURLRequestJobTest::MethodWrapper<Method>, 212 base::Unretained(this), method)); 213 test_finished_event_->Wait(); 214 } 215 216 void SetUpTest() { 217 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 218 DCHECK(task_stack_.empty()); 219 220 storage_delegate_.reset(new MockStorageDelegate(this)); 221 service_.reset(new MockAppCacheService()); 222 expected_read_result_ = 0; 223 expected_write_result_ = 0; 224 written_response_id_ = 0; 225 reader_deletion_count_down_ = 0; 226 writer_deletion_count_down_ = 0; 227 228 url_request_delegate_.reset(new MockURLRequestDelegate(this)); 229 job_factory_.reset(new MockURLRequestJobFactory()); 230 empty_context_.reset(new net::URLRequestContext()); 231 empty_context_->set_job_factory(job_factory_.get()); 232 } 233 234 void TearDownTest() { 235 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 236 request_.reset(); 237 238 while (!task_stack_.empty()) 239 task_stack_.pop(); 240 241 reader_.reset(); 242 read_buffer_ = NULL; 243 read_info_buffer_ = NULL; 244 writer_.reset(); 245 write_buffer_ = NULL; 246 write_info_buffer_ = NULL; 247 storage_delegate_.reset(); 248 service_.reset(); 249 250 DCHECK(!job_factory_->has_job()); 251 empty_context_.reset(); 252 job_factory_.reset(); 253 url_request_delegate_.reset(); 254 } 255 256 void TestFinished() { 257 // We unwind the stack prior to finishing up to let stack 258 // based objects get deleted. 259 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 260 base::MessageLoop::current()->PostTask( 261 FROM_HERE, 262 base::Bind(&AppCacheURLRequestJobTest::TestFinishedUnwound, 263 base::Unretained(this))); 264 } 265 266 void TestFinishedUnwound() { 267 TearDownTest(); 268 test_finished_event_->Signal(); 269 } 270 271 void PushNextTask(const base::Closure& task) { 272 task_stack_.push(std::pair<base::Closure, bool>(task, false)); 273 } 274 275 void PushNextTaskAsImmediate(const base::Closure& task) { 276 task_stack_.push(std::pair<base::Closure, bool>(task, true)); 277 } 278 279 void ScheduleNextTask() { 280 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 281 if (task_stack_.empty()) { 282 TestFinished(); 283 return; 284 } 285 base::Closure task =task_stack_.top().first; 286 bool immediate = task_stack_.top().second; 287 task_stack_.pop(); 288 if (immediate) 289 task.Run(); 290 else 291 base::MessageLoop::current()->PostTask(FROM_HERE, task); 292 } 293 294 // Wrappers to call AppCacheResponseReader/Writer Read and Write methods 295 296 void WriteBasicResponse() { 297 scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBasicBody)); 298 std::string raw_headers(kHttpBasicHeaders, arraysize(kHttpBasicHeaders)); 299 WriteResponse( 300 MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBasicBody)); 301 } 302 303 void WriteResponse(net::HttpResponseInfo* head, 304 IOBuffer* body, int body_len) { 305 DCHECK(body); 306 scoped_refptr<IOBuffer> body_ref(body); 307 PushNextTask(base::Bind(&AppCacheURLRequestJobTest::WriteResponseBody, 308 base::Unretained(this), body_ref, body_len)); 309 WriteResponseHead(head); 310 } 311 312 void WriteResponseHead(net::HttpResponseInfo* head) { 313 EXPECT_FALSE(writer_->IsWritePending()); 314 expected_write_result_ = GetHttpResponseInfoSize(head); 315 write_info_buffer_ = new HttpResponseInfoIOBuffer(head); 316 writer_->WriteInfo( 317 write_info_buffer_.get(), 318 base::Bind(&AppCacheURLRequestJobTest::OnWriteInfoComplete, 319 base::Unretained(this))); 320 } 321 322 void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) { 323 EXPECT_FALSE(writer_->IsWritePending()); 324 write_buffer_ = io_buffer; 325 expected_write_result_ = buf_len; 326 writer_->WriteData(write_buffer_.get(), 327 buf_len, 328 base::Bind(&AppCacheURLRequestJobTest::OnWriteComplete, 329 base::Unretained(this))); 330 } 331 332 void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) { 333 EXPECT_FALSE(reader_->IsReadPending()); 334 read_buffer_ = io_buffer; 335 expected_read_result_ = buf_len; 336 reader_->ReadData(read_buffer_.get(), 337 buf_len, 338 base::Bind(&AppCacheURLRequestJobTest::OnReadComplete, 339 base::Unretained(this))); 340 } 341 342 // AppCacheResponseReader / Writer completion callbacks 343 344 void OnWriteInfoComplete(int result) { 345 EXPECT_FALSE(writer_->IsWritePending()); 346 EXPECT_EQ(expected_write_result_, result); 347 ScheduleNextTask(); 348 } 349 350 void OnWriteComplete(int result) { 351 EXPECT_FALSE(writer_->IsWritePending()); 352 EXPECT_EQ(expected_write_result_, result); 353 ScheduleNextTask(); 354 } 355 356 void OnReadInfoComplete(int result) { 357 EXPECT_FALSE(reader_->IsReadPending()); 358 EXPECT_EQ(expected_read_result_, result); 359 ScheduleNextTask(); 360 } 361 362 void OnReadComplete(int result) { 363 EXPECT_FALSE(reader_->IsReadPending()); 364 EXPECT_EQ(expected_read_result_, result); 365 ScheduleNextTask(); 366 } 367 368 // Helpers to work with HttpResponseInfo objects 369 370 net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) { 371 net::HttpResponseInfo* info = new net::HttpResponseInfo; 372 info->request_time = base::Time::Now(); 373 info->response_time = base::Time::Now(); 374 info->was_cached = false; 375 info->headers = new net::HttpResponseHeaders(raw_headers); 376 return info; 377 } 378 379 int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) { 380 Pickle pickle; 381 return PickleHttpResonseInfo(&pickle, info); 382 } 383 384 bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1, 385 const net::HttpResponseInfo* info2) { 386 Pickle pickle1; 387 Pickle pickle2; 388 PickleHttpResonseInfo(&pickle1, info1); 389 PickleHttpResonseInfo(&pickle2, info2); 390 return (pickle1.size() == pickle2.size()) && 391 (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size())); 392 } 393 394 int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) { 395 const bool kSkipTransientHeaders = true; 396 const bool kTruncated = false; 397 info->Persist(pickle, kSkipTransientHeaders, kTruncated); 398 return pickle->size(); 399 } 400 401 // Helpers to fill and verify blocks of memory with a value 402 403 void FillData(char value, char* data, int data_len) { 404 memset(data, value, data_len); 405 } 406 407 bool CheckData(char value, const char* data, int data_len) { 408 for (int i = 0; i < data_len; ++i, ++data) { 409 if (*data != value) 410 return false; 411 } 412 return true; 413 } 414 415 // Individual Tests --------------------------------------------------------- 416 // Some of the individual tests involve multiple async steps. Each test 417 // is delineated with a section header. 418 419 // Basic ------------------------------------------------------------------- 420 void Basic() { 421 AppCacheStorage* storage = service_->storage(); 422 scoped_ptr<net::URLRequest> request(empty_context_->CreateRequest( 423 GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL, NULL)); 424 scoped_refptr<AppCacheURLRequestJob> job; 425 426 // Create an instance and see that it looks as expected. 427 428 job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false); 429 EXPECT_TRUE(job->is_waiting()); 430 EXPECT_FALSE(job->is_delivering_appcache_response()); 431 EXPECT_FALSE(job->is_delivering_network_response()); 432 EXPECT_FALSE(job->is_delivering_error_response()); 433 EXPECT_FALSE(job->has_been_started()); 434 EXPECT_FALSE(job->has_been_killed()); 435 EXPECT_EQ(GURL(), job->manifest_url()); 436 EXPECT_EQ(kAppCacheNoCacheId, job->cache_id()); 437 EXPECT_FALSE(job->entry().has_response_id()); 438 439 TestFinished(); 440 } 441 442 // DeliveryOrders ----------------------------------------------------- 443 void DeliveryOrders() { 444 AppCacheStorage* storage = service_->storage(); 445 scoped_ptr<net::URLRequest> request(empty_context_->CreateRequest( 446 GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL, NULL)); 447 scoped_refptr<AppCacheURLRequestJob> job; 448 449 // Create an instance, give it a delivery order and see that 450 // it looks as expected. 451 452 job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false); 453 job->DeliverErrorResponse(); 454 EXPECT_TRUE(job->is_delivering_error_response()); 455 EXPECT_FALSE(job->has_been_started()); 456 457 job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false); 458 job->DeliverNetworkResponse(); 459 EXPECT_TRUE(job->is_delivering_network_response()); 460 EXPECT_FALSE(job->has_been_started()); 461 462 job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false); 463 const GURL kManifestUrl("http://blah/"); 464 const int64 kCacheId(1); 465 const int64 kGroupId(1); 466 const AppCacheEntry kEntry(AppCacheEntry::EXPLICIT, 1); 467 job->DeliverAppCachedResponse(kManifestUrl, kCacheId, kGroupId, 468 kEntry, false); 469 EXPECT_FALSE(job->is_waiting()); 470 EXPECT_TRUE(job->is_delivering_appcache_response()); 471 EXPECT_FALSE(job->has_been_started()); 472 EXPECT_EQ(kManifestUrl, job->manifest_url()); 473 EXPECT_EQ(kCacheId, job->cache_id()); 474 EXPECT_EQ(kGroupId, job->group_id()); 475 EXPECT_EQ(kEntry.types(), job->entry().types()); 476 EXPECT_EQ(kEntry.response_id(), job->entry().response_id()); 477 478 TestFinished(); 479 } 480 481 // DeliverNetworkResponse -------------------------------------------------- 482 483 void DeliverNetworkResponse() { 484 // This test has async steps. 485 PushNextTask( 486 base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverNetworkResponse, 487 base::Unretained(this))); 488 489 AppCacheStorage* storage = service_->storage(); 490 request_ = empty_context_->CreateRequest(GURL("http://blah/"), 491 net::DEFAULT_PRIORITY, 492 url_request_delegate_.get(), 493 NULL); 494 495 // Setup to create an AppCacheURLRequestJob with orders to deliver 496 // a network response. 497 AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob( 498 request_.get(), NULL, storage, NULL, false); 499 job_factory_->SetJob(mock_job); 500 mock_job->DeliverNetworkResponse(); 501 EXPECT_TRUE(mock_job->is_delivering_network_response()); 502 EXPECT_FALSE(mock_job->has_been_started()); 503 504 // Start the request. 505 request_->Start(); 506 507 // The job should have been picked up. 508 EXPECT_FALSE(job_factory_->has_job()); 509 // Completion is async. 510 } 511 512 void VerifyDeliverNetworkResponse() { 513 EXPECT_EQ(request_->status().error(), 514 net::ERR_INTERNET_DISCONNECTED); 515 TestFinished(); 516 } 517 518 // DeliverErrorResponse -------------------------------------------------- 519 520 void DeliverErrorResponse() { 521 // This test has async steps. 522 PushNextTask( 523 base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverErrorResponse, 524 base::Unretained(this))); 525 526 AppCacheStorage* storage = service_->storage(); 527 request_ = empty_context_->CreateRequest(GURL("http://blah/"), 528 net::DEFAULT_PRIORITY, 529 url_request_delegate_.get(), 530 NULL); 531 532 // Setup to create an AppCacheURLRequestJob with orders to deliver 533 // a network response. 534 AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob( 535 request_.get(), NULL, storage, NULL, false); 536 job_factory_->SetJob(mock_job); 537 mock_job->DeliverErrorResponse(); 538 EXPECT_TRUE(mock_job->is_delivering_error_response()); 539 EXPECT_FALSE(mock_job->has_been_started()); 540 541 // Start the request. 542 request_->Start(); 543 544 // The job should have been picked up. 545 EXPECT_FALSE(job_factory_->has_job()); 546 // Completion is async. 547 } 548 549 void VerifyDeliverErrorResponse() { 550 EXPECT_EQ(request_->status().error(), net::ERR_FAILED); 551 TestFinished(); 552 } 553 554 // DeliverSmallAppCachedResponse -------------------------------------- 555 // "Small" being small enough to read completely in a single 556 // request->Read call. 557 558 void DeliverSmallAppCachedResponse() { 559 // This test has several async steps. 560 // 1. Write a small response to response storage. 561 // 2. Use net::URLRequest to retrieve it. 562 // 3. Verify we received what we expected to receive. 563 564 PushNextTask(base::Bind( 565 &AppCacheURLRequestJobTest::VerifyDeliverSmallAppCachedResponse, 566 base::Unretained(this))); 567 PushNextTask( 568 base::Bind(&AppCacheURLRequestJobTest::RequestAppCachedResource, 569 base::Unretained(this), false)); 570 571 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 572 written_response_id_ = writer_->response_id(); 573 WriteBasicResponse(); 574 // Continues async 575 } 576 577 void RequestAppCachedResource(bool start_after_delivery_orders) { 578 AppCacheStorage* storage = service_->storage(); 579 request_ = empty_context_->CreateRequest(GURL("http://blah/"), 580 net::DEFAULT_PRIORITY, 581 url_request_delegate_.get(), 582 NULL); 583 584 // Setup to create an AppCacheURLRequestJob with orders to deliver 585 // a network response. 586 scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob( 587 request_.get(), NULL, storage, NULL, false)); 588 589 if (start_after_delivery_orders) { 590 job->DeliverAppCachedResponse( 591 GURL(), 0, 111, 592 AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), 593 false); 594 EXPECT_TRUE(job->is_delivering_appcache_response()); 595 } 596 597 // Start the request. 598 EXPECT_FALSE(job->has_been_started()); 599 job_factory_->SetJob(job.get()); 600 request_->Start(); 601 EXPECT_FALSE(job_factory_->has_job()); 602 EXPECT_TRUE(job->has_been_started()); 603 604 if (!start_after_delivery_orders) { 605 job->DeliverAppCachedResponse( 606 GURL(), 0, 111, 607 AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), 608 false); 609 EXPECT_TRUE(job->is_delivering_appcache_response()); 610 } 611 612 // Completion is async. 613 } 614 615 void VerifyDeliverSmallAppCachedResponse() { 616 EXPECT_TRUE(request_->status().is_success()); 617 EXPECT_TRUE(CompareHttpResponseInfos( 618 write_info_buffer_->http_info.get(), 619 &url_request_delegate_->received_info_)); 620 EXPECT_EQ(5, url_request_delegate_->amount_received_); 621 EXPECT_EQ(0, memcmp(kHttpBasicBody, 622 url_request_delegate_->received_data_->data(), 623 strlen(kHttpBasicBody))); 624 TestFinished(); 625 } 626 627 // DeliverLargeAppCachedResponse -------------------------------------- 628 // "Large" enough to require multiple calls to request->Read to complete. 629 630 void DeliverLargeAppCachedResponse() { 631 // This test has several async steps. 632 // 1. Write a large response to response storage. 633 // 2. Use net::URLRequest to retrieve it. 634 // 3. Verify we received what we expected to receive. 635 636 PushNextTask(base::Bind( 637 &AppCacheURLRequestJobTest::VerifyDeliverLargeAppCachedResponse, 638 base::Unretained(this))); 639 PushNextTask(base::Bind( 640 &AppCacheURLRequestJobTest::RequestAppCachedResource, 641 base::Unretained(this), true)); 642 643 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 644 written_response_id_ = writer_->response_id(); 645 WriteLargeResponse(); 646 // Continues async 647 } 648 649 void WriteLargeResponse() { 650 // 3, 1k blocks 651 static const char kHttpHeaders[] = 652 "HTTP/1.0 200 OK\0Content-Length: 3072\0\0"; 653 scoped_refptr<IOBuffer> body(new IOBuffer(kBlockSize * 3)); 654 char* p = body->data(); 655 for (int i = 0; i < 3; ++i, p += kBlockSize) 656 FillData(i + 1, p, kBlockSize); 657 std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders)); 658 WriteResponse( 659 MakeHttpResponseInfo(raw_headers), body.get(), kBlockSize * 3); 660 } 661 662 void VerifyDeliverLargeAppCachedResponse() { 663 EXPECT_TRUE(request_->status().is_success()); 664 EXPECT_TRUE(CompareHttpResponseInfos( 665 write_info_buffer_->http_info.get(), 666 &url_request_delegate_->received_info_)); 667 EXPECT_EQ(3072, url_request_delegate_->amount_received_); 668 char* p = url_request_delegate_->received_data_->data(); 669 for (int i = 0; i < 3; ++i, p += kBlockSize) 670 EXPECT_TRUE(CheckData(i + 1, p, kBlockSize)); 671 TestFinished(); 672 } 673 674 // DeliverPartialResponse -------------------------------------- 675 676 void DeliverPartialResponse() { 677 // This test has several async steps. 678 // 1. Write a small response to response storage. 679 // 2. Use net::URLRequest to retrieve it a subset using a range request 680 // 3. Verify we received what we expected to receive. 681 PushNextTask(base::Bind( 682 &AppCacheURLRequestJobTest::VerifyDeliverPartialResponse, 683 base::Unretained(this))); 684 PushNextTask(base::Bind( 685 &AppCacheURLRequestJobTest::MakeRangeRequest, base::Unretained(this))); 686 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 687 written_response_id_ = writer_->response_id(); 688 WriteBasicResponse(); 689 // Continues async 690 } 691 692 void MakeRangeRequest() { 693 AppCacheStorage* storage = service_->storage(); 694 request_ = empty_context_->CreateRequest(GURL("http://blah/"), 695 net::DEFAULT_PRIORITY, 696 url_request_delegate_.get(), 697 NULL); 698 699 // Request a range, the 3 middle chars out of 'Hello' 700 net::HttpRequestHeaders extra_headers; 701 extra_headers.SetHeader("Range", "bytes= 1-3"); 702 request_->SetExtraRequestHeaders(extra_headers); 703 704 // Create job with orders to deliver an appcached entry. 705 scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob( 706 request_.get(), NULL, storage, NULL, false)); 707 job->DeliverAppCachedResponse( 708 GURL(), 0, 111, 709 AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), 710 false); 711 EXPECT_TRUE(job->is_delivering_appcache_response()); 712 713 // Start the request. 714 EXPECT_FALSE(job->has_been_started()); 715 job_factory_->SetJob(job.get()); 716 request_->Start(); 717 EXPECT_FALSE(job_factory_->has_job()); 718 EXPECT_TRUE(job->has_been_started()); 719 // Completion is async. 720 } 721 722 void VerifyDeliverPartialResponse() { 723 EXPECT_TRUE(request_->status().is_success()); 724 EXPECT_EQ(3, url_request_delegate_->amount_received_); 725 EXPECT_EQ(0, memcmp(kHttpBasicBody + 1, 726 url_request_delegate_->received_data_->data(), 727 3)); 728 net::HttpResponseHeaders* headers = 729 url_request_delegate_->received_info_.headers.get(); 730 EXPECT_EQ(206, headers->response_code()); 731 EXPECT_EQ(3, headers->GetContentLength()); 732 int64 range_start, range_end, object_size; 733 EXPECT_TRUE( 734 headers->GetContentRange(&range_start, &range_end, &object_size)); 735 EXPECT_EQ(1, range_start); 736 EXPECT_EQ(3, range_end); 737 EXPECT_EQ(5, object_size); 738 TestFinished(); 739 } 740 741 // CancelRequest -------------------------------------- 742 743 void CancelRequest() { 744 // This test has several async steps. 745 // 1. Write a large response to response storage. 746 // 2. Use net::URLRequest to retrieve it. 747 // 3. Cancel the request after data starts coming in. 748 749 PushNextTask(base::Bind( 750 &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this))); 751 PushNextTask(base::Bind( 752 &AppCacheURLRequestJobTest::RequestAppCachedResource, 753 base::Unretained(this), true)); 754 755 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 756 written_response_id_ = writer_->response_id(); 757 WriteLargeResponse(); 758 759 url_request_delegate_->kill_after_amount_received_ = kBlockSize; 760 url_request_delegate_->kill_with_io_pending_ = false; 761 // Continues async 762 } 763 764 void VerifyCancel() { 765 EXPECT_EQ(net::URLRequestStatus::CANCELED, 766 request_->status().status()); 767 TestFinished(); 768 } 769 770 // CancelRequestWithIOPending -------------------------------------- 771 772 void CancelRequestWithIOPending() { 773 // This test has several async steps. 774 // 1. Write a large response to response storage. 775 // 2. Use net::URLRequest to retrieve it. 776 // 3. Cancel the request after data starts coming in. 777 778 PushNextTask(base::Bind( 779 &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this))); 780 PushNextTask(base::Bind( 781 &AppCacheURLRequestJobTest::RequestAppCachedResource, 782 base::Unretained(this), true)); 783 784 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 785 written_response_id_ = writer_->response_id(); 786 WriteLargeResponse(); 787 788 url_request_delegate_->kill_after_amount_received_ = kBlockSize; 789 url_request_delegate_->kill_with_io_pending_ = true; 790 // Continues async 791 } 792 793 794 // Data members -------------------------------------------------------- 795 796 scoped_ptr<base::WaitableEvent> test_finished_event_; 797 scoped_ptr<MockStorageDelegate> storage_delegate_; 798 scoped_ptr<MockAppCacheService> service_; 799 std::stack<std::pair<base::Closure, bool> > task_stack_; 800 801 scoped_ptr<AppCacheResponseReader> reader_; 802 scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_; 803 scoped_refptr<IOBuffer> read_buffer_; 804 int expected_read_result_; 805 int reader_deletion_count_down_; 806 807 int64 written_response_id_; 808 scoped_ptr<AppCacheResponseWriter> writer_; 809 scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_; 810 scoped_refptr<IOBuffer> write_buffer_; 811 int expected_write_result_; 812 int writer_deletion_count_down_; 813 814 scoped_ptr<MockURLRequestJobFactory> job_factory_; 815 scoped_ptr<net::URLRequestContext> empty_context_; 816 scoped_ptr<net::URLRequest> request_; 817 scoped_ptr<MockURLRequestDelegate> url_request_delegate_; 818 819 static scoped_ptr<base::Thread> io_thread_; 820 }; 821 822 // static 823 scoped_ptr<base::Thread> AppCacheURLRequestJobTest::io_thread_; 824 825 TEST_F(AppCacheURLRequestJobTest, Basic) { 826 RunTestOnIOThread(&AppCacheURLRequestJobTest::Basic); 827 } 828 829 TEST_F(AppCacheURLRequestJobTest, DeliveryOrders) { 830 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliveryOrders); 831 } 832 833 TEST_F(AppCacheURLRequestJobTest, DeliverNetworkResponse) { 834 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverNetworkResponse); 835 } 836 837 TEST_F(AppCacheURLRequestJobTest, DeliverErrorResponse) { 838 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverErrorResponse); 839 } 840 841 TEST_F(AppCacheURLRequestJobTest, DeliverSmallAppCachedResponse) { 842 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverSmallAppCachedResponse); 843 } 844 845 TEST_F(AppCacheURLRequestJobTest, DeliverLargeAppCachedResponse) { 846 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverLargeAppCachedResponse); 847 } 848 849 TEST_F(AppCacheURLRequestJobTest, DeliverPartialResponse) { 850 RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverPartialResponse); 851 } 852 853 TEST_F(AppCacheURLRequestJobTest, CancelRequest) { 854 RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest); 855 } 856 857 TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) { 858 RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending); 859 } 860 861 } // namespace 862 863 } // namespace content 864