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