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