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

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: kinuko comment 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.
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();
kinuko 2017/05/29 04:27:16 nit: I think there's a chance that this could be c
jam 2017/05/30 05:23:26 Done.
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 }
246
247 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
248 scoped_refptr<net::IOBuffer> buf(
249 new NetToMojoIOBuffer(pending_write_.get()));
250 int bytes_read;
251 storage::BlobReader::Status read_status = blob_reader_->Read(
252 buf.get(), static_cast<int>(num_bytes), &bytes_read,
253 base::Bind(&BlobURLLoader::DidRead, weak_factory_.GetWeakPtr(), false));
254 switch (read_status) {
255 case storage::BlobReader::Status::NET_ERROR:
256 NotifyCompleted(blob_reader_->net_error());
257 return;
258 case storage::BlobReader::Status::IO_PENDING:
259 // Wait for DidRead.
260 return;
261 case storage::BlobReader::Status::DONE:
262 if (bytes_read > 0) {
263 DidRead(true, bytes_read);
264 } else {
265 writable_handle_watcher_.Cancel();
266 pending_write_->Complete(0);
267 pending_write_ = nullptr; // This closes the data pipe.
268 NotifyCompleted(net::OK);
269 return;
270 }
271 }
272 }
273
274 void DidRead(bool completed_synchronously, int num_bytes) {
275 if (response_body_consumer_handle_.is_valid()) {
276 // Send the data pipe on the first OnReadCompleted call.
277 client_->OnStartLoadingResponseBody(
278 std::move(response_body_consumer_handle_));
279 }
280 response_body_stream_ = pending_write_->Complete(num_bytes);
281 pending_write_ = nullptr;
282 if (completed_synchronously) {
283 base::ThreadTaskRunnerHandle::Get()->PostTask(
284 FROM_HERE,
285 base::Bind(&BlobURLLoader::ReadMore, weak_factory_.GetWeakPtr()));
286 } else {
287 ReadMore();
288 }
289 }
290
291 void OnResponseBodyStreamClosed(MojoResult result) {
292 response_body_stream_.reset();
293 pending_write_ = nullptr;
294 DeleteIfNeeded();
295 }
296
297 void OnResponseBodyStreamReady(MojoResult result) {
298 // TODO: Handle a bad |result| value.
299 DCHECK_EQ(result, MOJO_RESULT_OK);
300 ReadMore();
301 }
302
303 void DeleteIfNeeded() {
304 bool has_data_pipe =
305 pending_write_.get() || response_body_stream_.is_valid();
306 if (!has_data_pipe)
307 delete this;
308 }
309
310 mojo::AssociatedBinding<mojom::URLLoader> binding_;
311 ResourceRequest request_;
312 mojom::URLLoaderClientPtr client_;
313
314 bool byte_range_set_;
315 net::HttpByteRange byte_range_;
316
317 std::unique_ptr<storage::BlobDataHandle> blob_handle_;
318 std::unique_ptr<storage::BlobReader> blob_reader_;
319
320 // TODO(jam): share with URLLoaderImpl
321 mojo::ScopedDataPipeProducerHandle response_body_stream_;
322 mojo::ScopedDataPipeConsumerHandle response_body_consumer_handle_;
323 scoped_refptr<NetToMojoPendingBuffer> pending_write_;
324 mojo::SimpleWatcher writable_handle_watcher_;
325 mojo::SimpleWatcher peer_closed_handle_watcher_;
326
327 base::WeakPtrFactory<BlobURLLoader> weak_factory_;
328
329 DISALLOW_COPY_AND_ASSIGN(BlobURLLoader);
330 };
331
332 } // namespace
333
334 BlobURLLoaderFactory::BlobURLLoaderFactory(
335 StoragePartitionImpl* storage_partition)
336 : blob_storage_context_(ChromeBlobStorageContext::GetFor(
337 storage_partition->browser_context())),
338 file_system_context_(storage_partition->GetFileSystemContext()) {
339 DCHECK_CURRENTLY_ON(BrowserThread::UI);
340 }
341
342 mojom::URLLoaderFactoryPtr BlobURLLoaderFactory::CreateFactory() {
343 DCHECK_CURRENTLY_ON(BrowserThread::UI);
344 mojom::URLLoaderFactoryPtr factory;
345 mojom::URLLoaderFactoryRequest request = mojo::MakeRequest(&factory);
346 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
347 base::BindOnce(&BlobURLLoaderFactory::BindOnIO, this,
348 std::move(request)));
349
350 return factory;
351 }
352
353 BlobURLLoaderFactory::~BlobURLLoaderFactory() {}
354
355 void BlobURLLoaderFactory::BindOnIO(mojom::URLLoaderFactoryRequest request) {
356 DCHECK_CURRENTLY_ON(BrowserThread::IO);
357
358 loader_factory_bindings_.AddBinding(this, std::move(request));
359 }
360
361 void BlobURLLoaderFactory::CreateLoaderAndStart(
362 mojom::URLLoaderAssociatedRequest loader,
363 int32_t routing_id,
364 int32_t request_id,
365 uint32_t options,
366 const ResourceRequest& request,
367 mojom::URLLoaderClientPtr client) {
368 DCHECK_CURRENTLY_ON(BrowserThread::IO);
369 new BlobURLLoader(std::move(loader), request, std::move(client),
370 blob_storage_context_->context(),
371 file_system_context_.get());
372 }
373
374 void BlobURLLoaderFactory::SyncLoad(int32_t routing_id,
375 int32_t request_id,
376 const ResourceRequest& request,
377 SyncLoadCallback callback) {
378 NOTREACHED();
379 }
380
381 } // 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