Home | History | Annotate | Download | only in base
      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 "net/base/upload_file_element_reader.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_util.h"
      9 #include "base/location.h"
     10 #include "base/task_runner_util.h"
     11 #include "net/base/file_stream.h"
     12 #include "net/base/io_buffer.h"
     13 #include "net/base/net_errors.h"
     14 
     15 namespace net {
     16 
     17 namespace {
     18 
     19 // In tests, this value is used to override the return value of
     20 // UploadFileElementReader::GetContentLength() when set to non-zero.
     21 uint64 overriding_content_length = 0;
     22 
     23 }  // namespace
     24 
     25 UploadFileElementReader::UploadFileElementReader(
     26     base::TaskRunner* task_runner,
     27     const base::FilePath& path,
     28     uint64 range_offset,
     29     uint64 range_length,
     30     const base::Time& expected_modification_time)
     31     : task_runner_(task_runner),
     32       path_(path),
     33       range_offset_(range_offset),
     34       range_length_(range_length),
     35       expected_modification_time_(expected_modification_time),
     36       content_length_(0),
     37       bytes_remaining_(0),
     38       weak_ptr_factory_(this) {
     39   DCHECK(task_runner_.get());
     40 }
     41 
     42 UploadFileElementReader::~UploadFileElementReader() {
     43 }
     44 
     45 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const {
     46   return this;
     47 }
     48 
     49 int UploadFileElementReader::Init(const CompletionCallback& callback) {
     50   DCHECK(!callback.is_null());
     51   Reset();
     52 
     53   file_stream_.reset(new FileStream(task_runner_.get()));
     54   int result = file_stream_->Open(
     55       path_,
     56       base::File::FLAG_OPEN | base::File::FLAG_READ |
     57       base::File::FLAG_ASYNC,
     58       base::Bind(&UploadFileElementReader::OnOpenCompleted,
     59                  weak_ptr_factory_.GetWeakPtr(),
     60                  callback));
     61   DCHECK_GT(0, result);
     62   return result;
     63 }
     64 
     65 uint64 UploadFileElementReader::GetContentLength() const {
     66   if (overriding_content_length)
     67     return overriding_content_length;
     68   return content_length_;
     69 }
     70 
     71 uint64 UploadFileElementReader::BytesRemaining() const {
     72   return bytes_remaining_;
     73 }
     74 
     75 int UploadFileElementReader::Read(IOBuffer* buf,
     76                                   int buf_length,
     77                                   const CompletionCallback& callback) {
     78   DCHECK(!callback.is_null());
     79 
     80   uint64 num_bytes_to_read =
     81       std::min(BytesRemaining(), static_cast<uint64>(buf_length));
     82   if (num_bytes_to_read == 0)
     83     return 0;
     84 
     85   int result = file_stream_->Read(
     86       buf, num_bytes_to_read,
     87       base::Bind(base::IgnoreResult(&UploadFileElementReader::OnReadCompleted),
     88                  weak_ptr_factory_.GetWeakPtr(),
     89                  callback));
     90   // Even in async mode, FileStream::Read() may return the result synchronously.
     91   if (result != ERR_IO_PENDING)
     92     return OnReadCompleted(CompletionCallback(), result);
     93   return ERR_IO_PENDING;
     94 }
     95 
     96 void UploadFileElementReader::Reset() {
     97   weak_ptr_factory_.InvalidateWeakPtrs();
     98   bytes_remaining_ = 0;
     99   content_length_ = 0;
    100   file_stream_.reset();
    101 }
    102 
    103 void UploadFileElementReader::OnOpenCompleted(
    104     const CompletionCallback& callback,
    105     int result) {
    106   DCHECK(!callback.is_null());
    107 
    108   if (result < 0) {
    109     DLOG(WARNING) << "Failed to open \"" << path_.value()
    110                   << "\" for reading: " << result;
    111     callback.Run(result);
    112     return;
    113   }
    114 
    115   if (range_offset_) {
    116     int result = file_stream_->Seek(
    117         base::File::FROM_BEGIN, range_offset_,
    118         base::Bind(&UploadFileElementReader::OnSeekCompleted,
    119                    weak_ptr_factory_.GetWeakPtr(),
    120                    callback));
    121     DCHECK_GT(0, result);
    122     if (result != ERR_IO_PENDING)
    123       callback.Run(result);
    124   } else {
    125     OnSeekCompleted(callback, OK);
    126   }
    127 }
    128 
    129 void UploadFileElementReader::OnSeekCompleted(
    130     const CompletionCallback& callback,
    131     int64 result) {
    132   DCHECK(!callback.is_null());
    133 
    134   if (result < 0) {
    135     DLOG(WARNING) << "Failed to seek \"" << path_.value()
    136                   << "\" to offset: " << range_offset_ << " (" << result << ")";
    137     callback.Run(result);
    138     return;
    139   }
    140 
    141   base::File::Info* file_info = new base::File::Info;
    142   bool posted = base::PostTaskAndReplyWithResult(
    143       task_runner_.get(),
    144       FROM_HERE,
    145       base::Bind(&base::GetFileInfo, path_, file_info),
    146       base::Bind(&UploadFileElementReader::OnGetFileInfoCompleted,
    147                  weak_ptr_factory_.GetWeakPtr(),
    148                  callback,
    149                  base::Owned(file_info)));
    150   DCHECK(posted);
    151 }
    152 
    153 void UploadFileElementReader::OnGetFileInfoCompleted(
    154     const CompletionCallback& callback,
    155     base::File::Info* file_info,
    156     bool result) {
    157   DCHECK(!callback.is_null());
    158   if (!result) {
    159     DLOG(WARNING) << "Failed to get file info of \"" << path_.value() << "\"";
    160     callback.Run(ERR_FILE_NOT_FOUND);
    161     return;
    162   }
    163 
    164   int64 length = file_info->size;
    165   if (range_offset_ < static_cast<uint64>(length)) {
    166     // Compensate for the offset.
    167     length = std::min(length - range_offset_, range_length_);
    168   }
    169 
    170   // If the underlying file has been changed and the expected file modification
    171   // time is set, treat it as error. Note that the expected modification time
    172   // from WebKit is based on time_t precision. So we have to convert both to
    173   // time_t to compare. This check is used for sliced files.
    174   if (!expected_modification_time_.is_null() &&
    175       expected_modification_time_.ToTimeT() !=
    176       file_info->last_modified.ToTimeT()) {
    177     callback.Run(ERR_UPLOAD_FILE_CHANGED);
    178     return;
    179   }
    180 
    181   content_length_ = length;
    182   bytes_remaining_ = GetContentLength();
    183   callback.Run(OK);
    184 }
    185 
    186 int UploadFileElementReader::OnReadCompleted(
    187     const CompletionCallback& callback,
    188     int result) {
    189   if (result == 0)  // Reached end-of-file earlier than expected.
    190     result = ERR_UPLOAD_FILE_CHANGED;
    191 
    192   if (result > 0) {
    193     DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
    194     bytes_remaining_ -= result;
    195   }
    196 
    197   if (!callback.is_null())
    198     callback.Run(result);
    199   return result;
    200 }
    201 
    202 UploadFileElementReader::ScopedOverridingContentLengthForTests::
    203 ScopedOverridingContentLengthForTests(uint64 value) {
    204   overriding_content_length = value;
    205 }
    206 
    207 UploadFileElementReader::ScopedOverridingContentLengthForTests::
    208 ~ScopedOverridingContentLengthForTests() {
    209   overriding_content_length = 0;
    210 }
    211 
    212 }  // namespace net
    213