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/logging.h"
11 #include "base/memory/ref_counted.h"
12 #include "content/browser/fileapi/upload_file_system_file_element_reader.h"
13 #include "content/common/resource_request_body.h"
14 #include "net/base/elements_upload_data_stream.h"
15 #include "net/base/upload_bytes_element_reader.h"
16 #include "net/base/upload_disk_cache_entry_element_reader.h"
17 #include "net/base/upload_file_element_reader.h"
18 #include "storage/browser/blob/blob_data_handle.h"
19 #include "storage/browser/blob/blob_data_snapshot.h"
20 #include "storage/browser/blob/blob_storage_context.h"
22 namespace disk_cache
{
29 // A subclass of net::UploadBytesElementReader which owns ResourceRequestBody.
30 class BytesElementReader
: public net::UploadBytesElementReader
{
32 BytesElementReader(ResourceRequestBody
* resource_request_body
,
33 const ResourceRequestBody::Element
& element
)
34 : net::UploadBytesElementReader(element
.bytes(), element
.length()),
35 resource_request_body_(resource_request_body
) {
36 DCHECK_EQ(ResourceRequestBody::Element::TYPE_BYTES
, element
.type());
39 ~BytesElementReader() override
{}
42 scoped_refptr
<ResourceRequestBody
> resource_request_body_
;
44 DISALLOW_COPY_AND_ASSIGN(BytesElementReader
);
47 // A subclass of net::UploadFileElementReader which owns ResourceRequestBody.
48 // This class is necessary to ensure the BlobData and any attached shareable
49 // files survive until upload completion.
50 class FileElementReader
: public net::UploadFileElementReader
{
52 FileElementReader(ResourceRequestBody
* resource_request_body
,
53 base::TaskRunner
* task_runner
,
54 const ResourceRequestBody::Element
& element
)
55 : net::UploadFileElementReader(task_runner
,
59 element
.expected_modification_time()),
60 resource_request_body_(resource_request_body
) {
61 DCHECK_EQ(ResourceRequestBody::Element::TYPE_FILE
, element
.type());
64 ~FileElementReader() override
{}
67 scoped_refptr
<ResourceRequestBody
> resource_request_body_
;
69 DISALLOW_COPY_AND_ASSIGN(FileElementReader
);
72 // This owns the provided ResourceRequestBody. This is necessary to ensure the
73 // BlobData and open disk cache entries survive until upload completion.
74 class DiskCacheElementReader
: public net::UploadDiskCacheEntryElementReader
{
76 DiskCacheElementReader(ResourceRequestBody
* resource_request_body
,
77 disk_cache::Entry
* disk_cache_entry
,
78 int disk_cache_stream_index
,
79 const ResourceRequestBody::Element
& element
)
80 : net::UploadDiskCacheEntryElementReader(disk_cache_entry
,
81 disk_cache_stream_index
,
84 resource_request_body_(resource_request_body
) {
85 DCHECK_EQ(ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY
,
89 ~DiskCacheElementReader() override
{}
92 scoped_refptr
<ResourceRequestBody
> resource_request_body_
;
94 DISALLOW_COPY_AND_ASSIGN(DiskCacheElementReader
);
97 void ResolveBlobReference(
98 ResourceRequestBody
* body
,
99 storage::BlobStorageContext
* blob_context
,
100 const ResourceRequestBody::Element
& element
,
101 std::vector
<std::pair
<const ResourceRequestBody::Element
*,
102 const storage::BlobDataItem
*>>* resolved_elements
) {
103 DCHECK(blob_context
);
104 scoped_ptr
<storage::BlobDataHandle
> handle
=
105 blob_context
->GetBlobDataFromUUID(element
.blob_uuid());
110 // TODO(dmurph): Create a reader for blobs instead of decomposing the blob
111 // and storing the snapshot on the request to keep the resources around.
112 // Currently a handle is attached to the request in the resource dispatcher
113 // host, so we know the blob won't go away, but it's not very clear or useful.
114 scoped_ptr
<storage::BlobDataSnapshot
> snapshot
= handle
->CreateSnapshot();
115 // If there is no element in the referred blob data, just return.
116 if (snapshot
->items().empty())
119 // Append the elements in the referenced blob data.
120 for (const auto& item
: snapshot
->items()) {
121 DCHECK_NE(storage::DataElement::TYPE_BLOB
, item
->type());
122 resolved_elements
->push_back(
123 std::make_pair(item
->data_element_ptr(), item
.get()));
125 const void* key
= snapshot
.get();
126 body
->SetUserData(key
, snapshot
.release());
131 scoped_ptr
<net::UploadDataStream
> UploadDataStreamBuilder::Build(
132 ResourceRequestBody
* body
,
133 storage::BlobStorageContext
* blob_context
,
134 storage::FileSystemContext
* file_system_context
,
135 base::TaskRunner
* file_task_runner
) {
136 // Resolve all blob elements.
137 std::vector
<std::pair
<const ResourceRequestBody::Element
*,
138 const storage::BlobDataItem
*>> resolved_elements
;
139 for (size_t i
= 0; i
< body
->elements()->size(); ++i
) {
140 const ResourceRequestBody::Element
& element
= (*body
->elements())[i
];
141 if (element
.type() == ResourceRequestBody::Element::TYPE_BLOB
) {
142 ResolveBlobReference(body
, blob_context
, element
, &resolved_elements
);
143 } else if (element
.type() !=
144 ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY
) {
145 resolved_elements
.push_back(std::make_pair(&element
, nullptr));
151 ScopedVector
<net::UploadElementReader
> element_readers
;
152 for (const auto& element_and_blob_item_pair
: resolved_elements
) {
153 const ResourceRequestBody::Element
& element
=
154 *element_and_blob_item_pair
.first
;
155 switch (element
.type()) {
156 case ResourceRequestBody::Element::TYPE_BYTES
:
157 element_readers
.push_back(new BytesElementReader(body
, element
));
159 case ResourceRequestBody::Element::TYPE_FILE
:
160 element_readers
.push_back(
161 new FileElementReader(body
, file_task_runner
, element
));
163 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM
:
164 // If |body| contains any filesystem URLs, the caller should have
165 // supplied a FileSystemContext.
166 DCHECK(file_system_context
);
167 element_readers
.push_back(
168 new content::UploadFileSystemFileElementReader(
170 element
.filesystem_url(),
173 element
.expected_modification_time()));
175 case ResourceRequestBody::Element::TYPE_BLOB
:
176 // Blob elements should be resolved beforehand.
177 // TODO(dmurph): Create blob reader and store the snapshot in there.
180 case ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY
: {
181 // TODO(gavinp): If Build() is called with a DataElement of
182 // TYPE_DISK_CACHE_ENTRY then this code won't work because we won't call
183 // ResolveBlobReference() and so we won't find |item|. Is this OK?
184 const storage::BlobDataItem
* item
= element_and_blob_item_pair
.second
;
185 element_readers
.push_back(
186 new DiskCacheElementReader(body
, item
->disk_cache_entry(),
187 item
->disk_cache_stream_index(),
191 case ResourceRequestBody::Element::TYPE_UNKNOWN
:
197 return make_scoped_ptr(
198 new net::ElementsUploadDataStream(element_readers
.Pass(),
199 body
->identifier()));
202 } // namespace content