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/public/test/async_file_test_helper.h"
16 #include "content/public/test/test_file_system_context.h"
17 #include "net/base/io_buffer.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 webkit_blob::BlobData
;
33 using webkit_blob::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 fileapi::FileSystemType kFileSystemType
=
51 fileapi::kFileSystemTypeTemporary
;
55 class BlobURLRequestJobTest
: public testing::Test
{
58 // Test Harness -------------------------------------------------------------
59 // TODO(jianli): share this test harness with AppCacheURLRequestJobTest
61 class MockURLRequestDelegate
: public net::URLRequest::Delegate
{
63 MockURLRequestDelegate()
64 : received_data_(new net::IOBuffer(kBufferSize
)) {}
66 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
{
67 if (request
->status().is_success()) {
68 EXPECT_TRUE(request
->response_headers());
75 virtual void OnReadCompleted(net::URLRequest
* request
,
76 int bytes_read
) OVERRIDE
{
78 ReceiveData(request
, bytes_read
);
83 const std::string
& response_data() const { return response_data_
; }
86 void ReadSome(net::URLRequest
* request
) {
87 if (!request
->is_pending()) {
93 if (!request
->Read(received_data_
.get(), kBufferSize
, &bytes_read
)) {
94 if (!request
->status().is_io_pending()) {
100 ReceiveData(request
, bytes_read
);
103 void ReceiveData(net::URLRequest
* request
, int bytes_read
) {
105 response_data_
.append(received_data_
->data(),
106 static_cast<size_t>(bytes_read
));
113 void RequestComplete() {
114 base::MessageLoop::current()->Quit();
117 scoped_refptr
<net::IOBuffer
> received_data_
;
118 std::string response_data_
;
121 // A simple ProtocolHandler implementation to create BlobURLRequestJob.
122 class MockProtocolHandler
:
123 public net::URLRequestJobFactory::ProtocolHandler
{
125 MockProtocolHandler(BlobURLRequestJobTest
* test
) : test_(test
) {}
127 // net::URLRequestJobFactory::ProtocolHandler override.
128 virtual net::URLRequestJob
* MaybeCreateJob(
129 net::URLRequest
* request
,
130 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
131 return new BlobURLRequestJob(request
,
133 test_
->blob_data_
.get(),
134 test_
->file_system_context_
.get(),
135 base::MessageLoopProxy::current().get());
139 BlobURLRequestJobTest
* test_
;
142 BlobURLRequestJobTest()
143 : blob_data_(new BlobData()),
144 expected_status_code_(0) {}
146 virtual void SetUp() {
147 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
149 temp_file1_
= temp_dir_
.path().AppendASCII("BlobFile1.dat");
150 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
151 base::WriteFile(temp_file1_
, kTestFileData1
,
152 arraysize(kTestFileData1
) - 1));
153 base::File::Info file_info1
;
154 base::GetFileInfo(temp_file1_
, &file_info1
);
155 temp_file_modification_time1_
= file_info1
.last_modified
;
157 temp_file2_
= temp_dir_
.path().AppendASCII("BlobFile2.dat");
158 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
159 base::WriteFile(temp_file2_
, kTestFileData2
,
160 arraysize(kTestFileData2
) - 1));
161 base::File::Info file_info2
;
162 base::GetFileInfo(temp_file2_
, &file_info2
);
163 temp_file_modification_time2_
= file_info2
.last_modified
;
165 url_request_job_factory_
.SetProtocolHandler("blob",
166 new MockProtocolHandler(this));
167 url_request_context_
.set_job_factory(&url_request_job_factory_
);
170 virtual void TearDown() {
173 void SetUpFileSystem() {
174 // Prepare file system.
175 file_system_context_
= CreateFileSystemContextForTesting(
176 NULL
, temp_dir_
.path());
178 file_system_context_
->OpenFileSystem(
179 GURL(kFileSystemURLOrigin
),
181 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
182 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem
,
183 base::Unretained(this)));
184 base::RunLoop().RunUntilIdle();
185 ASSERT_TRUE(file_system_root_url_
.is_valid());
187 // Prepare files on file system.
188 const char kFilename1
[] = "FileSystemFile1.dat";
189 temp_file_system_file1_
= GetFileSystemURL(kFilename1
);
190 WriteFileSystemFile(kFilename1
, kTestFileSystemFileData1
,
191 arraysize(kTestFileSystemFileData1
) - 1,
192 &temp_file_system_file_modification_time1_
);
193 const char kFilename2
[] = "FileSystemFile2.dat";
194 temp_file_system_file2_
= GetFileSystemURL(kFilename2
);
195 WriteFileSystemFile(kFilename2
, kTestFileSystemFileData2
,
196 arraysize(kTestFileSystemFileData2
) - 1,
197 &temp_file_system_file_modification_time2_
);
200 GURL
GetFileSystemURL(const std::string
& filename
) {
201 return GURL(file_system_root_url_
.spec() + filename
);
204 void WriteFileSystemFile(const std::string
& filename
,
205 const char* buf
, int buf_size
,
206 base::Time
* modification_time
) {
207 fileapi::FileSystemURL url
=
208 file_system_context_
->CreateCrackedFileSystemURL(
209 GURL(kFileSystemURLOrigin
),
211 base::FilePath().AppendASCII(filename
));
213 ASSERT_EQ(base::File::FILE_OK
,
214 content::AsyncFileTestHelper::CreateFileWithData(
215 file_system_context_
, url
, buf
, buf_size
));
217 base::File::Info file_info
;
218 ASSERT_EQ(base::File::FILE_OK
,
219 content::AsyncFileTestHelper::GetMetadata(
220 file_system_context_
, url
, &file_info
));
221 if (modification_time
)
222 *modification_time
= file_info
.last_modified
;
225 void OnValidateFileSystem(const GURL
& root
,
226 const std::string
& name
,
227 base::File::Error result
) {
228 ASSERT_EQ(base::File::FILE_OK
, result
);
229 ASSERT_TRUE(root
.is_valid());
230 file_system_root_url_
= root
;
233 void TestSuccessNonrangeRequest(const std::string
& expected_response
,
234 int64 expected_content_length
) {
235 expected_status_code_
= 200;
236 expected_response_
= expected_response
;
237 TestRequest("GET", net::HttpRequestHeaders());
238 EXPECT_EQ(expected_content_length
,
239 request_
->response_headers()->GetContentLength());
242 void TestErrorRequest(int expected_status_code
) {
243 expected_status_code_
= expected_status_code
;
244 expected_response_
= "";
245 TestRequest("GET", net::HttpRequestHeaders());
248 void TestRequest(const std::string
& method
,
249 const net::HttpRequestHeaders
& extra_headers
) {
250 request_
= url_request_context_
.CreateRequest(
251 GURL("blob:blah"), net::DEFAULT_PRIORITY
, &url_request_delegate_
, NULL
);
252 request_
->set_method(method
);
253 if (!extra_headers
.IsEmpty())
254 request_
->SetExtraRequestHeaders(extra_headers
);
257 base::MessageLoop::current()->Run();
260 EXPECT_TRUE(request_
->status().is_success());
261 EXPECT_EQ(expected_status_code_
,
262 request_
->response_headers()->response_code());
263 EXPECT_EQ(expected_response_
, url_request_delegate_
.response_data());
266 void BuildComplicatedData(std::string
* expected_result
) {
267 blob_data_
->AppendData(kTestData1
+ 1, 2);
268 blob_data_
->AppendFile(temp_file1_
, 2, 3, temp_file_modification_time1_
);
269 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 3, 4,
270 temp_file_system_file_modification_time1_
);
271 blob_data_
->AppendData(kTestData2
+ 4, 5);
272 blob_data_
->AppendFile(temp_file2_
, 5, 6, temp_file_modification_time2_
);
273 blob_data_
->AppendFileSystemFile(temp_file_system_file2_
, 6, 7,
274 temp_file_system_file_modification_time2_
);
275 *expected_result
= std::string(kTestData1
+ 1, 2);
276 *expected_result
+= std::string(kTestFileData1
+ 2, 3);
277 *expected_result
+= std::string(kTestFileSystemFileData1
+ 3, 4);
278 *expected_result
+= std::string(kTestData2
+ 4, 5);
279 *expected_result
+= std::string(kTestFileData2
+ 5, 6);
280 *expected_result
+= std::string(kTestFileSystemFileData2
+ 6, 7);
283 // This only works if all the Blob items have a definite pre-computed length.
284 // Otherwise, this will fail a CHECK.
285 int64
GetTotalBlobLength() const {
287 const std::vector
<BlobData::Item
>& items
= blob_data_
->items();
288 for (std::vector
<BlobData::Item
>::const_iterator it
= items
.begin();
289 it
!= items
.end(); ++it
) {
290 int64 length
= base::checked_cast
<int64
>(it
->length());
291 CHECK(length
<= kint64max
- total
);
298 base::ScopedTempDir temp_dir_
;
299 base::FilePath temp_file1_
;
300 base::FilePath temp_file2_
;
301 base::Time temp_file_modification_time1_
;
302 base::Time temp_file_modification_time2_
;
303 GURL file_system_root_url_
;
304 GURL temp_file_system_file1_
;
305 GURL temp_file_system_file2_
;
306 base::Time temp_file_system_file_modification_time1_
;
307 base::Time temp_file_system_file_modification_time2_
;
309 base::MessageLoopForIO message_loop_
;
310 scoped_refptr
<fileapi::FileSystemContext
> file_system_context_
;
311 scoped_refptr
<BlobData
> blob_data_
;
312 net::URLRequestJobFactoryImpl url_request_job_factory_
;
313 net::URLRequestContext url_request_context_
;
314 MockURLRequestDelegate url_request_delegate_
;
315 scoped_ptr
<net::URLRequest
> request_
;
317 int expected_status_code_
;
318 std::string expected_response_
;
321 TEST_F(BlobURLRequestJobTest
, TestGetSimpleDataRequest
) {
322 blob_data_
->AppendData(kTestData1
);
323 TestSuccessNonrangeRequest(kTestData1
, arraysize(kTestData1
) - 1);
326 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileRequest
) {
327 blob_data_
->AppendFile(temp_file1_
, 0, -1, base::Time());
328 TestSuccessNonrangeRequest(kTestFileData1
, arraysize(kTestFileData1
) - 1);
331 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileRequest
) {
332 base::FilePath large_temp_file
=
333 temp_dir_
.path().AppendASCII("LargeBlob.dat");
334 std::string large_data
;
335 large_data
.reserve(kBufferSize
* 5);
336 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
337 large_data
.append(1, static_cast<char>(i
% 256));
338 ASSERT_EQ(static_cast<int>(large_data
.size()),
339 base::WriteFile(large_temp_file
, large_data
.data(),
341 blob_data_
->AppendFile(large_temp_file
, 0, -1, base::Time());
342 TestSuccessNonrangeRequest(large_data
, large_data
.size());
345 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileRequest
) {
346 base::FilePath non_existent_file
=
347 temp_file1_
.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
348 blob_data_
->AppendFile(non_existent_file
, 0, -1, base::Time());
349 TestErrorRequest(404);
352 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileRequest
) {
353 base::Time old_time
=
354 temp_file_modification_time1_
- base::TimeDelta::FromSeconds(10);
355 blob_data_
->AppendFile(temp_file1_
, 0, 3, old_time
);
356 TestErrorRequest(404);
359 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileRequest
) {
360 blob_data_
->AppendFile(temp_file1_
, 2, 4, temp_file_modification_time1_
);
361 std::string
result(kTestFileData1
+ 2, 4);
362 TestSuccessNonrangeRequest(result
, 4);
365 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileSystemFileRequest
) {
367 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, -1,
369 TestSuccessNonrangeRequest(kTestFileSystemFileData1
,
370 arraysize(kTestFileSystemFileData1
) - 1);
373 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileSystemFileRequest
) {
375 std::string large_data
;
376 large_data
.reserve(kBufferSize
* 5);
377 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
378 large_data
.append(1, static_cast<char>(i
% 256));
380 const char kFilename
[] = "LargeBlob.dat";
381 WriteFileSystemFile(kFilename
, large_data
.data(), large_data
.size(), NULL
);
383 blob_data_
->AppendFileSystemFile(GetFileSystemURL(kFilename
), 0, -1,
385 TestSuccessNonrangeRequest(large_data
, large_data
.size());
388 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileSystemFileRequest
) {
390 GURL non_existent_file
= GetFileSystemURL("non-existent.dat");
391 blob_data_
->AppendFileSystemFile(non_existent_file
, 0, -1, base::Time());
392 TestErrorRequest(404);
395 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileSystemFileRequest
) {
397 base::Time old_time
=
398 temp_file_system_file_modification_time1_
-
399 base::TimeDelta::FromSeconds(10);
400 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, 3, old_time
);
401 TestErrorRequest(404);
404 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileSystemFileRequest
) {
406 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 2, 4,
407 temp_file_system_file_modification_time1_
);
408 std::string
result(kTestFileSystemFileData1
+ 2, 4);
409 TestSuccessNonrangeRequest(result
, 4);
412 TEST_F(BlobURLRequestJobTest
, TestGetComplicatedDataAndFileRequest
) {
415 BuildComplicatedData(&result
);
416 TestSuccessNonrangeRequest(result
, GetTotalBlobLength());
419 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest1
) {
422 BuildComplicatedData(&result
);
423 net::HttpRequestHeaders extra_headers
;
424 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
425 net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
426 expected_status_code_
= 206;
427 expected_response_
= result
.substr(5, 10 - 5 + 1);
428 TestRequest("GET", extra_headers
);
430 EXPECT_EQ(6, request_
->response_headers()->GetContentLength());
432 int64 first
= 0, last
= 0, length
= 0;
434 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
437 EXPECT_EQ(GetTotalBlobLength(), length
);
440 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest2
) {
443 BuildComplicatedData(&result
);
444 net::HttpRequestHeaders extra_headers
;
445 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
446 net::HttpByteRange::Suffix(10).GetHeaderValue());
447 expected_status_code_
= 206;
448 expected_response_
= result
.substr(result
.length() - 10);
449 TestRequest("GET", extra_headers
);
451 EXPECT_EQ(10, request_
->response_headers()->GetContentLength());
453 int64 total
= GetTotalBlobLength();
454 int64 first
= 0, last
= 0, length
= 0;
456 request_
->response_headers()->GetContentRange(&first
, &last
, &length
));
457 EXPECT_EQ(total
- 10, first
);
458 EXPECT_EQ(total
- 1, last
);
459 EXPECT_EQ(total
, length
);
462 TEST_F(BlobURLRequestJobTest
, TestExtraHeaders
) {
463 blob_data_
->set_content_type(kTestContentType
);
464 blob_data_
->set_content_disposition(kTestContentDisposition
);
465 blob_data_
->AppendData(kTestData1
);
466 expected_status_code_
= 200;
467 expected_response_
= kTestData1
;
468 TestRequest("GET", net::HttpRequestHeaders());
470 std::string content_type
;
471 EXPECT_TRUE(request_
->response_headers()->GetMimeType(&content_type
));
472 EXPECT_EQ(kTestContentType
, content_type
);
474 std::string content_disposition
;
475 EXPECT_TRUE(request_
->response_headers()->EnumerateHeader(
476 &iter
, "Content-Disposition", &content_disposition
));
477 EXPECT_EQ(kTestContentDisposition
, content_disposition
);
480 } // namespace content