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