1 // Copyright 2013 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.
5 #include "base/basictypes.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/run_loop.h"
14 #include "base/time/time.h"
15 #include "content/browser/fileapi/mock_url_request_delegate.h"
16 #include "content/public/test/async_file_test_helper.h"
17 #include "content/public/test/test_file_system_context.h"
18 #include "net/base/request_priority.h"
19 #include "net/http/http_byte_range.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_context.h"
24 #include "net/url_request/url_request_job_factory_impl.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "webkit/browser/blob/blob_url_request_job.h"
27 #include "webkit/browser/fileapi/file_system_context.h"
28 #include "webkit/browser/fileapi/file_system_operation_context.h"
29 #include "webkit/browser/fileapi/file_system_url.h"
30 #include "webkit/common/blob/blob_data.h"
32 using storage::BlobData
;
33 using storage::BlobURLRequestJob
;
39 const int kBufferSize
= 1024;
40 const char kTestData1
[] = "Hello";
41 const char kTestData2
[] = "Here it is data.";
42 const char kTestFileData1
[] = "0123456789";
43 const char kTestFileData2
[] = "This is sample file.";
44 const char kTestFileSystemFileData1
[] = "abcdefghijklmnop";
45 const char kTestFileSystemFileData2
[] = "File system file test data.";
46 const char kTestContentType
[] = "foo/bar";
47 const char kTestContentDisposition
[] = "attachment; filename=foo.txt";
49 const char kFileSystemURLOrigin
[] = "http://remote";
50 const storage::FileSystemType kFileSystemType
=
51 storage::kFileSystemTypeTemporary
;
55 class BlobURLRequestJobTest
: public testing::Test
{
57 // A simple ProtocolHandler implementation to create BlobURLRequestJob.
58 class MockProtocolHandler
:
59 public net::URLRequestJobFactory::ProtocolHandler
{
61 MockProtocolHandler(BlobURLRequestJobTest
* test
) : test_(test
) {}
63 // net::URLRequestJobFactory::ProtocolHandler override.
64 virtual net::URLRequestJob
* MaybeCreateJob(
65 net::URLRequest
* request
,
66 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
67 return new BlobURLRequestJob(request
,
69 test_
->blob_data_
.get(),
70 test_
->file_system_context_
.get(),
71 base::MessageLoopProxy::current().get());
75 BlobURLRequestJobTest
* test_
;
78 BlobURLRequestJobTest()
79 : blob_data_(new BlobData()),
80 expected_status_code_(0) {}
82 virtual void SetUp() {
83 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
85 temp_file1_
= temp_dir_
.path().AppendASCII("BlobFile1.dat");
86 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
87 base::WriteFile(temp_file1_
, kTestFileData1
,
88 arraysize(kTestFileData1
) - 1));
89 base::File::Info file_info1
;
90 base::GetFileInfo(temp_file1_
, &file_info1
);
91 temp_file_modification_time1_
= file_info1
.last_modified
;
93 temp_file2_
= temp_dir_
.path().AppendASCII("BlobFile2.dat");
94 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
95 base::WriteFile(temp_file2_
, kTestFileData2
,
96 arraysize(kTestFileData2
) - 1));
97 base::File::Info file_info2
;
98 base::GetFileInfo(temp_file2_
, &file_info2
);
99 temp_file_modification_time2_
= file_info2
.last_modified
;
101 url_request_job_factory_
.SetProtocolHandler("blob",
102 new MockProtocolHandler(this));
103 url_request_context_
.set_job_factory(&url_request_job_factory_
);
106 virtual void TearDown() {
109 void SetUpFileSystem() {
110 // Prepare file system.
111 file_system_context_
= CreateFileSystemContextForTesting(
112 NULL
, temp_dir_
.path());
114 file_system_context_
->OpenFileSystem(
115 GURL(kFileSystemURLOrigin
),
117 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
118 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem
,
119 base::Unretained(this)));
120 base::RunLoop().RunUntilIdle();
121 ASSERT_TRUE(file_system_root_url_
.is_valid());
123 // Prepare files on file system.
124 const char kFilename1
[] = "FileSystemFile1.dat";
125 temp_file_system_file1_
= GetFileSystemURL(kFilename1
);
126 WriteFileSystemFile(kFilename1
, kTestFileSystemFileData1
,
127 arraysize(kTestFileSystemFileData1
) - 1,
128 &temp_file_system_file_modification_time1_
);
129 const char kFilename2
[] = "FileSystemFile2.dat";
130 temp_file_system_file2_
= GetFileSystemURL(kFilename2
);
131 WriteFileSystemFile(kFilename2
, kTestFileSystemFileData2
,
132 arraysize(kTestFileSystemFileData2
) - 1,
133 &temp_file_system_file_modification_time2_
);
136 GURL
GetFileSystemURL(const std::string
& filename
) {
137 return GURL(file_system_root_url_
.spec() + filename
);
140 void WriteFileSystemFile(const std::string
& filename
,
141 const char* buf
, int buf_size
,
142 base::Time
* modification_time
) {
143 storage::FileSystemURL url
=
144 file_system_context_
->CreateCrackedFileSystemURL(
145 GURL(kFileSystemURLOrigin
),
147 base::FilePath().AppendASCII(filename
));
149 ASSERT_EQ(base::File::FILE_OK
,
150 content::AsyncFileTestHelper::CreateFileWithData(
151 file_system_context_
.get(), url
, buf
, buf_size
));
153 base::File::Info file_info
;
154 ASSERT_EQ(base::File::FILE_OK
,
155 content::AsyncFileTestHelper::GetMetadata(
156 file_system_context_
.get(), url
, &file_info
));
157 if (modification_time
)
158 *modification_time
= file_info
.last_modified
;
161 void OnValidateFileSystem(const GURL
& root
,
162 const std::string
& name
,
163 base::File::Error result
) {
164 ASSERT_EQ(base::File::FILE_OK
, result
);
165 ASSERT_TRUE(root
.is_valid());
166 file_system_root_url_
= root
;
169 void TestSuccessNonrangeRequest(const std::string
& expected_response
,
170 int64 expected_content_length
) {
171 expected_status_code_
= 200;
172 expected_response_
= expected_response
;
173 TestRequest("GET", net::HttpRequestHeaders());
174 EXPECT_EQ(expected_content_length
,
175 request_
->response_headers()->GetContentLength());
178 void TestErrorRequest(int expected_status_code
) {
179 expected_status_code_
= expected_status_code
;
180 expected_response_
= "";
181 TestRequest("GET", net::HttpRequestHeaders());
184 void TestRequest(const std::string
& method
,
185 const net::HttpRequestHeaders
& extra_headers
) {
186 request_
= url_request_context_
.CreateRequest(
187 GURL("blob:blah"), net::DEFAULT_PRIORITY
, &url_request_delegate_
, NULL
);
188 request_
->set_method(method
);
189 if (!extra_headers
.IsEmpty())
190 request_
->SetExtraRequestHeaders(extra_headers
);
193 base::MessageLoop::current()->Run();
196 EXPECT_TRUE(request_
->status().is_success());
197 EXPECT_EQ(expected_status_code_
,
198 request_
->response_headers()->response_code());
199 EXPECT_EQ(expected_response_
, url_request_delegate_
.response_data());
202 void BuildComplicatedData(std::string
* expected_result
) {
203 blob_data_
->AppendData(kTestData1
+ 1, 2);
204 blob_data_
->AppendFile(temp_file1_
, 2, 3, temp_file_modification_time1_
);
205 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 3, 4,
206 temp_file_system_file_modification_time1_
);
207 blob_data_
->AppendData(kTestData2
+ 4, 5);
208 blob_data_
->AppendFile(temp_file2_
, 5, 6, temp_file_modification_time2_
);
209 blob_data_
->AppendFileSystemFile(temp_file_system_file2_
, 6, 7,
210 temp_file_system_file_modification_time2_
);
211 *expected_result
= std::string(kTestData1
+ 1, 2);
212 *expected_result
+= std::string(kTestFileData1
+ 2, 3);
213 *expected_result
+= std::string(kTestFileSystemFileData1
+ 3, 4);
214 *expected_result
+= std::string(kTestData2
+ 4, 5);
215 *expected_result
+= std::string(kTestFileData2
+ 5, 6);
216 *expected_result
+= std::string(kTestFileSystemFileData2
+ 6, 7);
219 // This only works if all the Blob items have a definite pre-computed length.
220 // Otherwise, this will fail a CHECK.
221 int64
GetTotalBlobLength() const {
223 const std::vector
<BlobData::Item
>& items
= blob_data_
->items();
224 for (std::vector
<BlobData::Item
>::const_iterator it
= items
.begin();
225 it
!= items
.end(); ++it
) {
226 int64 length
= base::checked_cast
<int64
>(it
->length());
227 CHECK(length
<= kint64max
- total
);
234 base::ScopedTempDir temp_dir_
;
235 base::FilePath temp_file1_
;
236 base::FilePath temp_file2_
;
237 base::Time temp_file_modification_time1_
;
238 base::Time temp_file_modification_time2_
;
239 GURL file_system_root_url_
;
240 GURL temp_file_system_file1_
;
241 GURL temp_file_system_file2_
;
242 base::Time temp_file_system_file_modification_time1_
;
243 base::Time temp_file_system_file_modification_time2_
;
245 base::MessageLoopForIO message_loop_
;
246 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
247 scoped_refptr
<BlobData
> blob_data_
;
248 net::URLRequestJobFactoryImpl url_request_job_factory_
;
249 net::URLRequestContext url_request_context_
;
250 MockURLRequestDelegate url_request_delegate_
;
251 scoped_ptr
<net::URLRequest
> request_
;
253 int expected_status_code_
;
254 std::string expected_response_
;
257 TEST_F(BlobURLRequestJobTest
, TestGetSimpleDataRequest
) {
258 blob_data_
->AppendData(kTestData1
);
259 TestSuccessNonrangeRequest(kTestData1
, arraysize(kTestData1
) - 1);
262 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileRequest
) {
263 blob_data_
->AppendFile(temp_file1_
, 0, kuint64max
, base::Time());
264 TestSuccessNonrangeRequest(kTestFileData1
, arraysize(kTestFileData1
) - 1);
267 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileRequest
) {
268 base::FilePath large_temp_file
=
269 temp_dir_
.path().AppendASCII("LargeBlob.dat");
270 std::string large_data
;
271 large_data
.reserve(kBufferSize
* 5);
272 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
273 large_data
.append(1, static_cast<char>(i
% 256));
274 ASSERT_EQ(static_cast<int>(large_data
.size()),
275 base::WriteFile(large_temp_file
, large_data
.data(),
277 blob_data_
->AppendFile(large_temp_file
, 0, kuint64max
, base::Time());
278 TestSuccessNonrangeRequest(large_data
, large_data
.size());
281 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileRequest
) {
282 base::FilePath non_existent_file
=
283 temp_file1_
.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
284 blob_data_
->AppendFile(non_existent_file
, 0, kuint64max
, base::Time());
285 TestErrorRequest(404);
288 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileRequest
) {
289 base::Time old_time
=
290 temp_file_modification_time1_
- base::TimeDelta::FromSeconds(10);
291 blob_data_
->AppendFile(temp_file1_
, 0, 3, old_time
);
292 TestErrorRequest(404);
295 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileRequest
) {
296 blob_data_
->AppendFile(temp_file1_
, 2, 4, temp_file_modification_time1_
);
297 std::string
result(kTestFileData1
+ 2, 4);
298 TestSuccessNonrangeRequest(result
, 4);
301 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileSystemFileRequest
) {
303 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, kuint64max
,
305 TestSuccessNonrangeRequest(kTestFileSystemFileData1
,
306 arraysize(kTestFileSystemFileData1
) - 1);
309 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileSystemFileRequest
) {
311 std::string large_data
;
312 large_data
.reserve(kBufferSize
* 5);
313 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
314 large_data
.append(1, static_cast<char>(i
% 256));
316 const char kFilename
[] = "LargeBlob.dat";
317 WriteFileSystemFile(kFilename
, large_data
.data(), large_data
.size(), NULL
);
319 blob_data_
->AppendFileSystemFile(GetFileSystemURL(kFilename
), 0, kuint64max
,
321 TestSuccessNonrangeRequest(large_data
, large_data
.size());
324 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileSystemFileRequest
) {
326 GURL non_existent_file
= GetFileSystemURL("non-existent.dat");
327 blob_data_
->AppendFileSystemFile(non_existent_file
, 0, kuint64max
,
329 TestErrorRequest(404);
332 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileSystemFileRequest
) {
334 base::Time old_time
=
335 temp_file_system_file_modification_time1_
-
336 base::TimeDelta::FromSeconds(10);
337 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, 3, old_time
);
338 TestErrorRequest(404);
341 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileSystemFileRequest
) {
343 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 2, 4,
344 temp_file_system_file_modification_time1_
);
345 std::string
result(kTestFileSystemFileData1
+ 2, 4);
346 TestSuccessNonrangeRequest(result
, 4);
349 TEST_F(BlobURLRequestJobTest
, TestGetComplicatedDataAndFileRequest
) {
352 BuildComplicatedData(&result
);
353 TestSuccessNonrangeRequest(result
, GetTotalBlobLength());
356 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest1
) {
359 BuildComplicatedData(&result
);
360 net::HttpRequestHeaders extra_headers
;
361 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
362 net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
363 expected_status_code_
= 206;
364 expected_response_
= result
.substr(5, 10 - 5 + 1);
365 TestRequest("GET", extra_headers
);
367 EXPECT_EQ(6, request_
->response_headers()->GetContentLength());
369 int64 first
= 0, last
= 0, length
= 0;
371 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
374 EXPECT_EQ(GetTotalBlobLength(), length
);
377 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest2
) {
380 BuildComplicatedData(&result
);
381 net::HttpRequestHeaders extra_headers
;
382 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
383 net::HttpByteRange::Suffix(10).GetHeaderValue());
384 expected_status_code_
= 206;
385 expected_response_
= result
.substr(result
.length() - 10);
386 TestRequest("GET", extra_headers
);
388 EXPECT_EQ(10, request_
->response_headers()->GetContentLength());
390 int64 total
= GetTotalBlobLength();
391 int64 first
= 0, last
= 0, length
= 0;
393 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
394 EXPECT_EQ(total
- 10, first
);
395 EXPECT_EQ(total
- 1, last
);
396 EXPECT_EQ(total
, length
);
399 TEST_F(BlobURLRequestJobTest
, TestExtraHeaders
) {
400 blob_data_
->set_content_type(kTestContentType
);
401 blob_data_
->set_content_disposition(kTestContentDisposition
);
402 blob_data_
->AppendData(kTestData1
);
403 expected_status_code_
= 200;
404 expected_response_
= kTestData1
;
405 TestRequest("GET", net::HttpRequestHeaders());
407 std::string content_type
;
408 EXPECT_TRUE(request_
->response_headers()->GetMimeType(&content_type
));
409 EXPECT_EQ(kTestContentType
, content_type
);
411 std::string content_disposition
;
412 EXPECT_TRUE(request_
->response_headers()->EnumerateHeader(
413 &iter
, "Content-Disposition", &content_disposition
));
414 EXPECT_EQ(kTestContentDisposition
, content_disposition
);
417 } // namespace content