Home | History | Annotate | Download | only in blob
      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 
     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 
     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 
     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 
     81 void BlobURLRequestJob::Kill() {
     82   DeleteCurrentFileReader();
     83 
     84   net::URLRequestJob::Kill();
     85   weak_factory_.InvalidateWeakPtrs();
     86 }
     87 
     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 
    118 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 
    125 void BlobURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
    126   if (response_info_)
    127     *info = *response_info_;
    128 }
    129 
    130 int BlobURLRequestJob::GetResponseCode() const {
    131   if (!response_info_)
    132     return -1;
    133 
    134   return response_info_->headers->response_code();
    135 }
    136 
    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 
    157 BlobURLRequestJob::~BlobURLRequestJob() {
    158   STLDeleteValues(&index_to_reader_);
    159 }
    160 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    432 int BlobURLRequestJob::BytesReadCompleted() {
    433   int bytes_read = read_buf_->BytesConsumed();
    434   read_buf_ = NULL;
    435   return bytes_read;
    436 }
    437 
    438 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 
    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 
    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 
    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 
    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 
    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 
    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