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