Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(534)

Side by Side Diff: content/browser/blob_storage/blob_url_loader_factory.cc

Issue 2906543002: Add support for reading blobs when using the network service. (Closed)
Patch Set: review comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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.
4
5 #include "content/browser/blob_storage/blob_url_loader_factory.h"
6
7 #include <stddef.h>
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
14 #include "content/browser/storage_partition_impl.h"
15 #include "content/common/net_adapters.h"
16 #include "content/common/url_loader.mojom.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "mojo/public/cpp/system/simple_watcher.h"
19 #include "net/base/io_buffer.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/http/http_status_code.h"
24 #include "net/http/http_util.h"
25 #include "storage/browser/blob/blob_data_handle.h"
26 #include "storage/browser/blob/blob_reader.h"
27 #include "storage/browser/blob/blob_storage_context.h"
28 #include "storage/browser/blob/blob_url_request_job.h"
29 #include "storage/browser/fileapi/file_system_context.h"
30
31 namespace content {
32
33 namespace {
34 constexpr size_t kDefaultAllocationSize = 512 * 1024;
35
36 // Note: some of this code is duplicated from storage::BlobURLRequestJob.
kinuko 2017/05/26 09:48:12 It feels we could share some more implementation w
jam 2017/05/26 14:14:53 There are some scattered lines of code that are sh
kinuko 2017/05/29 04:27:15 I wondered we might be able to have some static he
37 class BlobURLLoader : public mojom::URLLoader {
38 public:
39 BlobURLLoader(mojom::URLLoaderAssociatedRequest url_loader_request,
40 const ResourceRequest& request,
41 mojom::URLLoaderClientPtr client,
42 storage::BlobStorageContext* blob_storage_context,
43 storage::FileSystemContext* file_system_context)
44 : binding_(this, std::move(url_loader_request)),
45 request_(request),
46 client_(std::move(client)),
47 byte_range_set_(false),
48 writable_handle_watcher_(FROM_HERE,
49 mojo::SimpleWatcher::ArmingPolicy::MANUAL),
50 peer_closed_handle_watcher_(FROM_HERE,
51 mojo::SimpleWatcher::ArmingPolicy::MANUAL),
52 weak_factory_(this) {
53 DCHECK_CURRENTLY_ON(BrowserThread::IO);
54 blob_handle_ = blob_storage_context->GetBlobDataFromPublicURL(request.url);
55
56 // PostTask since it might destruct.
57 base::ThreadTaskRunnerHandle::Get()->PostTask(
58 FROM_HERE,
59 base::Bind(&BlobURLLoader::Start, weak_factory_.GetWeakPtr(), request,
60 make_scoped_refptr(file_system_context)));
61 }
62
63 void Start(const ResourceRequest& request,
64 scoped_refptr<storage::FileSystemContext> file_system_context) {
65 if (!blob_handle_) {
66 NotifyCompleted(net::ERR_FILE_NOT_FOUND);
67 return;
68 }
69
70 base::SequencedTaskRunner* file_task_runner =
71 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get();
72 blob_reader_ =
73 blob_handle_->CreateReader(file_system_context.get(), file_task_runner);
74
75 // We only support GET request per the spec.
76 if (request.method != "GET") {
77 NotifyCompleted(net::ERR_METHOD_NOT_SUPPORTED);
78 return;
79 }
80
81 if (blob_reader_->net_error()) {
82 NotifyCompleted(blob_reader_->net_error());
83 return;
84 }
85
86 net::HttpRequestHeaders request_headers;
87 request_headers.AddHeadersFromString(request.headers);
88 std::string range_header;
89 if (request_headers.GetHeader(net::HttpRequestHeaders::kRange,
90 &range_header)) {
91 // We only care about "Range" header here.
92 std::vector<net::HttpByteRange> ranges;
93 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
94 if (ranges.size() == 1) {
95 byte_range_set_ = true;
96 byte_range_ = ranges[0];
97 } else {
98 // We don't support multiple range requests in one single URL request,
99 // because we need to do multipart encoding here.
100 // TODO(jianli): Support multipart byte range requests.
101 NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
102 }
103 }
104 }
105
106 storage::BlobReader::Status size_status =
107 blob_reader_->CalculateSize(base::Bind(&BlobURLLoader::DidCalculateSize,
108 weak_factory_.GetWeakPtr()));
109 switch (size_status) {
110 case storage::BlobReader::Status::NET_ERROR:
111 NotifyCompleted(blob_reader_->net_error());
112 return;
113 case storage::BlobReader::Status::IO_PENDING:
114 return;
115 case storage::BlobReader::Status::DONE:
116 DidCalculateSize(net::OK);
117 return;
118 }
119 }
120
121 ~BlobURLLoader() override {}
122
123 private:
124 // mojom::URLLoader implementation:
125 void FollowRedirect() override { NOTREACHED(); }
126
127 void SetPriority(net::RequestPriority priority,
128 int32_t intra_priority_value) override {
129 NOTREACHED();
130 }
131
132 void NotifyCompleted(int error_code) {
133 ResourceRequestCompletionStatus request_complete_data;
134 request_complete_data.error_code = error_code;
135 client_->OnComplete(request_complete_data);
136
137 DeleteIfNeeded();
138 }
139
140 void DidCalculateSize(int result) {
141 if (result != net::OK) {
142 NotifyCompleted(result);
143 return;
144 }
145
146 // Apply the range requirement.
147 if (!byte_range_.ComputeBounds(blob_reader_->total_size())) {
148 NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
149 return;
150 }
151
152 DCHECK_LE(byte_range_.first_byte_position(),
153 byte_range_.last_byte_position() + 1);
154 uint64_t length =
155 base::checked_cast<uint64_t>(byte_range_.last_byte_position() -
156 byte_range_.first_byte_position() + 1);
157
158 if (byte_range_set_)
159 blob_reader_->SetReadRange(byte_range_.first_byte_position(), length);
160
161 net::HttpStatusCode status_code = net::HTTP_OK;
162 if (byte_range_set_ && byte_range_.IsValid()) {
163 status_code = net::HTTP_PARTIAL_CONTENT;
164 } else {
165 // TODO(horo): When the requester doesn't need the side data
166 // (ex:FileReader) we should skip reading the side data.
167 if (blob_reader_->has_side_data() &&
168 blob_reader_->ReadSideData(base::Bind(&BlobURLLoader::DidReadMetadata,
169 weak_factory_.GetWeakPtr())) ==
170 storage::BlobReader::Status::IO_PENDING) {
171 return;
172 }
173 }
174
175 HeadersCompleted(status_code);
176 }
177
178 void DidReadMetadata(storage::BlobReader::Status result) {
179 if (result != storage::BlobReader::Status::DONE) {
180 NotifyCompleted(blob_reader_->net_error());
181 return;
182 }
183 HeadersCompleted(net::HTTP_OK);
184 }
185
186 void HeadersCompleted(net::HttpStatusCode status_code) {
187 ResourceResponseHead response;
188 response.content_length = 0;
189 response.headers = storage::BlobURLRequestJob::GenerateHeaders(
190 status_code, blob_handle_.get(), blob_reader_.get(), &byte_range_,
191 &response.content_length);
192
193 std::string mime_type;
194 response.headers->GetMimeType(&mime_type);
195 // Match logic in StreamURLRequestJob::HeadersCompleted.
196 if (mime_type.empty())
197 mime_type = "text/plain";
198 response.mime_type = mime_type;
199
200 // TODO(jam): some of this code can be shared with
201 // content/network/url_loader_impl.h
202 client_->OnReceiveResponse(response, base::nullopt, nullptr);
203
204 net::IOBufferWithSize* metadata = blob_reader_->side_data();
205 if (metadata) {
206 const uint8_t* data = reinterpret_cast<const uint8_t*>(metadata->data());
207 client_->OnReceiveCachedMetadata(
208 std::vector<uint8_t>(data, data + metadata->size()));
209 }
210
211 mojo::DataPipe data_pipe(kDefaultAllocationSize);
212 response_body_stream_ = std::move(data_pipe.producer_handle);
213 response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
214 peer_closed_handle_watcher_.Watch(
215 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
216 base::Bind(&BlobURLLoader::OnResponseBodyStreamClosed,
217 base::Unretained(this)));
218 peer_closed_handle_watcher_.ArmOrNotify();
219
220 writable_handle_watcher_.Watch(
221 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
222 base::Bind(&BlobURLLoader::OnResponseBodyStreamReady,
223 base::Unretained(this)));
224
225 // Start reading...
226 ReadMore();
227 }
228
229 void ReadMore() {
230 DCHECK(!pending_write_.get());
231
232 uint32_t num_bytes;
233 // TODO: we should use the abstractions in MojoAsyncResourceHandler.
234 MojoResult result = NetToMojoPendingBuffer::BeginWrite(
235 &response_body_stream_, &pending_write_, &num_bytes);
236 if (result == MOJO_RESULT_SHOULD_WAIT) {
237 // The pipe is full. We need to wait for it to have more space.
238 writable_handle_watcher_.ArmOrNotify();
239 return;
240 } else if (result != MOJO_RESULT_OK) {
241 // The response body stream is in a bad state. Bail.
242 writable_handle_watcher_.Cancel();
243 response_body_stream_.reset();
244 NotifyCompleted(net::ERR_UNEXPECTED);
245 DeleteIfNeeded();
kinuko 2017/05/26 09:48:12 nit: not needed
jam 2017/05/26 14:14:53 Done.
246 return;
247 }
248
249 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
250 scoped_refptr<net::IOBuffer> buf(
251 new NetToMojoIOBuffer(pending_write_.get()));
252 int bytes_read;
253 storage::BlobReader::Status read_status = blob_reader_->Read(
254 buf.get(), static_cast<int>(num_bytes), &bytes_read,
255 base::Bind(&BlobURLLoader::DidRead, weak_factory_.GetWeakPtr(), false));
256 switch (read_status) {
257 case storage::BlobReader::Status::NET_ERROR:
258 NotifyCompleted(blob_reader_->net_error());
259 return;
260 case storage::BlobReader::Status::IO_PENDING:
261 // Wait for DidRead.
262 return;
263 case storage::BlobReader::Status::DONE:
264 if (bytes_read > 0) {
265 DidRead(true, bytes_read);
266 } else {
267 writable_handle_watcher_.Cancel();
268 pending_write_->Complete(0);
269 pending_write_ = nullptr; // This closes the data pipe.
270 NotifyCompleted(net::OK);
271 return;
272 }
273 }
274 }
275
276 void DidRead(bool completed_synchronously, int num_bytes) {
277 if (response_body_consumer_handle_.is_valid()) {
278 // Send the data pipe on the first OnReadCompleted call.
279 client_->OnStartLoadingResponseBody(
280 std::move(response_body_consumer_handle_));
281 }
282 response_body_stream_ = pending_write_->Complete(num_bytes);
283 pending_write_ = nullptr;
284 if (completed_synchronously) {
285 base::ThreadTaskRunnerHandle::Get()->PostTask(
286 FROM_HERE,
287 base::Bind(&BlobURLLoader::ReadMore, weak_factory_.GetWeakPtr()));
288 } else {
289 ReadMore();
290 }
291 }
292
293 void OnResponseBodyStreamClosed(MojoResult result) {
294 response_body_stream_.reset();
295 pending_write_ = nullptr;
296 DeleteIfNeeded();
297 }
298
299 void OnResponseBodyStreamReady(MojoResult result) {
300 // TODO: Handle a bad |result| value.
301 DCHECK_EQ(result, MOJO_RESULT_OK);
302 ReadMore();
303 }
304
305 void DeleteIfNeeded() {
306 bool has_data_pipe =
307 pending_write_.get() || response_body_stream_.is_valid();
308 if (!has_data_pipe)
309 delete this;
310 }
311
312 mojo::AssociatedBinding<mojom::URLLoader> binding_;
313 ResourceRequest request_;
314 mojom::URLLoaderClientPtr client_;
315
316 bool byte_range_set_;
317 net::HttpByteRange byte_range_;
318
319 std::unique_ptr<storage::BlobDataHandle> blob_handle_;
320 std::unique_ptr<storage::BlobReader> blob_reader_;
321
322 // TODO(jam): share with URLLoaderImpl
323 mojo::ScopedDataPipeProducerHandle response_body_stream_;
324 mojo::ScopedDataPipeConsumerHandle response_body_consumer_handle_;
325 scoped_refptr<NetToMojoPendingBuffer> pending_write_;
326 mojo::SimpleWatcher writable_handle_watcher_;
327 mojo::SimpleWatcher peer_closed_handle_watcher_;
328
329 base::WeakPtrFactory<BlobURLLoader> weak_factory_;
330
331 DISALLOW_COPY_AND_ASSIGN(BlobURLLoader);
332 };
333
334 } // namespace
335
336 BlobURLLoaderFactory::BlobURLLoaderFactory(
337 StoragePartitionImpl* storage_partition)
338 : blob_storage_context_(ChromeBlobStorageContext::GetFor(
339 storage_partition->browser_context())),
340 file_system_context_(storage_partition->GetFileSystemContext()) {
341 DCHECK_CURRENTLY_ON(BrowserThread::UI);
342 }
343
344 mojom::URLLoaderFactoryPtr BlobURLLoaderFactory::CreateFactory() {
345 DCHECK_CURRENTLY_ON(BrowserThread::UI);
346 mojom::URLLoaderFactoryPtr factory;
347 mojom::URLLoaderFactoryRequest request = mojo::MakeRequest(&factory);
348 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
349 base::BindOnce(&BlobURLLoaderFactory::BindOnIO, this,
350 std::move(request)));
351
352 return factory;
353 }
354
355 BlobURLLoaderFactory::~BlobURLLoaderFactory() {}
356
357 void BlobURLLoaderFactory::BindOnIO(mojom::URLLoaderFactoryRequest request) {
358 DCHECK_CURRENTLY_ON(BrowserThread::IO);
359
360 loader_factory_bindings_.AddBinding(this, std::move(request));
361 }
362
363 void BlobURLLoaderFactory::CreateLoaderAndStart(
364 mojom::URLLoaderAssociatedRequest loader,
365 int32_t routing_id,
366 int32_t request_id,
367 uint32_t options,
368 const ResourceRequest& request,
369 mojom::URLLoaderClientPtr client) {
370 DCHECK_CURRENTLY_ON(BrowserThread::IO);
371 new BlobURLLoader(std::move(loader), request, std::move(client),
372 blob_storage_context_->context(),
373 file_system_context_.get());
374 }
375
376 void BlobURLLoaderFactory::SyncLoad(int32_t routing_id,
377 int32_t request_id,
378 const ResourceRequest& request,
379 SyncLoadCallback callback) {
380 NOTREACHED();
381 }
382
383 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/blob_storage/blob_url_loader_factory.h ('k') | content/browser/frame_host/render_frame_host_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698