appcache_request_handler_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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  static void SetUpTestCase() {
168    io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread"));
169    base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
170    io_thread_->StartWithOptions(options);
171  }
172
173  static void TearDownTestCase() {
174    io_thread_.reset(NULL);
175  }
176
177  // Test harness --------------------------------------------------
178
179  AppCacheRequestHandlerTest() : host_(NULL) {}
180
181  template <class Method>
182  void RunTestOnIOThread(Method method) {
183    test_finished_event_ .reset(new base::WaitableEvent(false, false));
184    io_thread_->message_loop()->PostTask(
185        FROM_HERE,
186        base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>,
187                   base::Unretained(this), method));
188    test_finished_event_->Wait();
189  }
190
191  void SetUpTest() {
192    DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
193    mock_service_.reset(new MockAppCacheService);
194    mock_service_->set_request_context(&empty_context_);
195    mock_policy_.reset(new MockAppCachePolicy);
196    mock_service_->set_appcache_policy(mock_policy_.get());
197    mock_frontend_.reset(new MockFrontend);
198    backend_impl_.reset(new AppCacheBackendImpl);
199    backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(),
200                              kMockProcessId);
201    const int kHostId = 1;
202    backend_impl_->RegisterHost(kHostId);
203    host_ = backend_impl_->GetHost(kHostId);
204    job_factory_.reset(new MockURLRequestJobFactory());
205    empty_context_.set_job_factory(job_factory_.get());
206  }
207
208  void TearDownTest() {
209    DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
210    job_ = NULL;
211    handler_.reset();
212    request_.reset();
213    backend_impl_.reset();
214    mock_frontend_.reset();
215    mock_service_.reset();
216    mock_policy_.reset();
217    job_factory_.reset();
218    host_ = NULL;
219  }
220
221  void TestFinished() {
222    // We unwind the stack prior to finishing up to let stack
223    // based objects get deleted.
224    DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
225    base::MessageLoop::current()->PostTask(
226        FROM_HERE,
227        base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound,
228                   base::Unretained(this)));
229  }
230
231  void TestFinishedUnwound() {
232    TearDownTest();
233    test_finished_event_->Signal();
234  }
235
236  void PushNextTask(const base::Closure& task) {
237    task_stack_.push(task);
238  }
239
240  void ScheduleNextTask() {
241    DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
242    if (task_stack_.empty()) {
243      TestFinished();
244      return;
245    }
246    base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
247    task_stack_.pop();
248  }
249
250  // MainResource_Miss --------------------------------------------------
251
252  void MainResource_Miss() {
253    PushNextTask(
254        base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss,
255                   base::Unretained(this)));
256
257    request_ = empty_context_.CreateRequest(
258        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
259    handler_.reset(host_->CreateRequestHandler(request_.get(),
260                                               RESOURCE_TYPE_MAIN_FRAME));
261    EXPECT_TRUE(handler_.get());
262
263    job_ = handler_->MaybeLoadResource(request_.get(),
264                                       request_->context()->network_delegate());
265    EXPECT_TRUE(job_.get());
266    EXPECT_TRUE(job_->is_waiting());
267
268    // We have to wait for completion of storage->FindResponseForMainRequest.
269    ScheduleNextTask();
270  }
271
272  void Verify_MainResource_Miss() {
273    EXPECT_FALSE(job_->is_waiting());
274    EXPECT_TRUE(job_->is_delivering_network_response());
275
276    int64 cache_id = kAppCacheNoCacheId;
277    GURL manifest_url;
278    handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
279    EXPECT_EQ(kAppCacheNoCacheId, cache_id);
280    EXPECT_EQ(GURL(), manifest_url);
281    EXPECT_EQ(0, handler_->found_group_id_);
282
283    AppCacheURLRequestJob* fallback_job;
284    fallback_job = handler_->MaybeLoadFallbackForRedirect(
285        request_.get(),
286        request_->context()->network_delegate(),
287        GURL("http://blah/redirect"));
288    EXPECT_FALSE(fallback_job);
289    fallback_job = handler_->MaybeLoadFallbackForResponse(
290        request_.get(), request_->context()->network_delegate());
291    EXPECT_FALSE(fallback_job);
292
293    EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
294
295    TestFinished();
296  }
297
298  // MainResource_Hit --------------------------------------------------
299
300  void MainResource_Hit() {
301    PushNextTask(
302        base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit,
303                   base::Unretained(this)));
304
305    request_ = empty_context_.CreateRequest(
306        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
307    handler_.reset(host_->CreateRequestHandler(request_.get(),
308                                               RESOURCE_TYPE_MAIN_FRAME));
309    EXPECT_TRUE(handler_.get());
310
311    mock_storage()->SimulateFindMainResource(
312        AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
313        GURL(), AppCacheEntry(),
314        1, 2, GURL("http://blah/manifest/"));
315
316    job_ = handler_->MaybeLoadResource(request_.get(),
317                                       request_->context()->network_delegate());
318    EXPECT_TRUE(job_.get());
319    EXPECT_TRUE(job_->is_waiting());
320
321    // We have to wait for completion of storage->FindResponseForMainRequest.
322    ScheduleNextTask();
323  }
324
325  void Verify_MainResource_Hit() {
326    EXPECT_FALSE(job_->is_waiting());
327    EXPECT_TRUE(job_->is_delivering_appcache_response());
328
329    int64 cache_id = kAppCacheNoCacheId;
330    GURL manifest_url;
331    handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
332    EXPECT_EQ(1, cache_id);
333    EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
334    EXPECT_EQ(2, handler_->found_group_id_);
335
336    AppCacheURLRequestJob* fallback_job;
337    fallback_job = handler_->MaybeLoadFallbackForResponse(
338        request_.get(), request_->context()->network_delegate());
339    EXPECT_FALSE(fallback_job);
340
341    EXPECT_EQ(GURL("http://blah/manifest/"),
342              host_->preferred_manifest_url());
343
344    TestFinished();
345  }
346
347  // MainResource_Fallback --------------------------------------------------
348
349  void MainResource_Fallback() {
350    PushNextTask(
351        base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback,
352                   base::Unretained(this)));
353
354    request_ = empty_context_.CreateRequest(
355        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
356    handler_.reset(host_->CreateRequestHandler(request_.get(),
357                                               RESOURCE_TYPE_MAIN_FRAME));
358    EXPECT_TRUE(handler_.get());
359
360    mock_storage()->SimulateFindMainResource(
361        AppCacheEntry(),
362        GURL("http://blah/fallbackurl"),
363        AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
364        1, 2, GURL("http://blah/manifest/"));
365
366    job_ = handler_->MaybeLoadResource(request_.get(),
367                                       request_->context()->network_delegate());
368    EXPECT_TRUE(job_.get());
369    EXPECT_TRUE(job_->is_waiting());
370
371    // We have to wait for completion of storage->FindResponseForMainRequest.
372    ScheduleNextTask();
373  }
374
375  void SimulateResponseCode(int response_code) {
376    job_factory_->SetJob(
377        new MockURLRequestJob(
378            request_.get(),
379            request_->context()->network_delegate(),
380            response_code));
381    request_->Start();
382    // All our simulation needs  to satisfy are the following two DCHECKs
383    DCHECK(request_->status().is_success());
384    DCHECK_EQ(response_code, request_->GetResponseCode());
385  }
386
387  void SimulateResponseInfo(const net::HttpResponseInfo& info) {
388    job_factory_->SetJob(
389        new MockURLRequestJob(
390            request_.get(),
391            request_->context()->network_delegate(), info));
392    request_->Start();
393  }
394
395  void Verify_MainResource_Fallback() {
396    EXPECT_FALSE(job_->is_waiting());
397    EXPECT_TRUE(job_->is_delivering_network_response());
398
399    // When the request is restarted, the existing job is dropped so a
400    // real network job gets created. We expect NULL here which will cause
401    // the net library to create a real job.
402    job_ = handler_->MaybeLoadResource(request_.get(),
403                                       request_->context()->network_delegate());
404    EXPECT_FALSE(job_.get());
405
406    // Simulate an http error of the real network job.
407    SimulateResponseCode(500);
408
409    job_ = handler_->MaybeLoadFallbackForResponse(
410        request_.get(), request_->context()->network_delegate());
411    EXPECT_TRUE(job_.get());
412    EXPECT_TRUE(job_->is_delivering_appcache_response());
413
414    int64 cache_id = kAppCacheNoCacheId;
415    GURL manifest_url;
416    handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
417    EXPECT_EQ(1, cache_id);
418    EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
419    EXPECT_TRUE(host_->main_resource_was_namespace_entry_);
420    EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_);
421
422    EXPECT_EQ(GURL("http://blah/manifest/"),
423              host_->preferred_manifest_url());
424
425    TestFinished();
426  }
427
428  // MainResource_FallbackOverride --------------------------------------------
429
430  void MainResource_FallbackOverride() {
431    PushNextTask(base::Bind(
432        &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride,
433        base::Unretained(this)));
434
435    request_ = empty_context_.CreateRequest(
436        GURL("http://blah/fallback-override"), net::DEFAULT_PRIORITY,
437        &delegate_, NULL);
438    handler_.reset(host_->CreateRequestHandler(request_.get(),
439                                               RESOURCE_TYPE_MAIN_FRAME));
440    EXPECT_TRUE(handler_.get());
441
442    mock_storage()->SimulateFindMainResource(
443        AppCacheEntry(),
444        GURL("http://blah/fallbackurl"),
445        AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
446        1, 2, GURL("http://blah/manifest/"));
447
448    job_ = handler_->MaybeLoadResource(request_.get(),
449                                       request_->context()->network_delegate());
450    EXPECT_TRUE(job_.get());
451    EXPECT_TRUE(job_->is_waiting());
452
453    // We have to wait for completion of storage->FindResponseForMainRequest.
454    ScheduleNextTask();
455  }
456
457  void Verify_MainResource_FallbackOverride() {
458    EXPECT_FALSE(job_->is_waiting());
459    EXPECT_TRUE(job_->is_delivering_network_response());
460
461    // When the request is restarted, the existing job is dropped so a
462    // real network job gets created. We expect NULL here which will cause
463    // the net library to create a real job.
464    job_ = handler_->MaybeLoadResource(request_.get(),
465                                       request_->context()->network_delegate());
466    EXPECT_FALSE(job_.get());
467
468    // Simulate an http error of the real network job, but with custom
469    // headers that override the fallback behavior.
470    const char kOverrideHeaders[] =
471        "HTTP/1.1 404 BOO HOO\0"
472        "x-chromium-appcache-fallback-override: disallow-fallback\0"
473        "\0";
474    net::HttpResponseInfo info;
475    info.headers = new net::HttpResponseHeaders(
476        std::string(kOverrideHeaders, arraysize(kOverrideHeaders)));
477    SimulateResponseInfo(info);
478
479    job_ = handler_->MaybeLoadFallbackForResponse(
480        request_.get(), request_->context()->network_delegate());
481    EXPECT_FALSE(job_.get());
482
483    TestFinished();
484  }
485
486  // SubResource_Miss_WithNoCacheSelected ----------------------------------
487
488  void SubResource_Miss_WithNoCacheSelected() {
489    request_ = empty_context_.CreateRequest(
490        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
491    handler_.reset(host_->CreateRequestHandler(request_.get(),
492                                               RESOURCE_TYPE_SUB_RESOURCE));
493
494    // We avoid creating handler when possible, sub-resource requests are not
495    // subject to retrieval from an appcache when there's no associated cache.
496    EXPECT_FALSE(handler_.get());
497
498    TestFinished();
499  }
500
501  // SubResource_Miss_WithCacheSelected ----------------------------------
502
503  void SubResource_Miss_WithCacheSelected() {
504    // A sub-resource load where the resource is not in an appcache, or
505    // in a network or fallback namespace, should result in a failed request.
506    host_->AssociateCompleteCache(MakeNewCache());
507
508    request_ = empty_context_.CreateRequest(
509        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
510    handler_.reset(host_->CreateRequestHandler(request_.get(),
511                                               RESOURCE_TYPE_SUB_RESOURCE));
512    EXPECT_TRUE(handler_.get());
513
514    job_ = handler_->MaybeLoadResource(request_.get(),
515                                       request_->context()->network_delegate());
516    EXPECT_TRUE(job_.get());
517    EXPECT_TRUE(job_->is_delivering_error_response());
518
519    AppCacheURLRequestJob* fallback_job;
520    fallback_job = handler_->MaybeLoadFallbackForRedirect(
521        request_.get(),
522        request_->context()->network_delegate(),
523        GURL("http://blah/redirect"));
524    EXPECT_FALSE(fallback_job);
525    fallback_job = handler_->MaybeLoadFallbackForResponse(
526        request_.get(), request_->context()->network_delegate());
527    EXPECT_FALSE(fallback_job);
528
529    TestFinished();
530  }
531
532  // SubResource_Miss_WithWaitForCacheSelection -----------------------------
533
534  void SubResource_Miss_WithWaitForCacheSelection() {
535    // Precondition, the host is waiting on cache selection.
536    scoped_refptr<AppCache> cache(MakeNewCache());
537    host_->pending_selected_cache_id_ = cache->cache_id();
538    host_->set_preferred_manifest_url(cache->owning_group()->manifest_url());
539
540    request_ = empty_context_.CreateRequest(
541        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
542    handler_.reset(host_->CreateRequestHandler(request_.get(),
543                                               RESOURCE_TYPE_SUB_RESOURCE));
544    EXPECT_TRUE(handler_.get());
545    job_ = handler_->MaybeLoadResource(request_.get(),
546                                       request_->context()->network_delegate());
547    EXPECT_TRUE(job_.get());
548    EXPECT_TRUE(job_->is_waiting());
549
550    host_->FinishCacheSelection(cache.get(), NULL);
551    EXPECT_FALSE(job_->is_waiting());
552    EXPECT_TRUE(job_->is_delivering_error_response());
553
554    AppCacheURLRequestJob* fallback_job;
555    fallback_job = handler_->MaybeLoadFallbackForRedirect(
556        request_.get(),
557        request_->context()->network_delegate(),
558        GURL("http://blah/redirect"));
559    EXPECT_FALSE(fallback_job);
560    fallback_job = handler_->MaybeLoadFallbackForResponse(
561        request_.get(), request_->context()->network_delegate());
562    EXPECT_FALSE(fallback_job);
563
564    TestFinished();
565  }
566
567  // SubResource_Hit -----------------------------
568
569  void SubResource_Hit() {
570    host_->AssociateCompleteCache(MakeNewCache());
571
572    mock_storage()->SimulateFindSubResource(
573        AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
574
575    request_ = empty_context_.CreateRequest(
576        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
577    handler_.reset(host_->CreateRequestHandler(request_.get(),
578                                               RESOURCE_TYPE_SUB_RESOURCE));
579    EXPECT_TRUE(handler_.get());
580    job_ = handler_->MaybeLoadResource(request_.get(),
581                                       request_->context()->network_delegate());
582    EXPECT_TRUE(job_.get());
583    EXPECT_TRUE(job_->is_delivering_appcache_response());
584
585    AppCacheURLRequestJob* fallback_job;
586    fallback_job = handler_->MaybeLoadFallbackForRedirect(
587        request_.get(),
588        request_->context()->network_delegate(),
589        GURL("http://blah/redirect"));
590    EXPECT_FALSE(fallback_job);
591    fallback_job = handler_->MaybeLoadFallbackForResponse(
592        request_.get(), request_->context()->network_delegate());
593    EXPECT_FALSE(fallback_job);
594
595    TestFinished();
596  }
597
598  // SubResource_RedirectFallback -----------------------------
599
600  void SubResource_RedirectFallback() {
601    // Redirects to resources in the a different origin are subject to
602    // fallback namespaces.
603    host_->AssociateCompleteCache(MakeNewCache());
604
605    mock_storage()->SimulateFindSubResource(
606        AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
607
608    request_ = empty_context_.CreateRequest(
609        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
610    handler_.reset(host_->CreateRequestHandler(request_.get(),
611                                               RESOURCE_TYPE_SUB_RESOURCE));
612    EXPECT_TRUE(handler_.get());
613    job_ = handler_->MaybeLoadResource(request_.get(),
614                                       request_->context()->network_delegate());
615    EXPECT_FALSE(job_.get());
616
617    job_ = handler_->MaybeLoadFallbackForRedirect(
618        request_.get(),
619        request_->context()->network_delegate(),
620        GURL("http://not_blah/redirect"));
621    EXPECT_TRUE(job_.get());
622    EXPECT_TRUE(job_->is_delivering_appcache_response());
623
624    AppCacheURLRequestJob* fallback_job;
625    fallback_job = handler_->MaybeLoadFallbackForResponse(
626        request_.get(), request_->context()->network_delegate());
627    EXPECT_FALSE(fallback_job);
628
629    TestFinished();
630  }
631
632  // SubResource_NoRedirectFallback -----------------------------
633
634  void SubResource_NoRedirectFallback() {
635    // Redirects to resources in the same-origin are not subject to
636    // fallback namespaces.
637    host_->AssociateCompleteCache(MakeNewCache());
638
639    mock_storage()->SimulateFindSubResource(
640        AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
641
642    request_ = empty_context_.CreateRequest(
643        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
644    handler_.reset(host_->CreateRequestHandler(request_.get(),
645                                               RESOURCE_TYPE_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_ = empty_context_.CreateRequest(
678        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
679    handler_.reset(host_->CreateRequestHandler(request_.get(),
680                                               RESOURCE_TYPE_SUB_RESOURCE));
681    EXPECT_TRUE(handler_.get());
682    job_ = handler_->MaybeLoadResource(request_.get(),
683                                       request_->context()->network_delegate());
684    EXPECT_FALSE(job_.get());
685
686    AppCacheURLRequestJob* fallback_job;
687    fallback_job = handler_->MaybeLoadFallbackForRedirect(
688        request_.get(),
689        request_->context()->network_delegate(),
690        GURL("http://blah/redirect"));
691    EXPECT_FALSE(fallback_job);
692    fallback_job = handler_->MaybeLoadFallbackForResponse(
693        request_.get(), request_->context()->network_delegate());
694    EXPECT_FALSE(fallback_job);
695
696    TestFinished();
697  }
698
699  // DestroyedHost -----------------------------
700
701  void DestroyedHost() {
702    host_->AssociateCompleteCache(MakeNewCache());
703
704    mock_storage()->SimulateFindSubResource(
705        AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
706
707    request_ = empty_context_.CreateRequest(
708        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
709    handler_.reset(host_->CreateRequestHandler(request_.get(),
710                                               RESOURCE_TYPE_SUB_RESOURCE));
711    EXPECT_TRUE(handler_.get());
712
713    backend_impl_->UnregisterHost(1);
714    host_ = NULL;
715
716    EXPECT_FALSE(handler_->MaybeLoadResource(
717        request_.get(), request_->context()->network_delegate()));
718    EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
719        request_.get(),
720        request_->context()->network_delegate(),
721        GURL("http://blah/redirect")));
722    EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
723        request_.get(), request_->context()->network_delegate()));
724
725    TestFinished();
726  }
727
728  // DestroyedHostWithWaitingJob -----------------------------
729
730  void DestroyedHostWithWaitingJob() {
731    // Precondition, the host is waiting on cache selection.
732    host_->pending_selected_cache_id_ = 1;
733
734    request_ = empty_context_.CreateRequest(
735        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
736    handler_.reset(host_->CreateRequestHandler(request_.get(),
737                                               RESOURCE_TYPE_SUB_RESOURCE));
738    EXPECT_TRUE(handler_.get());
739
740    job_ = handler_->MaybeLoadResource(request_.get(),
741                                       request_->context()->network_delegate());
742    EXPECT_TRUE(job_.get());
743    EXPECT_TRUE(job_->is_waiting());
744
745    backend_impl_->UnregisterHost(1);
746    host_ = NULL;
747    EXPECT_TRUE(job_->has_been_killed());
748
749    EXPECT_FALSE(handler_->MaybeLoadResource(
750        request_.get(), request_->context()->network_delegate()));
751    EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
752        request_.get(),
753        request_->context()->network_delegate(),
754        GURL("http://blah/redirect")));
755    EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
756        request_.get(), request_->context()->network_delegate()));
757
758    TestFinished();
759  }
760
761  // UnsupportedScheme -----------------------------
762
763  void UnsupportedScheme() {
764    // Precondition, the host is waiting on cache selection.
765    host_->pending_selected_cache_id_ = 1;
766
767    request_ = empty_context_.CreateRequest(
768        GURL("ftp://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
769    handler_.reset(host_->CreateRequestHandler(request_.get(),
770                                               RESOURCE_TYPE_SUB_RESOURCE));
771    EXPECT_TRUE(handler_.get());  // we could redirect to http (conceivably)
772
773    EXPECT_FALSE(handler_->MaybeLoadResource(
774        request_.get(), request_->context()->network_delegate()));
775    EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
776        request_.get(),
777        request_->context()->network_delegate(),
778        GURL("ftp://blah/redirect")));
779    EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
780        request_.get(), request_->context()->network_delegate()));
781
782    TestFinished();
783  }
784
785  // CanceledRequest -----------------------------
786
787  void CanceledRequest() {
788    request_ = empty_context_.CreateRequest(
789        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
790    handler_.reset(host_->CreateRequestHandler(request_.get(),
791                                               RESOURCE_TYPE_MAIN_FRAME));
792    EXPECT_TRUE(handler_.get());
793
794    job_ = handler_->MaybeLoadResource(request_.get(),
795                                       request_->context()->network_delegate());
796    EXPECT_TRUE(job_.get());
797    EXPECT_TRUE(job_->is_waiting());
798    EXPECT_FALSE(job_->has_been_started());
799
800    job_factory_->SetJob(job_);
801    request_->Start();
802    EXPECT_TRUE(job_->has_been_started());
803
804    request_->Cancel();
805    EXPECT_TRUE(job_->has_been_killed());
806
807    EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
808        request_.get(), request_->context()->network_delegate()));
809
810    TestFinished();
811  }
812
813  // WorkerRequest -----------------------------
814
815  void WorkerRequest() {
816    EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
817        RESOURCE_TYPE_MAIN_FRAME));
818    EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
819        RESOURCE_TYPE_SUB_FRAME));
820    EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
821        RESOURCE_TYPE_SHARED_WORKER));
822    EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType(
823        RESOURCE_TYPE_WORKER));
824
825    request_ = empty_context_.CreateRequest(
826        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
827
828    const int kParentHostId = host_->host_id();
829    const int kWorkerHostId = 2;
830    const int kAbandonedWorkerHostId = 3;
831    const int kNonExsitingHostId = 700;
832
833    backend_impl_->RegisterHost(kWorkerHostId);
834    AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId);
835    worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
836    handler_.reset(worker_host->CreateRequestHandler(
837        request_.get(), RESOURCE_TYPE_SHARED_WORKER));
838    EXPECT_TRUE(handler_.get());
839    // Verify that the handler is associated with the parent host.
840    EXPECT_EQ(host_, handler_->host_);
841
842    // Create a new worker host, but associate it with a parent host that
843    // does not exists to simulate the host having been torn down.
844    backend_impl_->UnregisterHost(kWorkerHostId);
845    backend_impl_->RegisterHost(kAbandonedWorkerHostId);
846    worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId);
847    EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId));
848    worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId);
849    handler_.reset(worker_host->CreateRequestHandler(
850        request_.get(), RESOURCE_TYPE_SHARED_WORKER));
851    EXPECT_FALSE(handler_.get());
852
853    TestFinished();
854  }
855
856  // MainResource_Blocked --------------------------------------------------
857
858  void MainResource_Blocked() {
859    PushNextTask(
860        base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked,
861                   base::Unretained(this)));
862
863    request_ = empty_context_.CreateRequest(
864        GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
865    handler_.reset(host_->CreateRequestHandler(request_.get(),
866                                               RESOURCE_TYPE_MAIN_FRAME));
867    EXPECT_TRUE(handler_.get());
868
869    mock_policy_->can_load_return_value_ = false;
870    mock_storage()->SimulateFindMainResource(
871        AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
872        GURL(), AppCacheEntry(),
873        1, 2, GURL("http://blah/manifest/"));
874
875    job_ = handler_->MaybeLoadResource(request_.get(),
876                                       request_->context()->network_delegate());
877    EXPECT_TRUE(job_.get());
878    EXPECT_TRUE(job_->is_waiting());
879
880    // We have to wait for completion of storage->FindResponseForMainRequest.
881    ScheduleNextTask();
882  }
883
884  void Verify_MainResource_Blocked() {
885    EXPECT_FALSE(job_->is_waiting());
886    EXPECT_FALSE(job_->is_delivering_appcache_response());
887
888    EXPECT_EQ(0, handler_->found_cache_id_);
889    EXPECT_EQ(0, handler_->found_group_id_);
890    EXPECT_TRUE(handler_->found_manifest_url_.is_empty());
891    EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
892    EXPECT_TRUE(host_->main_resource_blocked_);
893    EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/"));
894
895    TestFinished();
896  }
897
898  // Test case helpers --------------------------------------------------
899
900  AppCache* MakeNewCache() {
901    AppCache* cache = new AppCache(
902        mock_storage(), mock_storage()->NewCacheId());
903    cache->set_complete(true);
904    AppCacheGroup* group = new AppCacheGroup(
905        mock_storage(), GURL("http://blah/manifest"),
906        mock_storage()->NewGroupId());
907    group->AddCache(cache);
908    return cache;
909  }
910
911  MockAppCacheStorage* mock_storage() {
912    return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage());
913  }
914
915  // Data members --------------------------------------------------
916
917  scoped_ptr<base::WaitableEvent> test_finished_event_;
918  std::stack<base::Closure> task_stack_;
919  scoped_ptr<MockAppCacheService> mock_service_;
920  scoped_ptr<AppCacheBackendImpl> backend_impl_;
921  scoped_ptr<MockFrontend> mock_frontend_;
922  scoped_ptr<MockAppCachePolicy> mock_policy_;
923  AppCacheHost* host_;
924  net::URLRequestContext empty_context_;
925  scoped_ptr<MockURLRequestJobFactory> job_factory_;
926  MockURLRequestDelegate delegate_;
927  scoped_ptr<net::URLRequest> request_;
928  scoped_ptr<AppCacheRequestHandler> handler_;
929  scoped_refptr<AppCacheURLRequestJob> job_;
930
931  static scoped_ptr<base::Thread> io_thread_;
932};
933
934// static
935scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_;
936
937TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) {
938  RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss);
939}
940
941TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) {
942  RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit);
943}
944
945TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) {
946  RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback);
947}
948
949TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) {
950  RunTestOnIOThread(
951      &AppCacheRequestHandlerTest::MainResource_FallbackOverride);
952}
953
954TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) {
955  RunTestOnIOThread(
956      &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected);
957}
958
959TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) {
960  RunTestOnIOThread(
961      &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected);
962}
963
964TEST_F(AppCacheRequestHandlerTest,
965       SubResource_Miss_WithWaitForCacheSelection) {
966  RunTestOnIOThread(
967      &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection);
968}
969
970TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) {
971  RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit);
972}
973
974TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) {
975  RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback);
976}
977
978TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) {
979  RunTestOnIOThread(
980    &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback);
981}
982
983TEST_F(AppCacheRequestHandlerTest, SubResource_Network) {
984  RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network);
985}
986
987TEST_F(AppCacheRequestHandlerTest, DestroyedHost) {
988  RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost);
989}
990
991TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) {
992  RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob);
993}
994
995TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) {
996  RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme);
997}
998
999TEST_F(AppCacheRequestHandlerTest, CanceledRequest) {
1000  RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest);
1001}
1002
1003TEST_F(AppCacheRequestHandlerTest, WorkerRequest) {
1004  RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest);
1005}
1006
1007TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) {
1008  RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked);
1009}
1010
1011}  // namespace content
1012