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 "base/basictypes.h"
6#include "base/callback.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "content/browser/fileapi/chrome_blob_storage_context.h"
11#include "content/browser/fileapi/mock_url_request_delegate.h"
12#include "content/browser/service_worker/embedded_worker_registry.h"
13#include "content/browser/service_worker/embedded_worker_test_helper.h"
14#include "content/browser/service_worker/service_worker_context_core.h"
15#include "content/browser/service_worker/service_worker_provider_host.h"
16#include "content/browser/service_worker/service_worker_registration.h"
17#include "content/browser/service_worker/service_worker_test_utils.h"
18#include "content/browser/service_worker/service_worker_url_request_job.h"
19#include "content/browser/service_worker/service_worker_version.h"
20#include "content/common/resource_request_body.h"
21#include "content/common/service_worker/service_worker_messages.h"
22#include "content/public/browser/blob_handle.h"
23#include "content/public/test/test_browser_context.h"
24#include "content/public/test/test_browser_thread_bundle.h"
25#include "net/base/io_buffer.h"
26#include "net/http/http_request_headers.h"
27#include "net/http/http_response_headers.h"
28#include "net/url_request/url_request.h"
29#include "net/url_request/url_request_context.h"
30#include "net/url_request/url_request_job_factory_impl.h"
31#include "storage/browser/blob/blob_storage_context.h"
32#include "storage/browser/blob/blob_url_request_job.h"
33#include "storage/browser/blob/blob_url_request_job_factory.h"
34#include "storage/common/blob/blob_data.h"
35#include "testing/gtest/include/gtest/gtest.h"
36
37namespace content {
38
39class ServiceWorkerURLRequestJobTest;
40
41namespace {
42
43const int kProcessID = 1;
44const int kProviderID = 100;
45const char kTestData[] = "Here is sample text for the blob.";
46
47class MockHttpProtocolHandler
48    : public net::URLRequestJobFactory::ProtocolHandler {
49 public:
50  MockHttpProtocolHandler(
51      base::WeakPtr<ServiceWorkerProviderHost> provider_host,
52      base::WeakPtr<storage::BlobStorageContext> blob_storage_context)
53      : provider_host_(provider_host),
54        blob_storage_context_(blob_storage_context) {}
55  virtual ~MockHttpProtocolHandler() {}
56
57  virtual net::URLRequestJob* MaybeCreateJob(
58      net::URLRequest* request,
59      net::NetworkDelegate* network_delegate) const OVERRIDE {
60    ServiceWorkerURLRequestJob* job =
61        new ServiceWorkerURLRequestJob(request,
62                                       network_delegate,
63                                       provider_host_,
64                                       blob_storage_context_,
65                                       scoped_refptr<ResourceRequestBody>());
66    job->ForwardToServiceWorker();
67    return job;
68  }
69
70 private:
71  base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
72  base::WeakPtr<storage::BlobStorageContext> blob_storage_context_;
73};
74
75// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
76// the memory.
77storage::BlobProtocolHandler* CreateMockBlobProtocolHandler(
78    storage::BlobStorageContext* blob_storage_context) {
79  // The FileSystemContext and MessageLoopProxy are not actually used but a
80  // MessageLoopProxy is needed to avoid a DCHECK in BlobURLRequestJob ctor.
81  return new storage::BlobProtocolHandler(
82      blob_storage_context, NULL, base::MessageLoopProxy::current().get());
83}
84
85}  // namespace
86
87class ServiceWorkerURLRequestJobTest : public testing::Test {
88 protected:
89  ServiceWorkerURLRequestJobTest()
90      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
91        blob_data_(new storage::BlobData("blob-id:myblob")) {}
92  virtual ~ServiceWorkerURLRequestJobTest() {}
93
94  virtual void SetUp() OVERRIDE {
95    browser_context_.reset(new TestBrowserContext);
96    SetUpWithHelper(new EmbeddedWorkerTestHelper(kProcessID));
97  }
98
99  void SetUpWithHelper(EmbeddedWorkerTestHelper* helper) {
100    helper_.reset(helper);
101
102    registration_ = new ServiceWorkerRegistration(
103        GURL("http://example.com/"),
104        1L,
105        helper_->context()->AsWeakPtr());
106    version_ = new ServiceWorkerVersion(
107        registration_.get(),
108        GURL("http://example.com/service_worker.js"),
109        1L,
110        helper_->context()->AsWeakPtr());
111
112    scoped_ptr<ServiceWorkerProviderHost> provider_host(
113        new ServiceWorkerProviderHost(
114            kProcessID, kProviderID, helper_->context()->AsWeakPtr(), NULL));
115    provider_host->AssociateRegistration(registration_.get());
116    registration_->SetActiveVersion(version_.get());
117
118    ChromeBlobStorageContext* chrome_blob_storage_context =
119        ChromeBlobStorageContext::GetFor(browser_context_.get());
120    // Wait for chrome_blob_storage_context to finish initializing.
121    base::RunLoop().RunUntilIdle();
122    storage::BlobStorageContext* blob_storage_context =
123        chrome_blob_storage_context->context();
124
125    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
126    url_request_job_factory_->SetProtocolHandler(
127        "http",
128        new MockHttpProtocolHandler(provider_host->AsWeakPtr(),
129                                    blob_storage_context->AsWeakPtr()));
130    url_request_job_factory_->SetProtocolHandler(
131        "blob", CreateMockBlobProtocolHandler(blob_storage_context));
132    url_request_context_.set_job_factory(url_request_job_factory_.get());
133
134    helper_->context()->AddProviderHost(provider_host.Pass());
135  }
136
137  virtual void TearDown() OVERRIDE {
138    version_ = NULL;
139    registration_ = NULL;
140    helper_.reset();
141  }
142
143  void TestRequest(int expected_status_code,
144                   const std::string& expected_status_text,
145                   const std::string& expected_response) {
146    request_ = url_request_context_.CreateRequest(
147        GURL("http://example.com/foo.html"),
148        net::DEFAULT_PRIORITY,
149        &url_request_delegate_,
150        NULL);
151
152    request_->set_method("GET");
153    request_->Start();
154    base::RunLoop().RunUntilIdle();
155    EXPECT_TRUE(request_->status().is_success());
156    EXPECT_EQ(expected_status_code,
157              request_->response_headers()->response_code());
158    EXPECT_EQ(expected_status_text,
159              request_->response_headers()->GetStatusText());
160    EXPECT_EQ(expected_response, url_request_delegate_.response_data());
161  }
162
163  TestBrowserThreadBundle thread_bundle_;
164
165  scoped_ptr<TestBrowserContext> browser_context_;
166  scoped_ptr<EmbeddedWorkerTestHelper> helper_;
167  scoped_refptr<ServiceWorkerRegistration> registration_;
168  scoped_refptr<ServiceWorkerVersion> version_;
169
170  scoped_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
171  net::URLRequestContext url_request_context_;
172  MockURLRequestDelegate url_request_delegate_;
173  scoped_ptr<net::URLRequest> request_;
174
175  scoped_refptr<storage::BlobData> blob_data_;
176
177 private:
178  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLRequestJobTest);
179};
180
181TEST_F(ServiceWorkerURLRequestJobTest, Simple) {
182  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
183  TestRequest(200, "OK", std::string());
184}
185
186// Responds to fetch events with a blob.
187class BlobResponder : public EmbeddedWorkerTestHelper {
188 public:
189  BlobResponder(int mock_render_process_id, const std::string& blob_uuid)
190      : EmbeddedWorkerTestHelper(mock_render_process_id),
191        blob_uuid_(blob_uuid) {}
192  virtual ~BlobResponder() {}
193
194 protected:
195  virtual void OnFetchEvent(int embedded_worker_id,
196                            int request_id,
197                            const ServiceWorkerFetchRequest& request) OVERRIDE {
198    SimulateSend(new ServiceWorkerHostMsg_FetchEventFinished(
199        embedded_worker_id,
200        request_id,
201        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
202        ServiceWorkerResponse(
203            GURL(""), 200, "OK", ServiceWorkerHeaderMap(), blob_uuid_)));
204  }
205
206  std::string blob_uuid_;
207
208 private:
209  DISALLOW_COPY_AND_ASSIGN(BlobResponder);
210};
211
212TEST_F(ServiceWorkerURLRequestJobTest, BlobResponse) {
213  ChromeBlobStorageContext* blob_storage_context =
214      ChromeBlobStorageContext::GetFor(browser_context_.get());
215  std::string expected_response;
216  for (int i = 0; i < 1024; ++i) {
217    blob_data_->AppendData(kTestData);
218    expected_response += kTestData;
219  }
220  scoped_ptr<storage::BlobDataHandle> blob_handle =
221      blob_storage_context->context()->AddFinishedBlob(blob_data_.get());
222  SetUpWithHelper(new BlobResponder(kProcessID, blob_handle->uuid()));
223
224  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
225  TestRequest(200, "OK", expected_response);
226}
227
228TEST_F(ServiceWorkerURLRequestJobTest, NonExistentBlobUUIDResponse) {
229  SetUpWithHelper(new BlobResponder(kProcessID, "blob-id:nothing-is-here"));
230  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
231  TestRequest(500, "Service Worker Response Error", std::string());
232}
233
234// TODO(kinuko): Add more tests with different response data and also for
235// FallbackToNetwork case.
236
237}  // namespace content
238