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 "content/browser/loader/upload_data_stream_builder.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/run_loop.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "content/common/resource_request_body.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/base/upload_bytes_element_reader.h"
20 #include "net/base/upload_data_stream.h"
21 #include "net/base/upload_disk_cache_entry_element_reader.h"
22 #include "net/base/upload_file_element_reader.h"
23 #include "net/disk_cache/disk_cache.h"
24 #include "storage/browser/blob/blob_data_builder.h"
25 #include "storage/browser/blob/blob_storage_context.h"
26 #include "testing/gtest/include/gtest/gtest.h"
29 using storage::BlobDataBuilder
;
30 using storage::BlobDataHandle
;
31 using storage::BlobStorageContext
;
36 const int kTestDiskCacheStreamIndex
= 0;
38 // Our disk cache tests don't need a real data handle since the tests themselves
39 // scope the disk cache and entries.
40 class EmptyDataHandle
: public storage::BlobDataBuilder::DataHandle
{
42 ~EmptyDataHandle() override
{}
45 scoped_ptr
<disk_cache::Backend
> CreateInMemoryDiskCache() {
46 scoped_ptr
<disk_cache::Backend
> cache
;
47 net::TestCompletionCallback callback
;
48 int rv
= disk_cache::CreateCacheBackend(net::MEMORY_CACHE
,
49 net::CACHE_BACKEND_DEFAULT
,
51 false, nullptr, nullptr, &cache
,
53 EXPECT_EQ(net::OK
, callback
.GetResult(rv
));
58 disk_cache::ScopedEntryPtr
CreateDiskCacheEntry(disk_cache::Backend
* cache
,
60 const std::string
& data
) {
61 disk_cache::Entry
* temp_entry
= nullptr;
62 net::TestCompletionCallback callback
;
63 int rv
= cache
->CreateEntry(key
, &temp_entry
, callback
.callback());
64 if (callback
.GetResult(rv
) != net::OK
)
66 disk_cache::ScopedEntryPtr
entry(temp_entry
);
68 scoped_refptr
<net::StringIOBuffer
> iobuffer
= new net::StringIOBuffer(data
);
69 rv
= entry
->WriteData(kTestDiskCacheStreamIndex
, 0, iobuffer
.get(),
70 iobuffer
->size(), callback
.callback(), false);
71 EXPECT_EQ(static_cast<int>(data
.size()), callback
.GetResult(rv
));
75 bool AreElementsEqual(const net::UploadElementReader
& reader
,
76 const ResourceRequestBody::Element
& element
) {
77 switch(element
.type()) {
78 case ResourceRequestBody::Element::TYPE_BYTES
: {
79 const net::UploadBytesElementReader
* bytes_reader
=
80 reader
.AsBytesReader();
81 return bytes_reader
&&
82 element
.length() == bytes_reader
->length() &&
83 std::equal(element
.bytes(), element
.bytes() + element
.length(),
84 bytes_reader
->bytes());
86 case ResourceRequestBody::Element::TYPE_FILE
: {
87 const net::UploadFileElementReader
* file_reader
= reader
.AsFileReader();
89 file_reader
->path() == element
.path() &&
90 file_reader
->range_offset() == element
.offset() &&
91 file_reader
->range_length() == element
.length() &&
92 file_reader
->expected_modification_time() ==
93 element
.expected_modification_time();
96 case ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY
: {
97 // TODO(gavinp): Should we be comparing a higher level structure
98 // such as the BlobDataItem so that we can do stronger equality
100 const net::UploadDiskCacheEntryElementReader
* disk_cache_entry_reader
=
101 reader
.AsDiskCacheEntryReaderForTests();
102 return disk_cache_entry_reader
&&
103 disk_cache_entry_reader
->range_offset_for_tests() ==
104 static_cast<int>(element
.offset()) &&
105 disk_cache_entry_reader
->range_length_for_tests() ==
106 static_cast<int>(element
.length());
117 TEST(UploadDataStreamBuilderTest
, CreateUploadDataStreamWithoutBlob
) {
118 base::MessageLoop message_loop
;
119 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
121 const char kData
[] = "123";
122 const base::FilePath::StringType kFilePath
= FILE_PATH_LITERAL("abc");
123 const uint64 kFileOffset
= 10U;
124 const uint64 kFileLength
= 100U;
125 const base::Time kFileTime
= base::Time::FromDoubleT(999);
126 const int64 kIdentifier
= 12345;
128 request_body
->AppendBytes(kData
, arraysize(kData
) - 1);
129 request_body
->AppendFileRange(base::FilePath(kFilePath
),
130 kFileOffset
, kFileLength
, kFileTime
);
131 request_body
->set_identifier(kIdentifier
);
133 scoped_ptr
<net::UploadDataStream
> upload(UploadDataStreamBuilder::Build(
134 request_body
.get(), NULL
, NULL
,
135 base::ThreadTaskRunnerHandle::Get().get()));
137 EXPECT_EQ(kIdentifier
, upload
->identifier());
138 ASSERT_TRUE(upload
->GetElementReaders());
139 ASSERT_EQ(request_body
->elements()->size(),
140 upload
->GetElementReaders()->size());
142 const net::UploadBytesElementReader
* r1
=
143 (*upload
->GetElementReaders())[0]->AsBytesReader();
145 EXPECT_EQ(kData
, std::string(r1
->bytes(), r1
->length()));
147 const net::UploadFileElementReader
* r2
=
148 (*upload
->GetElementReaders())[1]->AsFileReader();
150 EXPECT_EQ(kFilePath
, r2
->path().value());
151 EXPECT_EQ(kFileOffset
, r2
->range_offset());
152 EXPECT_EQ(kFileLength
, r2
->range_length());
153 EXPECT_EQ(kFileTime
, r2
->expected_modification_time());
156 TEST(UploadDataStreamBuilderTest
, ResolveBlobAndCreateUploadDataStream
) {
157 base::MessageLoop message_loop
;
159 // Setup blob data for testing.
160 base::Time time1
, time2
;
161 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1
);
162 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2
);
164 BlobStorageContext blob_storage_context
;
166 const std::string
blob_id0("id-0");
167 scoped_ptr
<BlobDataBuilder
> blob_data_builder(
168 new BlobDataBuilder(blob_id0
));
169 scoped_ptr
<BlobDataHandle
> handle1
=
170 blob_storage_context
.AddFinishedBlob(blob_data_builder
.get());
172 const std::string
blob_id1("id-1");
173 const std::string kBlobData
= "BlobData";
174 blob_data_builder
.reset(new BlobDataBuilder(blob_id1
));
175 blob_data_builder
->AppendData(kBlobData
);
176 blob_data_builder
->AppendFile(
177 base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1
);
178 scoped_ptr
<BlobDataHandle
> handle2
=
179 blob_storage_context
.AddFinishedBlob(blob_data_builder
.get());
181 const std::string
blob_id2("id-2");
182 const std::string kDiskCacheData
= "DiskCacheData";
183 scoped_ptr
<disk_cache::Backend
> disk_cache_backend
=
184 CreateInMemoryDiskCache();
185 ASSERT_TRUE(disk_cache_backend
);
186 disk_cache::ScopedEntryPtr disk_cache_entry
=
187 CreateDiskCacheEntry(disk_cache_backend
.get(), "a key", kDiskCacheData
);
188 ASSERT_TRUE(disk_cache_entry
);
189 blob_data_builder
.reset(new BlobDataBuilder(blob_id2
));
190 blob_data_builder
->AppendDiskCacheEntry(
191 new EmptyDataHandle(), disk_cache_entry
.get(),
192 kTestDiskCacheStreamIndex
);
193 scoped_ptr
<BlobDataHandle
> handle3
=
194 blob_storage_context
.AddFinishedBlob(blob_data_builder
.get());
196 // Setup upload data elements for comparison.
197 ResourceRequestBody::Element blob_element1
, blob_element2
, blob_element3
;
198 blob_element1
.SetToBytes(kBlobData
.c_str(), kBlobData
.size());
199 blob_element2
.SetToFilePathRange(
200 base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1
);
201 blob_element3
.SetToDiskCacheEntryRange(0, kDiskCacheData
.size());
203 ResourceRequestBody::Element upload_element1
, upload_element2
;
204 upload_element1
.SetToBytes("Hello", 5);
205 upload_element2
.SetToFilePathRange(
206 base::FilePath(FILE_PATH_LITERAL("foo1.txt")), 0, 20, time2
);
208 // Test no blob reference.
209 scoped_refptr
<ResourceRequestBody
> request_body(new ResourceRequestBody());
210 request_body
->AppendBytes(
211 upload_element1
.bytes(),
212 upload_element1
.length());
213 request_body
->AppendFileRange(
214 upload_element2
.path(),
215 upload_element2
.offset(),
216 upload_element2
.length(),
217 upload_element2
.expected_modification_time());
219 scoped_ptr
<net::UploadDataStream
> upload(UploadDataStreamBuilder::Build(
220 request_body
.get(), &blob_storage_context
, NULL
,
221 base::ThreadTaskRunnerHandle::Get().get()));
223 ASSERT_TRUE(upload
->GetElementReaders());
224 ASSERT_EQ(2U, upload
->GetElementReaders()->size());
225 EXPECT_TRUE(AreElementsEqual(
226 *(*upload
->GetElementReaders())[0], upload_element1
));
227 EXPECT_TRUE(AreElementsEqual(
228 *(*upload
->GetElementReaders())[1], upload_element2
));
230 // Test having only one blob reference that refers to empty blob data.
231 request_body
= new ResourceRequestBody();
232 request_body
->AppendBlob(blob_id0
);
234 upload
= UploadDataStreamBuilder::Build(
235 request_body
.get(), &blob_storage_context
, NULL
,
236 base::ThreadTaskRunnerHandle::Get().get());
237 ASSERT_TRUE(upload
->GetElementReaders());
238 ASSERT_EQ(0U, upload
->GetElementReaders()->size());
240 // Test having only one blob reference.
241 request_body
= new ResourceRequestBody();
242 request_body
->AppendBlob(blob_id1
);
244 upload
= UploadDataStreamBuilder::Build(
245 request_body
.get(), &blob_storage_context
, NULL
,
246 base::ThreadTaskRunnerHandle::Get().get());
247 ASSERT_TRUE(upload
->GetElementReaders());
248 ASSERT_EQ(2U, upload
->GetElementReaders()->size());
249 EXPECT_TRUE(AreElementsEqual(
250 *(*upload
->GetElementReaders())[0], blob_element1
));
251 EXPECT_TRUE(AreElementsEqual(
252 *(*upload
->GetElementReaders())[1], blob_element2
));
254 // Test having one blob reference which refers to a disk cache entry.
255 request_body
= new ResourceRequestBody();
256 request_body
->AppendBlob(blob_id2
);
258 upload
= UploadDataStreamBuilder::Build(
259 request_body
.get(), &blob_storage_context
, nullptr,
260 base::ThreadTaskRunnerHandle::Get().get());
261 ASSERT_TRUE(upload
->GetElementReaders());
262 ASSERT_EQ(1U, upload
->GetElementReaders()->size());
263 EXPECT_TRUE(AreElementsEqual(
264 *(*upload
->GetElementReaders())[0], blob_element3
));
266 // Test having one blob reference at the beginning.
267 request_body
= new ResourceRequestBody();
268 request_body
->AppendBlob(blob_id1
);
269 request_body
->AppendBytes(
270 upload_element1
.bytes(),
271 upload_element1
.length());
272 request_body
->AppendFileRange(
273 upload_element2
.path(),
274 upload_element2
.offset(),
275 upload_element2
.length(),
276 upload_element2
.expected_modification_time());
278 upload
= UploadDataStreamBuilder::Build(
279 request_body
.get(), &blob_storage_context
, NULL
,
280 base::ThreadTaskRunnerHandle::Get().get());
281 ASSERT_TRUE(upload
->GetElementReaders());
282 ASSERT_EQ(4U, upload
->GetElementReaders()->size());
283 EXPECT_TRUE(AreElementsEqual(
284 *(*upload
->GetElementReaders())[0], blob_element1
));
285 EXPECT_TRUE(AreElementsEqual(
286 *(*upload
->GetElementReaders())[1], blob_element2
));
287 EXPECT_TRUE(AreElementsEqual(
288 *(*upload
->GetElementReaders())[2], upload_element1
));
289 EXPECT_TRUE(AreElementsEqual(
290 *(*upload
->GetElementReaders())[3], upload_element2
));
292 // Test having one blob reference at the end.
293 request_body
= new ResourceRequestBody();
294 request_body
->AppendBytes(
295 upload_element1
.bytes(),
296 upload_element1
.length());
297 request_body
->AppendFileRange(
298 upload_element2
.path(),
299 upload_element2
.offset(),
300 upload_element2
.length(),
301 upload_element2
.expected_modification_time());
302 request_body
->AppendBlob(blob_id1
);
304 upload
= UploadDataStreamBuilder::Build(
305 request_body
.get(), &blob_storage_context
, NULL
,
306 base::ThreadTaskRunnerHandle::Get().get());
307 ASSERT_TRUE(upload
->GetElementReaders());
308 ASSERT_EQ(4U, upload
->GetElementReaders()->size());
309 EXPECT_TRUE(AreElementsEqual(
310 *(*upload
->GetElementReaders())[0], upload_element1
));
311 EXPECT_TRUE(AreElementsEqual(
312 *(*upload
->GetElementReaders())[1], upload_element2
));
313 EXPECT_TRUE(AreElementsEqual(
314 *(*upload
->GetElementReaders())[2], blob_element1
));
315 EXPECT_TRUE(AreElementsEqual(
316 *(*upload
->GetElementReaders())[3], blob_element2
));
318 // Test having one blob reference in the middle.
319 request_body
= new ResourceRequestBody();
320 request_body
->AppendBytes(
321 upload_element1
.bytes(),
322 upload_element1
.length());
323 request_body
->AppendBlob(blob_id1
);
324 request_body
->AppendFileRange(
325 upload_element2
.path(),
326 upload_element2
.offset(),
327 upload_element2
.length(),
328 upload_element2
.expected_modification_time());
330 upload
= UploadDataStreamBuilder::Build(
331 request_body
.get(), &blob_storage_context
, NULL
,
332 base::ThreadTaskRunnerHandle::Get().get());
333 ASSERT_TRUE(upload
->GetElementReaders());
334 ASSERT_EQ(4U, upload
->GetElementReaders()->size());
335 EXPECT_TRUE(AreElementsEqual(
336 *(*upload
->GetElementReaders())[0], upload_element1
));
337 EXPECT_TRUE(AreElementsEqual(
338 *(*upload
->GetElementReaders())[1], blob_element1
));
339 EXPECT_TRUE(AreElementsEqual(
340 *(*upload
->GetElementReaders())[2], blob_element2
));
341 EXPECT_TRUE(AreElementsEqual(
342 *(*upload
->GetElementReaders())[3], upload_element2
));
344 // Test having multiple blob references.
345 request_body
= new ResourceRequestBody();
346 request_body
->AppendBlob(blob_id1
);
347 request_body
->AppendBytes(
348 upload_element1
.bytes(),
349 upload_element1
.length());
350 request_body
->AppendBlob(blob_id1
);
351 request_body
->AppendBlob(blob_id1
);
352 request_body
->AppendFileRange(
353 upload_element2
.path(),
354 upload_element2
.offset(),
355 upload_element2
.length(),
356 upload_element2
.expected_modification_time());
358 upload
= UploadDataStreamBuilder::Build(
359 request_body
.get(), &blob_storage_context
, NULL
,
360 base::ThreadTaskRunnerHandle::Get().get());
361 ASSERT_TRUE(upload
->GetElementReaders());
362 ASSERT_EQ(8U, upload
->GetElementReaders()->size());
363 EXPECT_TRUE(AreElementsEqual(
364 *(*upload
->GetElementReaders())[0], blob_element1
));
365 EXPECT_TRUE(AreElementsEqual(
366 *(*upload
->GetElementReaders())[1], blob_element2
));
367 EXPECT_TRUE(AreElementsEqual(
368 *(*upload
->GetElementReaders())[2], upload_element1
));
369 EXPECT_TRUE(AreElementsEqual(
370 *(*upload
->GetElementReaders())[3], blob_element1
));
371 EXPECT_TRUE(AreElementsEqual(
372 *(*upload
->GetElementReaders())[4], blob_element2
));
373 EXPECT_TRUE(AreElementsEqual(
374 *(*upload
->GetElementReaders())[5], blob_element1
));
375 EXPECT_TRUE(AreElementsEqual(
376 *(*upload
->GetElementReaders())[6], blob_element2
));
377 EXPECT_TRUE(AreElementsEqual(
378 *(*upload
->GetElementReaders())[7], upload_element2
));
380 // Clean up for ASAN.
381 base::RunLoop().RunUntilIdle();
384 TEST(UploadDataStreamBuilderTest
,
385 WriteUploadDataStreamWithEmptyFileBackedBlob
) {
386 base::MessageLoopForIO message_loop
;
388 base::FilePath test_blob_path
;
389 ASSERT_TRUE(base::CreateTemporaryFile(&test_blob_path
));
391 const uint64_t kZeroLength
= 0;
392 base::Time blob_time
;
393 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &blob_time
);
394 ASSERT_TRUE(base::TouchFile(test_blob_path
, blob_time
, blob_time
));
396 BlobStorageContext blob_storage_context
;
398 // A blob created from an empty file added several times.
399 const std::string
blob_id("id-0");
400 scoped_ptr
<BlobDataBuilder
> blob_data_builder(new BlobDataBuilder(blob_id
));
401 blob_data_builder
->AppendFile(test_blob_path
, 0, kZeroLength
, blob_time
);
402 scoped_ptr
<BlobDataHandle
> handle
=
403 blob_storage_context
.AddFinishedBlob(blob_data_builder
.get());
405 ResourceRequestBody::Element blob_element
;
406 blob_element
.SetToFilePathRange(test_blob_path
, 0, kZeroLength
, blob_time
);
408 scoped_refptr
<ResourceRequestBody
> request_body(new ResourceRequestBody());
409 scoped_ptr
<net::UploadDataStream
> upload(UploadDataStreamBuilder::Build(
410 request_body
.get(), &blob_storage_context
, NULL
,
411 base::ThreadTaskRunnerHandle::Get().get()));
413 request_body
= new ResourceRequestBody();
414 request_body
->AppendBlob(blob_id
);
415 request_body
->AppendBlob(blob_id
);
416 request_body
->AppendBlob(blob_id
);
418 upload
= UploadDataStreamBuilder::Build(
419 request_body
.get(), &blob_storage_context
, NULL
,
420 base::ThreadTaskRunnerHandle::Get().get());
421 ASSERT_TRUE(upload
->GetElementReaders());
422 const auto& readers
= *upload
->GetElementReaders();
423 ASSERT_EQ(3U, readers
.size());
424 EXPECT_TRUE(AreElementsEqual(*readers
[0], blob_element
));
425 EXPECT_TRUE(AreElementsEqual(*readers
[1], blob_element
));
426 EXPECT_TRUE(AreElementsEqual(*readers
[2], blob_element
));
428 net::TestCompletionCallback init_callback
;
429 ASSERT_EQ(net::ERR_IO_PENDING
, upload
->Init(init_callback
.callback()));
430 EXPECT_EQ(net::OK
, init_callback
.WaitForResult());
432 EXPECT_EQ(kZeroLength
, upload
->size());
434 // Purposely (try to) read more than what is in the stream. If we try to
435 // read zero bytes then UploadDataStream::Read will fail a DCHECK.
436 int kBufferLength
= kZeroLength
+ 1;
437 scoped_ptr
<char[]> buffer(new char[kBufferLength
]);
438 scoped_refptr
<net::IOBuffer
> io_buffer
=
439 new net::WrappedIOBuffer(buffer
.get());
440 net::TestCompletionCallback read_callback
;
442 upload
->Read(io_buffer
.get(), kBufferLength
, read_callback
.callback());
443 EXPECT_EQ(static_cast<int>(kZeroLength
), read_callback
.GetResult(result
));
445 base::DeleteFile(test_blob_path
, false);
447 // Clean up for ASAN.
448 base::RunLoop().RunUntilIdle();
450 } // namespace content