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/files/file_path.h"
8 #include "base/files/file_util.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/thread_task_runner_handle.h"
15 #include "base/time/time.h"
16 #include "content/browser/fileapi/mock_url_request_delegate.h"
17 #include "content/public/test/async_file_test_helper.h"
18 #include "content/public/test/test_file_system_context.h"
19 #include "net/base/request_priority.h"
20 #include "net/http/http_byte_range.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/url_request/url_request.h"
24 #include "net/url_request/url_request_context.h"
25 #include "net/url_request/url_request_job_factory_impl.h"
26 #include "storage/browser/blob/blob_data_builder.h"
27 #include "storage/browser/blob/blob_data_handle.h"
28 #include "storage/browser/blob/blob_data_snapshot.h"
29 #include "storage/browser/blob/blob_storage_context.h"
30 #include "storage/browser/blob/blob_url_request_job.h"
31 #include "storage/browser/fileapi/file_system_context.h"
32 #include "storage/browser/fileapi/file_system_operation_context.h"
33 #include "storage/browser/fileapi/file_system_url.h"
34 #include "testing/gtest/include/gtest/gtest.h"
36 using storage::BlobDataSnapshot
;
37 using storage::BlobDataBuilder
;
38 using storage::BlobURLRequestJob
;
44 const int kBufferSize
= 1024;
45 const char kTestData1
[] = "Hello";
46 const char kTestData2
[] = "Here it is data.";
47 const char kTestFileData1
[] = "0123456789";
48 const char kTestFileData2
[] = "This is sample file.";
49 const char kTestFileSystemFileData1
[] = "abcdefghijklmnop";
50 const char kTestFileSystemFileData2
[] = "File system file test data.";
51 const char kTestContentType
[] = "foo/bar";
52 const char kTestContentDisposition
[] = "attachment; filename=foo.txt";
54 const char kFileSystemURLOrigin
[] = "http://remote";
55 const storage::FileSystemType kFileSystemType
=
56 storage::kFileSystemTypeTemporary
;
60 class BlobURLRequestJobTest
: public testing::Test
{
62 // A simple ProtocolHandler implementation to create BlobURLRequestJob.
63 class MockProtocolHandler
:
64 public net::URLRequestJobFactory::ProtocolHandler
{
66 MockProtocolHandler(BlobURLRequestJobTest
* test
) : test_(test
) {}
68 // net::URLRequestJobFactory::ProtocolHandler override.
69 net::URLRequestJob
* MaybeCreateJob(
70 net::URLRequest
* request
,
71 net::NetworkDelegate
* network_delegate
) const override
{
72 return new BlobURLRequestJob(request
, network_delegate
,
73 test_
->GetSnapshotFromBuilder(),
74 test_
->file_system_context_
.get(),
75 base::ThreadTaskRunnerHandle::Get().get());
79 BlobURLRequestJobTest
* test_
;
82 BlobURLRequestJobTest()
83 : blob_data_(new BlobDataBuilder("uuid")), expected_status_code_(0) {}
85 void SetUp() override
{
86 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
88 temp_file1_
= temp_dir_
.path().AppendASCII("BlobFile1.dat");
89 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
90 base::WriteFile(temp_file1_
, kTestFileData1
,
91 arraysize(kTestFileData1
) - 1));
92 base::File::Info file_info1
;
93 base::GetFileInfo(temp_file1_
, &file_info1
);
94 temp_file_modification_time1_
= file_info1
.last_modified
;
96 temp_file2_
= temp_dir_
.path().AppendASCII("BlobFile2.dat");
97 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
98 base::WriteFile(temp_file2_
, kTestFileData2
,
99 arraysize(kTestFileData2
) - 1));
100 base::File::Info file_info2
;
101 base::GetFileInfo(temp_file2_
, &file_info2
);
102 temp_file_modification_time2_
= file_info2
.last_modified
;
104 url_request_job_factory_
.SetProtocolHandler("blob",
105 new MockProtocolHandler(this));
106 url_request_context_
.set_job_factory(&url_request_job_factory_
);
109 void TearDown() override
{
110 blob_handle_
.reset();
112 base::RunLoop run_loop
;
113 run_loop
.RunUntilIdle();
116 void SetUpFileSystem() {
117 // Prepare file system.
118 file_system_context_
= CreateFileSystemContextForTesting(
119 NULL
, temp_dir_
.path());
121 file_system_context_
->OpenFileSystem(
122 GURL(kFileSystemURLOrigin
),
124 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
125 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem
,
126 base::Unretained(this)));
127 base::RunLoop().RunUntilIdle();
128 ASSERT_TRUE(file_system_root_url_
.is_valid());
130 // Prepare files on file system.
131 const char kFilename1
[] = "FileSystemFile1.dat";
132 temp_file_system_file1_
= GetFileSystemURL(kFilename1
);
133 WriteFileSystemFile(kFilename1
, kTestFileSystemFileData1
,
134 arraysize(kTestFileSystemFileData1
) - 1,
135 &temp_file_system_file_modification_time1_
);
136 const char kFilename2
[] = "FileSystemFile2.dat";
137 temp_file_system_file2_
= GetFileSystemURL(kFilename2
);
138 WriteFileSystemFile(kFilename2
, kTestFileSystemFileData2
,
139 arraysize(kTestFileSystemFileData2
) - 1,
140 &temp_file_system_file_modification_time2_
);
143 GURL
GetFileSystemURL(const std::string
& filename
) {
144 return GURL(file_system_root_url_
.spec() + filename
);
147 void WriteFileSystemFile(const std::string
& filename
,
148 const char* buf
, int buf_size
,
149 base::Time
* modification_time
) {
150 storage::FileSystemURL url
=
151 file_system_context_
->CreateCrackedFileSystemURL(
152 GURL(kFileSystemURLOrigin
),
154 base::FilePath().AppendASCII(filename
));
156 ASSERT_EQ(base::File::FILE_OK
,
157 content::AsyncFileTestHelper::CreateFileWithData(
158 file_system_context_
.get(), url
, buf
, buf_size
));
160 base::File::Info file_info
;
161 ASSERT_EQ(base::File::FILE_OK
,
162 content::AsyncFileTestHelper::GetMetadata(
163 file_system_context_
.get(), url
, &file_info
));
164 if (modification_time
)
165 *modification_time
= file_info
.last_modified
;
168 void OnValidateFileSystem(const GURL
& root
,
169 const std::string
& name
,
170 base::File::Error result
) {
171 ASSERT_EQ(base::File::FILE_OK
, result
);
172 ASSERT_TRUE(root
.is_valid());
173 file_system_root_url_
= root
;
176 void TestSuccessNonrangeRequest(const std::string
& expected_response
,
177 int64 expected_content_length
) {
178 expected_status_code_
= 200;
179 expected_response_
= expected_response
;
180 TestRequest("GET", net::HttpRequestHeaders());
181 EXPECT_EQ(expected_content_length
,
182 request_
->response_headers()->GetContentLength());
185 void TestErrorRequest(int expected_status_code
) {
186 expected_status_code_
= expected_status_code
;
187 expected_response_
= "";
188 TestRequest("GET", net::HttpRequestHeaders());
191 void TestRequest(const std::string
& method
,
192 const net::HttpRequestHeaders
& extra_headers
) {
193 request_
= url_request_context_
.CreateRequest(
194 GURL("blob:blah"), net::DEFAULT_PRIORITY
, &url_request_delegate_
);
195 request_
->set_method(method
);
196 if (!extra_headers
.IsEmpty())
197 request_
->SetExtraRequestHeaders(extra_headers
);
200 base::MessageLoop::current()->Run();
203 EXPECT_TRUE(request_
->status().is_success());
204 EXPECT_EQ(expected_status_code_
,
205 request_
->response_headers()->response_code());
206 EXPECT_EQ(expected_response_
, url_request_delegate_
.response_data());
209 void BuildComplicatedData(std::string
* expected_result
) {
210 blob_data_
->AppendData(kTestData1
+ 1, 2);
211 blob_data_
->AppendFile(temp_file1_
, 2, 3, temp_file_modification_time1_
);
212 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 3, 4,
213 temp_file_system_file_modification_time1_
);
214 blob_data_
->AppendData(kTestData2
+ 4, 5);
215 blob_data_
->AppendFile(temp_file2_
, 5, 6, temp_file_modification_time2_
);
216 blob_data_
->AppendFileSystemFile(temp_file_system_file2_
, 6, 7,
217 temp_file_system_file_modification_time2_
);
218 *expected_result
= std::string(kTestData1
+ 1, 2);
219 *expected_result
+= std::string(kTestFileData1
+ 2, 3);
220 *expected_result
+= std::string(kTestFileSystemFileData1
+ 3, 4);
221 *expected_result
+= std::string(kTestData2
+ 4, 5);
222 *expected_result
+= std::string(kTestFileData2
+ 5, 6);
223 *expected_result
+= std::string(kTestFileSystemFileData2
+ 6, 7);
226 scoped_ptr
<BlobDataSnapshot
> GetSnapshotFromBuilder() {
228 blob_handle_
= blob_context_
.AddFinishedBlob(blob_data_
.get()).Pass();
230 return blob_handle_
->CreateSnapshot().Pass();
233 // This only works if all the Blob items have a definite pre-computed length.
234 // Otherwise, this will fail a CHECK.
235 int64
GetTotalBlobLength() {
237 scoped_ptr
<BlobDataSnapshot
> data
= GetSnapshotFromBuilder();
238 const auto& items
= data
->items();
239 for (const auto& item
: items
) {
240 int64 length
= base::checked_cast
<int64
>(item
->length());
241 CHECK(length
<= kint64max
- total
);
248 base::ScopedTempDir temp_dir_
;
249 base::FilePath temp_file1_
;
250 base::FilePath temp_file2_
;
251 base::Time temp_file_modification_time1_
;
252 base::Time temp_file_modification_time2_
;
253 GURL file_system_root_url_
;
254 GURL temp_file_system_file1_
;
255 GURL temp_file_system_file2_
;
256 base::Time temp_file_system_file_modification_time1_
;
257 base::Time temp_file_system_file_modification_time2_
;
259 base::MessageLoopForIO message_loop_
;
260 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
262 storage::BlobStorageContext blob_context_
;
263 scoped_ptr
<storage::BlobDataHandle
> blob_handle_
;
264 scoped_ptr
<BlobDataBuilder
> blob_data_
;
265 scoped_ptr
<BlobDataSnapshot
> blob_data_snapshot_
;
266 net::URLRequestJobFactoryImpl url_request_job_factory_
;
267 net::URLRequestContext url_request_context_
;
268 MockURLRequestDelegate url_request_delegate_
;
269 scoped_ptr
<net::URLRequest
> request_
;
271 int expected_status_code_
;
272 std::string expected_response_
;
275 TEST_F(BlobURLRequestJobTest
, TestGetSimpleDataRequest
) {
276 blob_data_
->AppendData(kTestData1
);
277 TestSuccessNonrangeRequest(kTestData1
, arraysize(kTestData1
) - 1);
280 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileRequest
) {
281 blob_data_
->AppendFile(temp_file1_
, 0, kuint64max
, base::Time());
282 TestSuccessNonrangeRequest(kTestFileData1
, arraysize(kTestFileData1
) - 1);
285 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileRequest
) {
286 base::FilePath large_temp_file
=
287 temp_dir_
.path().AppendASCII("LargeBlob.dat");
288 std::string large_data
;
289 large_data
.reserve(kBufferSize
* 5);
290 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
291 large_data
.append(1, static_cast<char>(i
% 256));
292 ASSERT_EQ(static_cast<int>(large_data
.size()),
293 base::WriteFile(large_temp_file
, large_data
.data(),
295 blob_data_
->AppendFile(large_temp_file
, 0, kuint64max
, base::Time());
296 TestSuccessNonrangeRequest(large_data
, large_data
.size());
299 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileRequest
) {
300 base::FilePath non_existent_file
=
301 temp_file1_
.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
302 blob_data_
->AppendFile(non_existent_file
, 0, kuint64max
, base::Time());
303 TestErrorRequest(404);
306 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileRequest
) {
307 base::Time old_time
=
308 temp_file_modification_time1_
- base::TimeDelta::FromSeconds(10);
309 blob_data_
->AppendFile(temp_file1_
, 0, 3, old_time
);
310 TestErrorRequest(404);
313 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileRequest
) {
314 blob_data_
->AppendFile(temp_file1_
, 2, 4, temp_file_modification_time1_
);
315 std::string
result(kTestFileData1
+ 2, 4);
316 TestSuccessNonrangeRequest(result
, 4);
319 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileSystemFileRequest
) {
321 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, kuint64max
,
323 TestSuccessNonrangeRequest(kTestFileSystemFileData1
,
324 arraysize(kTestFileSystemFileData1
) - 1);
327 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileSystemFileRequest
) {
329 std::string large_data
;
330 large_data
.reserve(kBufferSize
* 5);
331 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
332 large_data
.append(1, static_cast<char>(i
% 256));
334 const char kFilename
[] = "LargeBlob.dat";
335 WriteFileSystemFile(kFilename
, large_data
.data(), large_data
.size(), NULL
);
337 blob_data_
->AppendFileSystemFile(GetFileSystemURL(kFilename
), 0, kuint64max
,
339 TestSuccessNonrangeRequest(large_data
, large_data
.size());
342 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileSystemFileRequest
) {
344 GURL non_existent_file
= GetFileSystemURL("non-existent.dat");
345 blob_data_
->AppendFileSystemFile(non_existent_file
, 0, kuint64max
,
347 TestErrorRequest(404);
350 TEST_F(BlobURLRequestJobTest
, TestGetInvalidFileSystemFileRequest
) {
353 blob_data_
->AppendFileSystemFile(invalid_file
, 0, kuint64max
, base::Time());
354 TestErrorRequest(500);
357 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileSystemFileRequest
) {
359 base::Time old_time
=
360 temp_file_system_file_modification_time1_
-
361 base::TimeDelta::FromSeconds(10);
362 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, 3, old_time
);
363 TestErrorRequest(404);
366 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileSystemFileRequest
) {
368 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 2, 4,
369 temp_file_system_file_modification_time1_
);
370 std::string
result(kTestFileSystemFileData1
+ 2, 4);
371 TestSuccessNonrangeRequest(result
, 4);
374 TEST_F(BlobURLRequestJobTest
, TestGetComplicatedDataAndFileRequest
) {
377 BuildComplicatedData(&result
);
378 TestSuccessNonrangeRequest(result
, GetTotalBlobLength());
381 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest1
) {
384 BuildComplicatedData(&result
);
385 net::HttpRequestHeaders extra_headers
;
386 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
387 net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
388 expected_status_code_
= 206;
389 expected_response_
= result
.substr(5, 10 - 5 + 1);
390 TestRequest("GET", extra_headers
);
392 EXPECT_EQ(6, request_
->response_headers()->GetContentLength());
394 int64 first
= 0, last
= 0, length
= 0;
396 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
399 EXPECT_EQ(GetTotalBlobLength(), length
);
402 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest2
) {
405 BuildComplicatedData(&result
);
406 net::HttpRequestHeaders extra_headers
;
407 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
408 net::HttpByteRange::Suffix(10).GetHeaderValue());
409 expected_status_code_
= 206;
410 expected_response_
= result
.substr(result
.length() - 10);
411 TestRequest("GET", extra_headers
);
413 EXPECT_EQ(10, request_
->response_headers()->GetContentLength());
415 int64 total
= GetTotalBlobLength();
416 int64 first
= 0, last
= 0, length
= 0;
418 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
419 EXPECT_EQ(total
- 10, first
);
420 EXPECT_EQ(total
- 1, last
);
421 EXPECT_EQ(total
, length
);
424 TEST_F(BlobURLRequestJobTest
, TestExtraHeaders
) {
425 blob_data_
->set_content_type(kTestContentType
);
426 blob_data_
->set_content_disposition(kTestContentDisposition
);
427 blob_data_
->AppendData(kTestData1
);
428 expected_status_code_
= 200;
429 expected_response_
= kTestData1
;
430 TestRequest("GET", net::HttpRequestHeaders());
432 std::string content_type
;
433 EXPECT_TRUE(request_
->response_headers()->GetMimeType(&content_type
));
434 EXPECT_EQ(kTestContentType
, content_type
);
436 std::string content_disposition
;
437 EXPECT_TRUE(request_
->response_headers()->EnumerateHeader(
438 &iter
, "Content-Disposition", &content_disposition
));
439 EXPECT_EQ(kTestContentDisposition
, content_disposition
);
442 } // namespace content