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
31using net::IOBuffer;
32using net::WrappedIOBuffer;
33
34namespace content {
35
36namespace {
37
38const char kHttpBasicHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
39const char kHttpBasicBody[] = "Hello";
40
41const int kNumBlocks = 4;
42const int kBlockSize = 1024;
43
44class 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
93class 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
823scoped_ptr<base::Thread> AppCacheURLRequestJobTest::io_thread_;
824
825TEST_F(AppCacheURLRequestJobTest, Basic) {
826  RunTestOnIOThread(&AppCacheURLRequestJobTest::Basic);
827}
828
829TEST_F(AppCacheURLRequestJobTest, DeliveryOrders) {
830  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliveryOrders);
831}
832
833TEST_F(AppCacheURLRequestJobTest, DeliverNetworkResponse) {
834  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverNetworkResponse);
835}
836
837TEST_F(AppCacheURLRequestJobTest, DeliverErrorResponse) {
838  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverErrorResponse);
839}
840
841TEST_F(AppCacheURLRequestJobTest, DeliverSmallAppCachedResponse) {
842  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverSmallAppCachedResponse);
843}
844
845TEST_F(AppCacheURLRequestJobTest, DeliverLargeAppCachedResponse) {
846  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverLargeAppCachedResponse);
847}
848
849TEST_F(AppCacheURLRequestJobTest, DeliverPartialResponse) {
850  RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverPartialResponse);
851}
852
853TEST_F(AppCacheURLRequestJobTest, CancelRequest) {
854  RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest);
855}
856
857TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) {
858  RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending);
859}
860
861}  // namespace
862
863}  // namespace content
864