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/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 "storage/browser/blob/blob_data_builder.h"
26 #include "storage/browser/blob/blob_data_handle.h"
27 #include "storage/browser/blob/blob_data_snapshot.h"
28 #include "storage/browser/blob/blob_storage_context.h"
29 #include "storage/browser/blob/blob_url_request_job.h"
30 #include "storage/browser/fileapi/file_system_context.h"
31 #include "storage/browser/fileapi/file_system_operation_context.h"
32 #include "storage/browser/fileapi/file_system_url.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 using storage::BlobDataSnapshot
;
36 using storage::BlobDataBuilder
;
37 using storage::BlobURLRequestJob
;
43 const int kBufferSize
= 1024;
44 const char kTestData1
[] = "Hello";
45 const char kTestData2
[] = "Here it is data.";
46 const char kTestFileData1
[] = "0123456789";
47 const char kTestFileData2
[] = "This is sample file.";
48 const char kTestFileSystemFileData1
[] = "abcdefghijklmnop";
49 const char kTestFileSystemFileData2
[] = "File system file test data.";
50 const char kTestContentType
[] = "foo/bar";
51 const char kTestContentDisposition
[] = "attachment; filename=foo.txt";
53 const char kFileSystemURLOrigin
[] = "http://remote";
54 const storage::FileSystemType kFileSystemType
=
55 storage::kFileSystemTypeTemporary
;
59 class BlobURLRequestJobTest
: public testing::Test
{
61 // A simple ProtocolHandler implementation to create BlobURLRequestJob.
62 class MockProtocolHandler
:
63 public net::URLRequestJobFactory::ProtocolHandler
{
65 MockProtocolHandler(BlobURLRequestJobTest
* test
) : test_(test
) {}
67 // net::URLRequestJobFactory::ProtocolHandler override.
68 net::URLRequestJob
* MaybeCreateJob(
69 net::URLRequest
* request
,
70 net::NetworkDelegate
* network_delegate
) const override
{
71 return new BlobURLRequestJob(request
, network_delegate
,
72 test_
->GetSnapshotFromBuilder(),
73 test_
->file_system_context_
.get(),
74 base::MessageLoopProxy::current().get());
78 BlobURLRequestJobTest
* test_
;
81 BlobURLRequestJobTest()
82 : blob_data_(new BlobDataBuilder("uuid")), expected_status_code_(0) {}
84 void SetUp() override
{
85 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
87 temp_file1_
= temp_dir_
.path().AppendASCII("BlobFile1.dat");
88 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
89 base::WriteFile(temp_file1_
, kTestFileData1
,
90 arraysize(kTestFileData1
) - 1));
91 base::File::Info file_info1
;
92 base::GetFileInfo(temp_file1_
, &file_info1
);
93 temp_file_modification_time1_
= file_info1
.last_modified
;
95 temp_file2_
= temp_dir_
.path().AppendASCII("BlobFile2.dat");
96 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
97 base::WriteFile(temp_file2_
, kTestFileData2
,
98 arraysize(kTestFileData2
) - 1));
99 base::File::Info file_info2
;
100 base::GetFileInfo(temp_file2_
, &file_info2
);
101 temp_file_modification_time2_
= file_info2
.last_modified
;
103 url_request_job_factory_
.SetProtocolHandler("blob",
104 new MockProtocolHandler(this));
105 url_request_context_
.set_job_factory(&url_request_job_factory_
);
108 void TearDown() override
{
109 blob_handle_
.reset();
111 base::RunLoop run_loop
;
112 run_loop
.RunUntilIdle();
115 void SetUpFileSystem() {
116 // Prepare file system.
117 file_system_context_
= CreateFileSystemContextForTesting(
118 NULL
, temp_dir_
.path());
120 file_system_context_
->OpenFileSystem(
121 GURL(kFileSystemURLOrigin
),
123 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
124 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem
,
125 base::Unretained(this)));
126 base::RunLoop().RunUntilIdle();
127 ASSERT_TRUE(file_system_root_url_
.is_valid());
129 // Prepare files on file system.
130 const char kFilename1
[] = "FileSystemFile1.dat";
131 temp_file_system_file1_
= GetFileSystemURL(kFilename1
);
132 WriteFileSystemFile(kFilename1
, kTestFileSystemFileData1
,
133 arraysize(kTestFileSystemFileData1
) - 1,
134 &temp_file_system_file_modification_time1_
);
135 const char kFilename2
[] = "FileSystemFile2.dat";
136 temp_file_system_file2_
= GetFileSystemURL(kFilename2
);
137 WriteFileSystemFile(kFilename2
, kTestFileSystemFileData2
,
138 arraysize(kTestFileSystemFileData2
) - 1,
139 &temp_file_system_file_modification_time2_
);
142 GURL
GetFileSystemURL(const std::string
& filename
) {
143 return GURL(file_system_root_url_
.spec() + filename
);
146 void WriteFileSystemFile(const std::string
& filename
,
147 const char* buf
, int buf_size
,
148 base::Time
* modification_time
) {
149 storage::FileSystemURL url
=
150 file_system_context_
->CreateCrackedFileSystemURL(
151 GURL(kFileSystemURLOrigin
),
153 base::FilePath().AppendASCII(filename
));
155 ASSERT_EQ(base::File::FILE_OK
,
156 content::AsyncFileTestHelper::CreateFileWithData(
157 file_system_context_
.get(), url
, buf
, buf_size
));
159 base::File::Info file_info
;
160 ASSERT_EQ(base::File::FILE_OK
,
161 content::AsyncFileTestHelper::GetMetadata(
162 file_system_context_
.get(), url
, &file_info
));
163 if (modification_time
)
164 *modification_time
= file_info
.last_modified
;
167 void OnValidateFileSystem(const GURL
& root
,
168 const std::string
& name
,
169 base::File::Error result
) {
170 ASSERT_EQ(base::File::FILE_OK
, result
);
171 ASSERT_TRUE(root
.is_valid());
172 file_system_root_url_
= root
;
175 void TestSuccessNonrangeRequest(const std::string
& expected_response
,
176 int64 expected_content_length
) {
177 expected_status_code_
= 200;
178 expected_response_
= expected_response
;
179 TestRequest("GET", net::HttpRequestHeaders());
180 EXPECT_EQ(expected_content_length
,
181 request_
->response_headers()->GetContentLength());
184 void TestErrorRequest(int expected_status_code
) {
185 expected_status_code_
= expected_status_code
;
186 expected_response_
= "";
187 TestRequest("GET", net::HttpRequestHeaders());
190 void TestRequest(const std::string
& method
,
191 const net::HttpRequestHeaders
& extra_headers
) {
192 request_
= url_request_context_
.CreateRequest(
193 GURL("blob:blah"), net::DEFAULT_PRIORITY
, &url_request_delegate_
);
194 request_
->set_method(method
);
195 if (!extra_headers
.IsEmpty())
196 request_
->SetExtraRequestHeaders(extra_headers
);
199 base::MessageLoop::current()->Run();
202 EXPECT_TRUE(request_
->status().is_success());
203 EXPECT_EQ(expected_status_code_
,
204 request_
->response_headers()->response_code());
205 EXPECT_EQ(expected_response_
, url_request_delegate_
.response_data());
208 void BuildComplicatedData(std::string
* expected_result
) {
209 blob_data_
->AppendData(kTestData1
+ 1, 2);
210 blob_data_
->AppendFile(temp_file1_
, 2, 3, temp_file_modification_time1_
);
211 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 3, 4,
212 temp_file_system_file_modification_time1_
);
213 blob_data_
->AppendData(kTestData2
+ 4, 5);
214 blob_data_
->AppendFile(temp_file2_
, 5, 6, temp_file_modification_time2_
);
215 blob_data_
->AppendFileSystemFile(temp_file_system_file2_
, 6, 7,
216 temp_file_system_file_modification_time2_
);
217 *expected_result
= std::string(kTestData1
+ 1, 2);
218 *expected_result
+= std::string(kTestFileData1
+ 2, 3);
219 *expected_result
+= std::string(kTestFileSystemFileData1
+ 3, 4);
220 *expected_result
+= std::string(kTestData2
+ 4, 5);
221 *expected_result
+= std::string(kTestFileData2
+ 5, 6);
222 *expected_result
+= std::string(kTestFileSystemFileData2
+ 6, 7);
225 scoped_ptr
<BlobDataSnapshot
> GetSnapshotFromBuilder() {
227 blob_handle_
= blob_context_
.AddFinishedBlob(blob_data_
.get()).Pass();
229 return blob_handle_
->CreateSnapshot().Pass();
232 // This only works if all the Blob items have a definite pre-computed length.
233 // Otherwise, this will fail a CHECK.
234 int64
GetTotalBlobLength() {
236 scoped_ptr
<BlobDataSnapshot
> data
= GetSnapshotFromBuilder();
237 const auto& items
= data
->items();
238 for (const auto& item
: items
) {
239 int64 length
= base::checked_cast
<int64
>(item
->length());
240 CHECK(length
<= kint64max
- total
);
247 base::ScopedTempDir temp_dir_
;
248 base::FilePath temp_file1_
;
249 base::FilePath temp_file2_
;
250 base::Time temp_file_modification_time1_
;
251 base::Time temp_file_modification_time2_
;
252 GURL file_system_root_url_
;
253 GURL temp_file_system_file1_
;
254 GURL temp_file_system_file2_
;
255 base::Time temp_file_system_file_modification_time1_
;
256 base::Time temp_file_system_file_modification_time2_
;
258 base::MessageLoopForIO message_loop_
;
259 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
261 storage::BlobStorageContext blob_context_
;
262 scoped_ptr
<storage::BlobDataHandle
> blob_handle_
;
263 scoped_ptr
<BlobDataBuilder
> blob_data_
;
264 scoped_ptr
<BlobDataSnapshot
> blob_data_snapshot_
;
265 net::URLRequestJobFactoryImpl url_request_job_factory_
;
266 net::URLRequestContext url_request_context_
;
267 MockURLRequestDelegate url_request_delegate_
;
268 scoped_ptr
<net::URLRequest
> request_
;
270 int expected_status_code_
;
271 std::string expected_response_
;
274 TEST_F(BlobURLRequestJobTest
, TestGetSimpleDataRequest
) {
275 blob_data_
->AppendData(kTestData1
);
276 TestSuccessNonrangeRequest(kTestData1
, arraysize(kTestData1
) - 1);
279 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileRequest
) {
280 blob_data_
->AppendFile(temp_file1_
, 0, kuint64max
, base::Time());
281 TestSuccessNonrangeRequest(kTestFileData1
, arraysize(kTestFileData1
) - 1);
284 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileRequest
) {
285 base::FilePath large_temp_file
=
286 temp_dir_
.path().AppendASCII("LargeBlob.dat");
287 std::string large_data
;
288 large_data
.reserve(kBufferSize
* 5);
289 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
290 large_data
.append(1, static_cast<char>(i
% 256));
291 ASSERT_EQ(static_cast<int>(large_data
.size()),
292 base::WriteFile(large_temp_file
, large_data
.data(),
294 blob_data_
->AppendFile(large_temp_file
, 0, kuint64max
, base::Time());
295 TestSuccessNonrangeRequest(large_data
, large_data
.size());
298 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileRequest
) {
299 base::FilePath non_existent_file
=
300 temp_file1_
.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
301 blob_data_
->AppendFile(non_existent_file
, 0, kuint64max
, base::Time());
302 TestErrorRequest(404);
305 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileRequest
) {
306 base::Time old_time
=
307 temp_file_modification_time1_
- base::TimeDelta::FromSeconds(10);
308 blob_data_
->AppendFile(temp_file1_
, 0, 3, old_time
);
309 TestErrorRequest(404);
312 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileRequest
) {
313 blob_data_
->AppendFile(temp_file1_
, 2, 4, temp_file_modification_time1_
);
314 std::string
result(kTestFileData1
+ 2, 4);
315 TestSuccessNonrangeRequest(result
, 4);
318 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileSystemFileRequest
) {
320 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, kuint64max
,
322 TestSuccessNonrangeRequest(kTestFileSystemFileData1
,
323 arraysize(kTestFileSystemFileData1
) - 1);
326 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileSystemFileRequest
) {
328 std::string large_data
;
329 large_data
.reserve(kBufferSize
* 5);
330 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
331 large_data
.append(1, static_cast<char>(i
% 256));
333 const char kFilename
[] = "LargeBlob.dat";
334 WriteFileSystemFile(kFilename
, large_data
.data(), large_data
.size(), NULL
);
336 blob_data_
->AppendFileSystemFile(GetFileSystemURL(kFilename
), 0, kuint64max
,
338 TestSuccessNonrangeRequest(large_data
, large_data
.size());
341 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileSystemFileRequest
) {
343 GURL non_existent_file
= GetFileSystemURL("non-existent.dat");
344 blob_data_
->AppendFileSystemFile(non_existent_file
, 0, kuint64max
,
346 TestErrorRequest(404);
349 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileSystemFileRequest
) {
351 base::Time old_time
=
352 temp_file_system_file_modification_time1_
-
353 base::TimeDelta::FromSeconds(10);
354 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, 3, old_time
);
355 TestErrorRequest(404);
358 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileSystemFileRequest
) {
360 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 2, 4,
361 temp_file_system_file_modification_time1_
);
362 std::string
result(kTestFileSystemFileData1
+ 2, 4);
363 TestSuccessNonrangeRequest(result
, 4);
366 TEST_F(BlobURLRequestJobTest
, TestGetComplicatedDataAndFileRequest
) {
369 BuildComplicatedData(&result
);
370 TestSuccessNonrangeRequest(result
, GetTotalBlobLength());
373 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest1
) {
376 BuildComplicatedData(&result
);
377 net::HttpRequestHeaders extra_headers
;
378 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
379 net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
380 expected_status_code_
= 206;
381 expected_response_
= result
.substr(5, 10 - 5 + 1);
382 TestRequest("GET", extra_headers
);
384 EXPECT_EQ(6, request_
->response_headers()->GetContentLength());
386 int64 first
= 0, last
= 0, length
= 0;
388 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
391 EXPECT_EQ(GetTotalBlobLength(), length
);
394 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest2
) {
397 BuildComplicatedData(&result
);
398 net::HttpRequestHeaders extra_headers
;
399 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
400 net::HttpByteRange::Suffix(10).GetHeaderValue());
401 expected_status_code_
= 206;
402 expected_response_
= result
.substr(result
.length() - 10);
403 TestRequest("GET", extra_headers
);
405 EXPECT_EQ(10, request_
->response_headers()->GetContentLength());
407 int64 total
= GetTotalBlobLength();
408 int64 first
= 0, last
= 0, length
= 0;
410 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
411 EXPECT_EQ(total
- 10, first
);
412 EXPECT_EQ(total
- 1, last
);
413 EXPECT_EQ(total
, length
);
416 TEST_F(BlobURLRequestJobTest
, TestExtraHeaders
) {
417 blob_data_
->set_content_type(kTestContentType
);
418 blob_data_
->set_content_disposition(kTestContentDisposition
);
419 blob_data_
->AppendData(kTestData1
);
420 expected_status_code_
= 200;
421 expected_response_
= kTestData1
;
422 TestRequest("GET", net::HttpRequestHeaders());
424 std::string content_type
;
425 EXPECT_TRUE(request_
->response_headers()->GetMimeType(&content_type
));
426 EXPECT_EQ(kTestContentType
, content_type
);
428 std::string content_disposition
;
429 EXPECT_TRUE(request_
->response_headers()->EnumerateHeader(
430 &iter
, "Content-Disposition", &content_disposition
));
431 EXPECT_EQ(kTestContentDisposition
, content_disposition
);
434 } // namespace content