1 // Copyright (c) 2012 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 "storage/browser/blob/blob_url_request_job.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
11
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/compiler_specific.h"
15 #include "base/files/file_util_proxy.h"
16 #include "base/format_macros.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/message_loop/message_loop_proxy.h"
19 #include "base/numerics/safe_conversions.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringprintf.h"
23 #include "net/base/io_buffer.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_request_headers.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_response_info.h"
28 #include "net/http/http_util.h"
29 #include "net/url_request/url_request.h"
30 #include "net/url_request/url_request_context.h"
31 #include "net/url_request/url_request_error_job.h"
32 #include "net/url_request/url_request_status.h"
33 #include "storage/browser/blob/file_stream_reader.h"
34 #include "storage/browser/fileapi/file_system_context.h"
35 #include "storage/browser/fileapi/file_system_url.h"
36
37 namespace storage {
38
39 namespace {
40
IsFileType(BlobData::Item::Type type)41 bool IsFileType(BlobData::Item::Type type) {
42 switch (type) {
43 case BlobData::Item::TYPE_FILE:
44 case BlobData::Item::TYPE_FILE_FILESYSTEM:
45 return true;
46 default:
47 return false;
48 }
49 }
50
51 } // namespace
52
BlobURLRequestJob(net::URLRequest * request,net::NetworkDelegate * network_delegate,const scoped_refptr<BlobData> & blob_data,storage::FileSystemContext * file_system_context,base::MessageLoopProxy * file_thread_proxy)53 BlobURLRequestJob::BlobURLRequestJob(
54 net::URLRequest* request,
55 net::NetworkDelegate* network_delegate,
56 const scoped_refptr<BlobData>& blob_data,
57 storage::FileSystemContext* file_system_context,
58 base::MessageLoopProxy* file_thread_proxy)
59 : net::URLRequestJob(request, network_delegate),
60 blob_data_(blob_data),
61 file_system_context_(file_system_context),
62 file_thread_proxy_(file_thread_proxy),
63 total_size_(0),
64 remaining_bytes_(0),
65 pending_get_file_info_count_(0),
66 current_item_index_(0),
67 current_item_offset_(0),
68 error_(false),
69 byte_range_set_(false),
70 weak_factory_(this) {
71 DCHECK(file_thread_proxy_.get());
72 }
73
Start()74 void BlobURLRequestJob::Start() {
75 // Continue asynchronously.
76 base::MessageLoop::current()->PostTask(
77 FROM_HERE,
78 base::Bind(&BlobURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
79 }
80
Kill()81 void BlobURLRequestJob::Kill() {
82 DeleteCurrentFileReader();
83
84 net::URLRequestJob::Kill();
85 weak_factory_.InvalidateWeakPtrs();
86 }
87
ReadRawData(net::IOBuffer * dest,int dest_size,int * bytes_read)88 bool BlobURLRequestJob::ReadRawData(net::IOBuffer* dest,
89 int dest_size,
90 int* bytes_read) {
91 DCHECK_NE(dest_size, 0);
92 DCHECK(bytes_read);
93 DCHECK_GE(remaining_bytes_, 0);
94
95 // Bail out immediately if we encounter an error.
96 if (error_) {
97 *bytes_read = 0;
98 return true;
99 }
100
101 if (remaining_bytes_ < dest_size)
102 dest_size = static_cast<int>(remaining_bytes_);
103
104 // If we should copy zero bytes because |remaining_bytes_| is zero, short
105 // circuit here.
106 if (!dest_size) {
107 *bytes_read = 0;
108 return true;
109 }
110
111 // Keep track of the buffer.
112 DCHECK(!read_buf_.get());
113 read_buf_ = new net::DrainableIOBuffer(dest, dest_size);
114
115 return ReadLoop(bytes_read);
116 }
117
GetMimeType(std::string * mime_type) const118 bool BlobURLRequestJob::GetMimeType(std::string* mime_type) const {
119 if (!response_info_)
120 return false;
121
122 return response_info_->headers->GetMimeType(mime_type);
123 }
124
GetResponseInfo(net::HttpResponseInfo * info)125 void BlobURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
126 if (response_info_)
127 *info = *response_info_;
128 }
129
GetResponseCode() const130 int BlobURLRequestJob::GetResponseCode() const {
131 if (!response_info_)
132 return -1;
133
134 return response_info_->headers->response_code();
135 }
136
SetExtraRequestHeaders(const net::HttpRequestHeaders & headers)137 void BlobURLRequestJob::SetExtraRequestHeaders(
138 const net::HttpRequestHeaders& headers) {
139 std::string range_header;
140 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
141 // We only care about "Range" header here.
142 std::vector<net::HttpByteRange> ranges;
143 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
144 if (ranges.size() == 1) {
145 byte_range_set_ = true;
146 byte_range_ = ranges[0];
147 } else {
148 // We don't support multiple range requests in one single URL request,
149 // because we need to do multipart encoding here.
150 // TODO(jianli): Support multipart byte range requests.
151 NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
152 }
153 }
154 }
155 }
156
~BlobURLRequestJob()157 BlobURLRequestJob::~BlobURLRequestJob() {
158 STLDeleteValues(&index_to_reader_);
159 }
160
DidStart()161 void BlobURLRequestJob::DidStart() {
162 error_ = false;
163
164 // We only support GET request per the spec.
165 if (request()->method() != "GET") {
166 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
167 return;
168 }
169
170 // If the blob data is not present, bail out.
171 if (!blob_data_.get()) {
172 NotifyFailure(net::ERR_FILE_NOT_FOUND);
173 return;
174 }
175
176 CountSize();
177 }
178
AddItemLength(size_t index,int64 item_length)179 bool BlobURLRequestJob::AddItemLength(size_t index, int64 item_length) {
180 if (item_length > kint64max - total_size_) {
181 NotifyFailure(net::ERR_FAILED);
182 return false;
183 }
184
185 // Cache the size and add it to the total size.
186 DCHECK_LT(index, item_length_list_.size());
187 item_length_list_[index] = item_length;
188 total_size_ += item_length;
189 return true;
190 }
191
CountSize()192 void BlobURLRequestJob::CountSize() {
193 pending_get_file_info_count_ = 0;
194 total_size_ = 0;
195 item_length_list_.resize(blob_data_->items().size());
196
197 for (size_t i = 0; i < blob_data_->items().size(); ++i) {
198 const BlobData::Item& item = blob_data_->items().at(i);
199 if (IsFileType(item.type())) {
200 ++pending_get_file_info_count_;
201 GetFileStreamReader(i)->GetLength(
202 base::Bind(&BlobURLRequestJob::DidGetFileItemLength,
203 weak_factory_.GetWeakPtr(), i));
204 continue;
205 }
206
207 if (!AddItemLength(i, item.length()))
208 return;
209 }
210
211 if (pending_get_file_info_count_ == 0)
212 DidCountSize(net::OK);
213 }
214
DidCountSize(int error)215 void BlobURLRequestJob::DidCountSize(int error) {
216 DCHECK(!error_);
217
218 // If an error occured, bail out.
219 if (error != net::OK) {
220 NotifyFailure(error);
221 return;
222 }
223
224 // Apply the range requirement.
225 if (!byte_range_.ComputeBounds(total_size_)) {
226 NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
227 return;
228 }
229
230 remaining_bytes_ = base::checked_cast<int64>(
231 byte_range_.last_byte_position() - byte_range_.first_byte_position() + 1);
232 DCHECK_GE(remaining_bytes_, 0);
233
234 // Do the seek at the beginning of the request.
235 if (byte_range_.first_byte_position())
236 Seek(byte_range_.first_byte_position());
237
238 NotifySuccess();
239 }
240
DidGetFileItemLength(size_t index,int64 result)241 void BlobURLRequestJob::DidGetFileItemLength(size_t index, int64 result) {
242 // Do nothing if we have encountered an error.
243 if (error_)
244 return;
245
246 if (result == net::ERR_UPLOAD_FILE_CHANGED) {
247 NotifyFailure(net::ERR_FILE_NOT_FOUND);
248 return;
249 } else if (result < 0) {
250 NotifyFailure(result);
251 return;
252 }
253
254 DCHECK_LT(index, blob_data_->items().size());
255 const BlobData::Item& item = blob_data_->items().at(index);
256 DCHECK(IsFileType(item.type()));
257
258 uint64 file_length = result;
259 uint64 item_offset = item.offset();
260 uint64 item_length = item.length();
261
262 if (item_offset > file_length) {
263 NotifyFailure(net::ERR_FILE_NOT_FOUND);
264 return;
265 }
266
267 uint64 max_length = file_length - item_offset;
268
269 // If item length is undefined, then we need to use the file size being
270 // resolved in the real time.
271 if (item_length == std::numeric_limits<uint64>::max()) {
272 item_length = max_length;
273 } else if (item_length > max_length) {
274 NotifyFailure(net::ERR_FILE_NOT_FOUND);
275 return;
276 }
277
278 if (!AddItemLength(index, item_length))
279 return;
280
281 if (--pending_get_file_info_count_ == 0)
282 DidCountSize(net::OK);
283 }
284
Seek(int64 offset)285 void BlobURLRequestJob::Seek(int64 offset) {
286 // Skip the initial items that are not in the range.
287 for (current_item_index_ = 0;
288 current_item_index_ < blob_data_->items().size() &&
289 offset >= item_length_list_[current_item_index_];
290 ++current_item_index_) {
291 offset -= item_length_list_[current_item_index_];
292 }
293
294 // Set the offset that need to jump to for the first item in the range.
295 current_item_offset_ = offset;
296
297 if (offset == 0)
298 return;
299
300 // Adjust the offset of the first stream if it is of file type.
301 const BlobData::Item& item = blob_data_->items().at(current_item_index_);
302 if (IsFileType(item.type())) {
303 DeleteCurrentFileReader();
304 CreateFileStreamReader(current_item_index_, offset);
305 }
306 }
307
ReadItem()308 bool BlobURLRequestJob::ReadItem() {
309 // Are we done with reading all the blob data?
310 if (remaining_bytes_ == 0)
311 return true;
312
313 // If we get to the last item but still expect something to read, bail out
314 // since something is wrong.
315 if (current_item_index_ >= blob_data_->items().size()) {
316 NotifyFailure(net::ERR_FAILED);
317 return false;
318 }
319
320 // Compute the bytes to read for current item.
321 int bytes_to_read = ComputeBytesToRead();
322
323 // If nothing to read for current item, advance to next item.
324 if (bytes_to_read == 0) {
325 AdvanceItem();
326 return ReadItem();
327 }
328
329 // Do the reading.
330 const BlobData::Item& item = blob_data_->items().at(current_item_index_);
331 if (item.type() == BlobData::Item::TYPE_BYTES)
332 return ReadBytesItem(item, bytes_to_read);
333 if (IsFileType(item.type())) {
334 return ReadFileItem(GetFileStreamReader(current_item_index_),
335 bytes_to_read);
336 }
337 NOTREACHED();
338 return false;
339 }
340
AdvanceItem()341 void BlobURLRequestJob::AdvanceItem() {
342 // Close the file if the current item is a file.
343 DeleteCurrentFileReader();
344
345 // Advance to the next item.
346 current_item_index_++;
347 current_item_offset_ = 0;
348 }
349
AdvanceBytesRead(int result)350 void BlobURLRequestJob::AdvanceBytesRead(int result) {
351 DCHECK_GT(result, 0);
352
353 // Do we finish reading the current item?
354 current_item_offset_ += result;
355 if (current_item_offset_ == item_length_list_[current_item_index_])
356 AdvanceItem();
357
358 // Subtract the remaining bytes.
359 remaining_bytes_ -= result;
360 DCHECK_GE(remaining_bytes_, 0);
361
362 // Adjust the read buffer.
363 read_buf_->DidConsume(result);
364 DCHECK_GE(read_buf_->BytesRemaining(), 0);
365 }
366
ReadBytesItem(const BlobData::Item & item,int bytes_to_read)367 bool BlobURLRequestJob::ReadBytesItem(const BlobData::Item& item,
368 int bytes_to_read) {
369 DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
370
371 memcpy(read_buf_->data(),
372 item.bytes() + item.offset() + current_item_offset_,
373 bytes_to_read);
374
375 AdvanceBytesRead(bytes_to_read);
376 return true;
377 }
378
ReadFileItem(FileStreamReader * reader,int bytes_to_read)379 bool BlobURLRequestJob::ReadFileItem(FileStreamReader* reader,
380 int bytes_to_read) {
381 DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
382 DCHECK(reader);
383 const int result = reader->Read(
384 read_buf_.get(),
385 bytes_to_read,
386 base::Bind(&BlobURLRequestJob::DidReadFile, base::Unretained(this)));
387 if (result >= 0) {
388 // Data is immediately available.
389 if (GetStatus().is_io_pending())
390 DidReadFile(result);
391 else
392 AdvanceBytesRead(result);
393 return true;
394 }
395 if (result == net::ERR_IO_PENDING)
396 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
397 else
398 NotifyFailure(result);
399 return false;
400 }
401
DidReadFile(int result)402 void BlobURLRequestJob::DidReadFile(int result) {
403 if (result <= 0) {
404 NotifyFailure(net::ERR_FAILED);
405 return;
406 }
407 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
408
409 AdvanceBytesRead(result);
410
411 // If the read buffer is completely filled, we're done.
412 if (!read_buf_->BytesRemaining()) {
413 int bytes_read = BytesReadCompleted();
414 NotifyReadComplete(bytes_read);
415 return;
416 }
417
418 // Otherwise, continue the reading.
419 int bytes_read = 0;
420 if (ReadLoop(&bytes_read))
421 NotifyReadComplete(bytes_read);
422 }
423
DeleteCurrentFileReader()424 void BlobURLRequestJob::DeleteCurrentFileReader() {
425 IndexToReaderMap::iterator found = index_to_reader_.find(current_item_index_);
426 if (found != index_to_reader_.end() && found->second) {
427 delete found->second;
428 index_to_reader_.erase(found);
429 }
430 }
431
BytesReadCompleted()432 int BlobURLRequestJob::BytesReadCompleted() {
433 int bytes_read = read_buf_->BytesConsumed();
434 read_buf_ = NULL;
435 return bytes_read;
436 }
437
ComputeBytesToRead() const438 int BlobURLRequestJob::ComputeBytesToRead() const {
439 int64 current_item_length = item_length_list_[current_item_index_];
440
441 int64 item_remaining = current_item_length - current_item_offset_;
442 int64 buf_remaining = read_buf_->BytesRemaining();
443 int64 max_remaining = std::numeric_limits<int>::max();
444
445 int64 min = std::min(std::min(std::min(item_remaining,
446 buf_remaining),
447 remaining_bytes_),
448 max_remaining);
449
450 return static_cast<int>(min);
451 }
452
ReadLoop(int * bytes_read)453 bool BlobURLRequestJob::ReadLoop(int* bytes_read) {
454 // Read until we encounter an error or could not get the data immediately.
455 while (remaining_bytes_ > 0 && read_buf_->BytesRemaining() > 0) {
456 if (!ReadItem())
457 return false;
458 }
459
460 *bytes_read = BytesReadCompleted();
461 return true;
462 }
463
NotifySuccess()464 void BlobURLRequestJob::NotifySuccess() {
465 net::HttpStatusCode status_code = net::HTTP_OK;
466 if (byte_range_set_ && byte_range_.IsValid())
467 status_code = net::HTTP_PARTIAL_CONTENT;
468 HeadersCompleted(status_code);
469 }
470
NotifyFailure(int error_code)471 void BlobURLRequestJob::NotifyFailure(int error_code) {
472 error_ = true;
473
474 // If we already return the headers on success, we can't change the headers
475 // now. Instead, we just error out.
476 if (response_info_) {
477 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
478 error_code));
479 return;
480 }
481
482 net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
483 switch (error_code) {
484 case net::ERR_ACCESS_DENIED:
485 status_code = net::HTTP_FORBIDDEN;
486 break;
487 case net::ERR_FILE_NOT_FOUND:
488 status_code = net::HTTP_NOT_FOUND;
489 break;
490 case net::ERR_METHOD_NOT_SUPPORTED:
491 status_code = net::HTTP_METHOD_NOT_ALLOWED;
492 break;
493 case net::ERR_REQUEST_RANGE_NOT_SATISFIABLE:
494 status_code = net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
495 break;
496 case net::ERR_FAILED:
497 break;
498 default:
499 DCHECK(false);
500 break;
501 }
502 HeadersCompleted(status_code);
503 }
504
HeadersCompleted(net::HttpStatusCode status_code)505 void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
506 std::string status("HTTP/1.1 ");
507 status.append(base::IntToString(status_code));
508 status.append(" ");
509 status.append(net::GetHttpReasonPhrase(status_code));
510 status.append("\0\0", 2);
511 net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
512
513 if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
514 std::string content_length_header(net::HttpRequestHeaders::kContentLength);
515 content_length_header.append(": ");
516 content_length_header.append(base::Int64ToString(remaining_bytes_));
517 headers->AddHeader(content_length_header);
518 if (status_code == net::HTTP_PARTIAL_CONTENT) {
519 DCHECK(byte_range_set_);
520 DCHECK(byte_range_.IsValid());
521 std::string content_range_header(net::HttpResponseHeaders::kContentRange);
522 content_range_header.append(": bytes ");
523 content_range_header.append(base::StringPrintf(
524 "%" PRId64 "-%" PRId64,
525 byte_range_.first_byte_position(), byte_range_.last_byte_position()));
526 content_range_header.append("/");
527 content_range_header.append(base::StringPrintf("%" PRId64, total_size_));
528 headers->AddHeader(content_range_header);
529 }
530 if (!blob_data_->content_type().empty()) {
531 std::string content_type_header(net::HttpRequestHeaders::kContentType);
532 content_type_header.append(": ");
533 content_type_header.append(blob_data_->content_type());
534 headers->AddHeader(content_type_header);
535 }
536 if (!blob_data_->content_disposition().empty()) {
537 std::string content_disposition_header("Content-Disposition: ");
538 content_disposition_header.append(blob_data_->content_disposition());
539 headers->AddHeader(content_disposition_header);
540 }
541 }
542
543 response_info_.reset(new net::HttpResponseInfo());
544 response_info_->headers = headers;
545
546 set_expected_content_size(remaining_bytes_);
547
548 NotifyHeadersComplete();
549 }
550
GetFileStreamReader(size_t index)551 FileStreamReader* BlobURLRequestJob::GetFileStreamReader(size_t index) {
552 DCHECK_LT(index, blob_data_->items().size());
553 const BlobData::Item& item = blob_data_->items().at(index);
554 if (!IsFileType(item.type()))
555 return NULL;
556 if (index_to_reader_.find(index) == index_to_reader_.end())
557 CreateFileStreamReader(index, 0);
558 DCHECK(index_to_reader_[index]);
559 return index_to_reader_[index];
560 }
561
CreateFileStreamReader(size_t index,int64 additional_offset)562 void BlobURLRequestJob::CreateFileStreamReader(size_t index,
563 int64 additional_offset) {
564 DCHECK_LT(index, blob_data_->items().size());
565 const BlobData::Item& item = blob_data_->items().at(index);
566 DCHECK(IsFileType(item.type()));
567 DCHECK_EQ(0U, index_to_reader_.count(index));
568
569 FileStreamReader* reader = NULL;
570 switch (item.type()) {
571 case BlobData::Item::TYPE_FILE:
572 reader = FileStreamReader::CreateForLocalFile(
573 file_thread_proxy_.get(),
574 item.path(),
575 item.offset() + additional_offset,
576 item.expected_modification_time());
577 break;
578 case BlobData::Item::TYPE_FILE_FILESYSTEM:
579 reader = file_system_context_
580 ->CreateFileStreamReader(
581 storage::FileSystemURL(file_system_context_->CrackURL(
582 item.filesystem_url())),
583 item.offset() + additional_offset,
584 item.length() == std::numeric_limits<uint64>::max()
585 ? storage::kMaximumLength
586 : item.length() - additional_offset,
587 item.expected_modification_time())
588 .release();
589 break;
590 default:
591 NOTREACHED();
592 }
593 DCHECK(reader);
594 index_to_reader_[index] = reader;
595 }
596
597 } // namespace storage
598