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