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