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/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 // This function is used to implement Init().
     24 template<typename FileStreamDeleter>
     25 int InitInternal(const base::FilePath& path,
     26                  uint64 range_offset,
     27                  uint64 range_length,
     28                  const base::Time& expected_modification_time,
     29                  scoped_ptr<FileStream, FileStreamDeleter>* out_file_stream,
     30                  uint64* out_content_length) {
     31   scoped_ptr<FileStream> file_stream(new FileStream(NULL));
     32   int64 rv = file_stream->OpenSync(
     33       path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
     34   if (rv != OK) {
     35     // If the file can't be opened, the upload should fail.
     36     DLOG(WARNING) << "Failed to open \"" << path.value()
     37                   << "\" for reading: " << rv;
     38     return rv;
     39   } else if (range_offset) {
     40     rv = file_stream->SeekSync(FROM_BEGIN, range_offset);
     41     if (rv < 0) {
     42       DLOG(WARNING) << "Failed to seek \"" << path.value()
     43                     << "\" to offset: " << range_offset << " (" << rv << ")";
     44       return rv;
     45     }
     46   }
     47 
     48   int64 length = 0;
     49   if (!base::GetFileSize(path, &length)) {
     50     DLOG(WARNING) << "Failed to get file size of \"" << path.value() << "\"";
     51     return ERR_FILE_NOT_FOUND;
     52   }
     53 
     54   if (range_offset < static_cast<uint64>(length)) {
     55     // Compensate for the offset.
     56     length = std::min(length - range_offset, range_length);
     57   }
     58 
     59   // If the underlying file has been changed and the expected file modification
     60   // time is set, treat it as error. Note that the expected modification time
     61   // from WebKit is based on time_t precision. So we have to convert both to
     62   // time_t to compare. This check is used for sliced files.
     63   if (!expected_modification_time.is_null()) {
     64     base::PlatformFileInfo info;
     65     if (!base::GetFileInfo(path, &info)) {
     66       DLOG(WARNING) << "Failed to get file info of \"" << path.value() << "\"";
     67       return ERR_FILE_NOT_FOUND;
     68     }
     69 
     70     if (expected_modification_time.ToTimeT() != info.last_modified.ToTimeT()) {
     71       return ERR_UPLOAD_FILE_CHANGED;
     72     }
     73   }
     74 
     75   *out_content_length = length;
     76   out_file_stream->reset(file_stream.release());
     77 
     78   return OK;
     79 }
     80 
     81 // This function is used to implement Read().
     82 int ReadInternal(scoped_refptr<IOBuffer> buf,
     83                  int buf_length,
     84                  uint64 bytes_remaining,
     85                  FileStream* file_stream) {
     86   DCHECK_LT(0, buf_length);
     87 
     88   const uint64 num_bytes_to_read =
     89       std::min(bytes_remaining, static_cast<uint64>(buf_length));
     90 
     91   int result = 0;
     92   if (num_bytes_to_read > 0) {
     93     DCHECK(file_stream);  // file_stream is non-null if content_length_ > 0.
     94     result = file_stream->ReadSync(buf->data(), num_bytes_to_read);
     95     if (result == 0)  // Reached end-of-file earlier than expected.
     96       result = ERR_UPLOAD_FILE_CHANGED;
     97   }
     98   return result;
     99 }
    100 
    101 }  // namespace
    102 
    103 UploadFileElementReader::FileStreamDeleter::FileStreamDeleter(
    104     base::TaskRunner* task_runner) : task_runner_(task_runner) {
    105   DCHECK(task_runner_.get());
    106 }
    107 
    108 UploadFileElementReader::FileStreamDeleter::~FileStreamDeleter() {}
    109 
    110 void UploadFileElementReader::FileStreamDeleter::operator() (
    111     FileStream* file_stream) const {
    112   if (file_stream) {
    113     task_runner_->PostTask(FROM_HERE,
    114                            base::Bind(&base::DeletePointer<FileStream>,
    115                                       file_stream));
    116   }
    117 }
    118 
    119 UploadFileElementReader::UploadFileElementReader(
    120     base::TaskRunner* task_runner,
    121     const base::FilePath& path,
    122     uint64 range_offset,
    123     uint64 range_length,
    124     const base::Time& expected_modification_time)
    125     : task_runner_(task_runner),
    126       path_(path),
    127       range_offset_(range_offset),
    128       range_length_(range_length),
    129       expected_modification_time_(expected_modification_time),
    130       file_stream_(NULL, FileStreamDeleter(task_runner_.get())),
    131       content_length_(0),
    132       bytes_remaining_(0),
    133       weak_ptr_factory_(this) {
    134   DCHECK(task_runner_.get());
    135 }
    136 
    137 UploadFileElementReader::~UploadFileElementReader() {
    138 }
    139 
    140 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const {
    141   return this;
    142 }
    143 
    144 int UploadFileElementReader::Init(const CompletionCallback& callback) {
    145   DCHECK(!callback.is_null());
    146   Reset();
    147 
    148   ScopedFileStreamPtr* file_stream =
    149       new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get()));
    150   uint64* content_length = new uint64;
    151   const bool posted = base::PostTaskAndReplyWithResult(
    152       task_runner_.get(),
    153       FROM_HERE,
    154       base::Bind(&InitInternal<FileStreamDeleter>,
    155                  path_,
    156                  range_offset_,
    157                  range_length_,
    158                  expected_modification_time_,
    159                  file_stream,
    160                  content_length),
    161       base::Bind(&UploadFileElementReader::OnInitCompleted,
    162                  weak_ptr_factory_.GetWeakPtr(),
    163                  base::Owned(file_stream),
    164                  base::Owned(content_length),
    165                  callback));
    166   DCHECK(posted);
    167   return ERR_IO_PENDING;
    168 }
    169 
    170 uint64 UploadFileElementReader::GetContentLength() const {
    171   if (overriding_content_length)
    172     return overriding_content_length;
    173   return content_length_;
    174 }
    175 
    176 uint64 UploadFileElementReader::BytesRemaining() const {
    177   return bytes_remaining_;
    178 }
    179 
    180 int UploadFileElementReader::Read(IOBuffer* buf,
    181                                   int buf_length,
    182                                   const CompletionCallback& callback) {
    183   DCHECK(!callback.is_null());
    184 
    185   if (BytesRemaining() == 0)
    186     return 0;
    187 
    188   // Save the value of file_stream_.get() before base::Passed() invalidates it.
    189   FileStream* file_stream_ptr = file_stream_.get();
    190   // Pass the ownership of file_stream_ to the worker pool to safely perform
    191   // operation even when |this| is destructed before the read completes.
    192   const bool posted = base::PostTaskAndReplyWithResult(
    193       task_runner_.get(),
    194       FROM_HERE,
    195       base::Bind(&ReadInternal,
    196                  scoped_refptr<IOBuffer>(buf),
    197                  buf_length,
    198                  BytesRemaining(),
    199                  file_stream_ptr),
    200       base::Bind(&UploadFileElementReader::OnReadCompleted,
    201                  weak_ptr_factory_.GetWeakPtr(),
    202                  base::Passed(&file_stream_),
    203                  callback));
    204   DCHECK(posted);
    205   return ERR_IO_PENDING;
    206 }
    207 
    208 void UploadFileElementReader::Reset() {
    209   weak_ptr_factory_.InvalidateWeakPtrs();
    210   bytes_remaining_ = 0;
    211   content_length_ = 0;
    212   file_stream_.reset();
    213 }
    214 
    215 void UploadFileElementReader::OnInitCompleted(
    216     ScopedFileStreamPtr* file_stream,
    217     uint64* content_length,
    218     const CompletionCallback& callback,
    219     int result) {
    220   file_stream_.swap(*file_stream);
    221   content_length_ = *content_length;
    222   bytes_remaining_ = GetContentLength();
    223   if (!callback.is_null())
    224     callback.Run(result);
    225 }
    226 
    227 void UploadFileElementReader::OnReadCompleted(
    228     ScopedFileStreamPtr file_stream,
    229     const CompletionCallback& callback,
    230     int result) {
    231   file_stream_.swap(file_stream);
    232   if (result > 0) {
    233     DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
    234     bytes_remaining_ -= result;
    235   }
    236   if (!callback.is_null())
    237     callback.Run(result);
    238 }
    239 
    240 UploadFileElementReader::ScopedOverridingContentLengthForTests::
    241 ScopedOverridingContentLengthForTests(uint64 value) {
    242   overriding_content_length = value;
    243 }
    244 
    245 UploadFileElementReader::ScopedOverridingContentLengthForTests::
    246 ~ScopedOverridingContentLengthForTests() {
    247   overriding_content_length = 0;
    248 }
    249 
    250 UploadFileElementReaderSync::UploadFileElementReaderSync(
    251     const base::FilePath& path,
    252     uint64 range_offset,
    253     uint64 range_length,
    254     const base::Time& expected_modification_time)
    255     : path_(path),
    256       range_offset_(range_offset),
    257       range_length_(range_length),
    258       expected_modification_time_(expected_modification_time),
    259       content_length_(0),
    260       bytes_remaining_(0) {
    261 }
    262 
    263 UploadFileElementReaderSync::~UploadFileElementReaderSync() {
    264 }
    265 
    266 int UploadFileElementReaderSync::Init(const CompletionCallback& callback) {
    267   bytes_remaining_ = 0;
    268   content_length_ = 0;
    269   file_stream_.reset();
    270 
    271   const int result = InitInternal(path_, range_offset_, range_length_,
    272                                   expected_modification_time_,
    273                                   &file_stream_, &content_length_);
    274   bytes_remaining_ = GetContentLength();
    275   return result;
    276 }
    277 
    278 uint64 UploadFileElementReaderSync::GetContentLength() const {
    279   return content_length_;
    280 }
    281 
    282 uint64 UploadFileElementReaderSync::BytesRemaining() const {
    283   return bytes_remaining_;
    284 }
    285 
    286 int UploadFileElementReaderSync::Read(IOBuffer* buf,
    287                                       int buf_length,
    288                                       const CompletionCallback& callback) {
    289   const int result = ReadInternal(buf, buf_length, BytesRemaining(),
    290                                   file_stream_.get());
    291   if (result > 0) {
    292     DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
    293     bytes_remaining_ -= result;
    294   }
    295   return result;
    296 }
    297 
    298 }  // namespace net
    299