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

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: merge and switch back to associated request 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 // This class handles a request for a blob:// url. It self-destructs (see
37 // DeleteIfNeeded) when it has finished responding.
38 // Note: some of this code is duplicated from storage::BlobURLRequestJob.
39 class BlobURLLoader : public mojom::URLLoader {
40 public:
41 BlobURLLoader(mojom::URLLoaderAssociatedRequest url_loader_request,
42 const ResourceRequest& request,
43 mojom::URLLoaderClientPtr client,
44 storage::BlobStorageContext* blob_storage_context,
45 scoped_refptr<storage::FileSystemContext> file_system_context)
46 : binding_(this, std::move(url_loader_request)),
47 request_(request),
48 client_(std::move(client)),
49 writable_handle_watcher_(FROM_HERE,
50 mojo::SimpleWatcher::ArmingPolicy::MANUAL),
51 peer_closed_handle_watcher_(FROM_HERE,
52 mojo::SimpleWatcher::ArmingPolicy::MANUAL),
53 weak_factory_(this) {
54 DCHECK_CURRENTLY_ON(BrowserThread::IO);
55 if (blob_storage_context) {
56 blob_handle_ =
57 blob_storage_context->GetBlobDataFromPublicURL(request.url);
58 }
59
60 // PostTask since it might destruct.
61 base::ThreadTaskRunnerHandle::Get()->PostTask(
62 FROM_HERE, base::Bind(&BlobURLLoader::Start, weak_factory_.GetWeakPtr(),
63 request, file_system_context));
64 }
65
66 void Start(const ResourceRequest& request,
67 scoped_refptr<storage::FileSystemContext> file_system_context) {
68 if (!blob_handle_) {
69 NotifyCompleted(net::ERR_FILE_NOT_FOUND);
70 return;
71 }
72
73 base::SequencedTaskRunner* file_task_runner =
74 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get();
75 blob_reader_ =
76 blob_handle_->CreateReader(file_system_context.get(), file_task_runner);
77
78 // We only support GET request per the spec.
79 if (request.method != "GET") {
80 NotifyCompleted(net::ERR_METHOD_NOT_SUPPORTED);
81 return;
82 }
83
84 if (blob_reader_->net_error()) {
85 NotifyCompleted(blob_reader_->net_error());
86 return;
87 }
88
89 net::HttpRequestHeaders request_headers;
90 request_headers.AddHeadersFromString(request.headers);
91 std::string range_header;
92 if (request_headers.GetHeader(net::HttpRequestHeaders::kRange,
93 &range_header)) {
94 // We only care about "Range" header here.
95 std::vector<net::HttpByteRange> ranges;
96 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
97 if (ranges.size() == 1) {
98 byte_range_set_ = true;
99 byte_range_ = ranges[0];
100 } else {
101 // We don't support multiple range requests in one single URL request,
102 // because we need to do multipart encoding here.
103 // TODO(jianli): Support multipart byte range requests.
104 NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
105 }
106 }
107 }
108
109 storage::BlobReader::Status size_status =
110 blob_reader_->CalculateSize(base::Bind(&BlobURLLoader::DidCalculateSize,
111 weak_factory_.GetWeakPtr()));
112 switch (size_status) {
113 case storage::BlobReader::Status::NET_ERROR:
114 NotifyCompleted(blob_reader_->net_error());
115 return;
116 case storage::BlobReader::Status::IO_PENDING:
117 return;
118 case storage::BlobReader::Status::DONE:
119 DidCalculateSize(net::OK);
120 return;
121 }
122
123 NOTREACHED();
124 }
125
126 ~BlobURLLoader() override {}
127
128 private:
129 // mojom::URLLoader implementation:
130 void FollowRedirect() override { NOTREACHED(); }
131
132 void SetPriority(net::RequestPriority priority,
133 int32_t intra_priority_value) override {}
134
135 // Notifies the client that the request completed. Takes care of deleting this
136 // object now if possible (i.e. no outstanding data pipe), otherwise this
137 // object will be deleted when the data pipe is closed.
138 void NotifyCompleted(int error_code) {
139 if (error_code != net::OK && !sent_headers_) {
140 net::HttpStatusCode status_code =
141 storage::BlobURLRequestJob::NetErrorToHttpStatusCode(error_code);
142 ResourceResponseHead response;
143 response.headers = storage::BlobURLRequestJob::GenerateHeaders(
144 status_code, nullptr, nullptr, nullptr, nullptr);
145 client_->OnReceiveResponse(response, base::nullopt, nullptr);
146 }
147 ResourceRequestCompletionStatus request_complete_data;
148 client_->OnComplete(request_complete_data);
149
150 DeleteIfNeeded();
151 }
152
153 void DidCalculateSize(int result) {
154 if (result != net::OK) {
155 NotifyCompleted(result);
156 return;
157 }
158
159 // Apply the range requirement.
160 if (!byte_range_.ComputeBounds(blob_reader_->total_size())) {
161 NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
162 return;
163 }
164
165 DCHECK_LE(byte_range_.first_byte_position(),
166 byte_range_.last_byte_position() + 1);
167 uint64_t length =
168 base::checked_cast<uint64_t>(byte_range_.last_byte_position() -
169 byte_range_.first_byte_position() + 1);
170
171 if (byte_range_set_)
172 blob_reader_->SetReadRange(byte_range_.first_byte_position(), length);
173
174 net::HttpStatusCode status_code = net::HTTP_OK;
175 if (byte_range_set_ && byte_range_.IsValid()) {
176 status_code = net::HTTP_PARTIAL_CONTENT;
177 } else {
178 // TODO(horo): When the requester doesn't need the side data
179 // (ex:FileReader) we should skip reading the side data.
180 if (blob_reader_->has_side_data() &&
181 blob_reader_->ReadSideData(base::Bind(&BlobURLLoader::DidReadMetadata,
182 weak_factory_.GetWeakPtr())) ==
183 storage::BlobReader::Status::IO_PENDING) {
184 return;
185 }
186 }
187
188 HeadersCompleted(status_code);
189 }
190
191 void DidReadMetadata(storage::BlobReader::Status result) {
192 if (result != storage::BlobReader::Status::DONE) {
193 NotifyCompleted(blob_reader_->net_error());
194 return;
195 }
196 HeadersCompleted(net::HTTP_OK);
197 }
198
199 void HeadersCompleted(net::HttpStatusCode status_code) {
200 ResourceResponseHead response;
201 response.content_length = 0;
202 response.headers = storage::BlobURLRequestJob::GenerateHeaders(
203 status_code, blob_handle_.get(), blob_reader_.get(), &byte_range_,
204 &response.content_length);
205
206 std::string mime_type;
207 response.headers->GetMimeType(&mime_type);
208 // Match logic in StreamURLRequestJob::HeadersCompleted.
209 if (mime_type.empty())
210 mime_type = "text/plain";
211 response.mime_type = mime_type;
212
213 // TODO(jam): some of this code can be shared with
214 // content/network/url_loader_impl.h
215 client_->OnReceiveResponse(response, base::nullopt, nullptr);
216 sent_headers_ = true;
217
218 net::IOBufferWithSize* metadata = blob_reader_->side_data();
219 if (metadata) {
220 const uint8_t* data = reinterpret_cast<const uint8_t*>(metadata->data());
221 client_->OnReceiveCachedMetadata(
222 std::vector<uint8_t>(data, data + metadata->size()));
223 }
224
225 mojo::DataPipe data_pipe(kDefaultAllocationSize);
226 response_body_stream_ = std::move(data_pipe.producer_handle);
227 response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
228 peer_closed_handle_watcher_.Watch(
229 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
230 base::Bind(&BlobURLLoader::OnResponseBodyStreamClosed,
231 base::Unretained(this)));
232 peer_closed_handle_watcher_.ArmOrNotify();
233
234 writable_handle_watcher_.Watch(
235 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
236 base::Bind(&BlobURLLoader::OnResponseBodyStreamReady,
237 base::Unretained(this)));
238
239 // Start reading...
240 ReadMore();
241 }
242
243 void ReadMore() {
244 DCHECK(!pending_write_.get());
245
246 uint32_t num_bytes;
247 // TODO: we should use the abstractions in MojoAsyncResourceHandler.
248 MojoResult result = NetToMojoPendingBuffer::BeginWrite(
249 &response_body_stream_, &pending_write_, &num_bytes);
250 if (result == MOJO_RESULT_SHOULD_WAIT) {
251 // The pipe is full. We need to wait for it to have more space.
252 writable_handle_watcher_.ArmOrNotify();
253 return;
254 } else if (result != MOJO_RESULT_OK) {
255 // The response body stream is in a bad state. Bail.
256 writable_handle_watcher_.Cancel();
257 response_body_stream_.reset();
258 NotifyCompleted(net::ERR_UNEXPECTED);
259 return;
260 }
261
262 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
263 scoped_refptr<net::IOBuffer> buf(
264 new NetToMojoIOBuffer(pending_write_.get()));
265 int bytes_read;
266 storage::BlobReader::Status read_status = blob_reader_->Read(
267 buf.get(), static_cast<int>(num_bytes), &bytes_read,
268 base::Bind(&BlobURLLoader::DidRead, weak_factory_.GetWeakPtr(), false));
269 switch (read_status) {
270 case storage::BlobReader::Status::NET_ERROR:
271 NotifyCompleted(blob_reader_->net_error());
272 return;
273 case storage::BlobReader::Status::IO_PENDING:
274 // Wait for DidRead.
275 return;
276 case storage::BlobReader::Status::DONE:
277 if (bytes_read > 0) {
278 DidRead(true, bytes_read);
279 } else {
280 writable_handle_watcher_.Cancel();
281 pending_write_->Complete(0);
282 pending_write_ = nullptr; // This closes the data pipe.
283 NotifyCompleted(net::OK);
284 return;
285 }
286 }
287 }
288
289 void DidRead(bool completed_synchronously, int num_bytes) {
290 if (response_body_consumer_handle_.is_valid()) {
291 // Send the data pipe on the first OnReadCompleted call.
292 client_->OnStartLoadingResponseBody(
293 std::move(response_body_consumer_handle_));
294 }
295 response_body_stream_ = pending_write_->Complete(num_bytes);
296 pending_write_ = nullptr;
297 if (completed_synchronously) {
298 base::ThreadTaskRunnerHandle::Get()->PostTask(
299 FROM_HERE,
300 base::Bind(&BlobURLLoader::ReadMore, weak_factory_.GetWeakPtr()));
301 } else {
302 ReadMore();
303 }
304 }
305
306 void OnResponseBodyStreamClosed(MojoResult result) {
307 response_body_stream_.reset();
308 pending_write_ = nullptr;
309 DeleteIfNeeded();
310 }
311
312 void OnResponseBodyStreamReady(MojoResult result) {
313 // TODO: Handle a bad |result| value.
314 DCHECK_EQ(result, MOJO_RESULT_OK);
315 ReadMore();
316 }
317
318 void DeleteIfNeeded() {
319 bool has_data_pipe =
320 pending_write_.get() || response_body_stream_.is_valid();
321 if (!has_data_pipe)
322 delete this;
323 }
324
325 mojo::AssociatedBinding<mojom::URLLoader> binding_;
326 ResourceRequest request_;
327 mojom::URLLoaderClientPtr client_;
328
329 bool byte_range_set_ = false;
330 net::HttpByteRange byte_range_;
331
332 bool sent_headers_ = false;
333
334 std::unique_ptr<storage::BlobDataHandle> blob_handle_;
335 std::unique_ptr<storage::BlobReader> blob_reader_;
336
337 // TODO(jam): share with URLLoaderImpl
338 mojo::ScopedDataPipeProducerHandle response_body_stream_;
339 mojo::ScopedDataPipeConsumerHandle response_body_consumer_handle_;
340 scoped_refptr<NetToMojoPendingBuffer> pending_write_;
341 mojo::SimpleWatcher writable_handle_watcher_;
342 mojo::SimpleWatcher peer_closed_handle_watcher_;
343
344 base::WeakPtrFactory<BlobURLLoader> weak_factory_;
345
346 DISALLOW_COPY_AND_ASSIGN(BlobURLLoader);
347 };
348
349 } // namespace
350
351 BlobURLLoaderFactory::BlobURLLoaderFactory(
352 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
353 scoped_refptr<storage::FileSystemContext> file_system_context)
354 : blob_storage_context_(std::move(blob_storage_context)),
355 file_system_context_(file_system_context) {
356 DCHECK_CURRENTLY_ON(BrowserThread::UI);
357 }
358
359 mojom::URLLoaderFactoryPtr BlobURLLoaderFactory::CreateFactory() {
360 DCHECK_CURRENTLY_ON(BrowserThread::UI);
361 mojom::URLLoaderFactoryPtr factory;
362 mojom::URLLoaderFactoryRequest request = mojo::MakeRequest(&factory);
363 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
364 base::BindOnce(&BlobURLLoaderFactory::BindOnIO, this,
365 std::move(request)));
366
367 return factory;
368 }
369
370 BlobURLLoaderFactory::~BlobURLLoaderFactory() {}
371
372 void BlobURLLoaderFactory::BindOnIO(mojom::URLLoaderFactoryRequest request) {
373 DCHECK_CURRENTLY_ON(BrowserThread::IO);
374
375 loader_factory_bindings_.AddBinding(this, std::move(request));
376 }
377
378 void BlobURLLoaderFactory::CreateLoaderAndStart(
379 mojom::URLLoaderAssociatedRequest loader,
380 int32_t routing_id,
381 int32_t request_id,
382 uint32_t options,
383 const ResourceRequest& request,
384 mojom::URLLoaderClientPtr client) {
385 DCHECK_CURRENTLY_ON(BrowserThread::IO);
386 new BlobURLLoader(std::move(loader), request, std::move(client),
387 blob_storage_context_.get(), file_system_context_);
388 }
389
390 void BlobURLLoaderFactory::SyncLoad(int32_t routing_id,
391 int32_t request_id,
392 const ResourceRequest& request,
393 SyncLoadCallback callback) {
394 NOTREACHED();
395 }
396
397 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698