| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/upload_file_element_reader.h" | 5 #include "net/base/upload_file_element_reader.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/task_runner_util.h" | 10 #include "base/task_runner_util.h" |
| 11 #include "net/base/file_stream.h" | 11 #include "net/base/file_stream.h" |
| 12 #include "net/base/io_buffer.h" | 12 #include "net/base/io_buffer.h" |
| 13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
| 14 | 14 |
| 15 namespace net { | 15 namespace net { |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 | 18 |
| 19 // In tests, this value is used to override the return value of | 19 // In tests, this value is used to override the return value of |
| 20 // UploadFileElementReader::GetContentLength() when set to non-zero. | 20 // UploadFileElementReader::GetContentLength() when set to non-zero. |
| 21 uint64 overriding_content_length = 0; | 21 uint64 overriding_content_length = 0; |
| 22 | 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::File::Info 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 |
| 23 } // namespace | 101 } // namespace |
| 24 | 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 |
| 25 UploadFileElementReader::UploadFileElementReader( | 119 UploadFileElementReader::UploadFileElementReader( |
| 26 base::TaskRunner* task_runner, | 120 base::TaskRunner* task_runner, |
| 27 const base::FilePath& path, | 121 const base::FilePath& path, |
| 28 uint64 range_offset, | 122 uint64 range_offset, |
| 29 uint64 range_length, | 123 uint64 range_length, |
| 30 const base::Time& expected_modification_time) | 124 const base::Time& expected_modification_time) |
| 31 : task_runner_(task_runner), | 125 : task_runner_(task_runner), |
| 32 path_(path), | 126 path_(path), |
| 33 range_offset_(range_offset), | 127 range_offset_(range_offset), |
| 34 range_length_(range_length), | 128 range_length_(range_length), |
| 35 expected_modification_time_(expected_modification_time), | 129 expected_modification_time_(expected_modification_time), |
| 130 file_stream_(NULL, FileStreamDeleter(task_runner_.get())), |
| 36 content_length_(0), | 131 content_length_(0), |
| 37 bytes_remaining_(0), | 132 bytes_remaining_(0), |
| 38 weak_ptr_factory_(this) { | 133 weak_ptr_factory_(this) { |
| 39 DCHECK(task_runner_.get()); | 134 DCHECK(task_runner_.get()); |
| 40 } | 135 } |
| 41 | 136 |
| 42 UploadFileElementReader::~UploadFileElementReader() { | 137 UploadFileElementReader::~UploadFileElementReader() { |
| 43 } | 138 } |
| 44 | 139 |
| 45 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const { | 140 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const { |
| 46 return this; | 141 return this; |
| 47 } | 142 } |
| 48 | 143 |
| 49 int UploadFileElementReader::Init(const CompletionCallback& callback) { | 144 int UploadFileElementReader::Init(const CompletionCallback& callback) { |
| 50 DCHECK(!callback.is_null()); | 145 DCHECK(!callback.is_null()); |
| 51 Reset(); | 146 Reset(); |
| 52 | 147 |
| 53 file_stream_.reset(new FileStream(NULL, task_runner_.get())); | 148 ScopedFileStreamPtr* file_stream = |
| 54 int result = file_stream_->Open( | 149 new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get())); |
| 55 path_, | 150 uint64* content_length = new uint64; |
| 56 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | | 151 const bool posted = base::PostTaskAndReplyWithResult( |
| 57 base::PLATFORM_FILE_ASYNC, | 152 task_runner_.get(), |
| 58 base::Bind(&UploadFileElementReader::OnOpenCompleted, | 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, |
| 59 weak_ptr_factory_.GetWeakPtr(), | 162 weak_ptr_factory_.GetWeakPtr(), |
| 163 base::Owned(file_stream), |
| 164 base::Owned(content_length), |
| 60 callback)); | 165 callback)); |
| 61 DCHECK_GT(0, result); | 166 DCHECK(posted); |
| 62 return result; | 167 return ERR_IO_PENDING; |
| 63 } | 168 } |
| 64 | 169 |
| 65 uint64 UploadFileElementReader::GetContentLength() const { | 170 uint64 UploadFileElementReader::GetContentLength() const { |
| 66 if (overriding_content_length) | 171 if (overriding_content_length) |
| 67 return overriding_content_length; | 172 return overriding_content_length; |
| 68 return content_length_; | 173 return content_length_; |
| 69 } | 174 } |
| 70 | 175 |
| 71 uint64 UploadFileElementReader::BytesRemaining() const { | 176 uint64 UploadFileElementReader::BytesRemaining() const { |
| 72 return bytes_remaining_; | 177 return bytes_remaining_; |
| 73 } | 178 } |
| 74 | 179 |
| 75 int UploadFileElementReader::Read(IOBuffer* buf, | 180 int UploadFileElementReader::Read(IOBuffer* buf, |
| 76 int buf_length, | 181 int buf_length, |
| 77 const CompletionCallback& callback) { | 182 const CompletionCallback& callback) { |
| 78 DCHECK(!callback.is_null()); | 183 DCHECK(!callback.is_null()); |
| 79 | 184 |
| 80 uint64 num_bytes_to_read = | 185 if (BytesRemaining() == 0) |
| 81 std::min(BytesRemaining(), static_cast<uint64>(buf_length)); | |
| 82 if (num_bytes_to_read == 0) | |
| 83 return 0; | 186 return 0; |
| 84 | 187 |
| 85 int result = file_stream_->Read( | 188 // Save the value of file_stream_.get() before base::Passed() invalidates it. |
| 86 buf, num_bytes_to_read, | 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), |
| 87 base::Bind(&UploadFileElementReader::OnReadCompleted, | 200 base::Bind(&UploadFileElementReader::OnReadCompleted, |
| 88 weak_ptr_factory_.GetWeakPtr(), | 201 weak_ptr_factory_.GetWeakPtr(), |
| 202 base::Passed(&file_stream_), |
| 89 callback)); | 203 callback)); |
| 90 DCHECK_GT(0, result); | 204 DCHECK(posted); |
| 91 return result; | 205 return ERR_IO_PENDING; |
| 92 } | 206 } |
| 93 | 207 |
| 94 void UploadFileElementReader::Reset() { | 208 void UploadFileElementReader::Reset() { |
| 95 weak_ptr_factory_.InvalidateWeakPtrs(); | 209 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 96 bytes_remaining_ = 0; | 210 bytes_remaining_ = 0; |
| 97 content_length_ = 0; | 211 content_length_ = 0; |
| 98 file_stream_.reset(); | 212 file_stream_.reset(); |
| 99 } | 213 } |
| 100 | 214 |
| 101 void UploadFileElementReader::OnOpenCompleted( | 215 void UploadFileElementReader::OnInitCompleted( |
| 216 ScopedFileStreamPtr* file_stream, |
| 217 uint64* content_length, |
| 102 const CompletionCallback& callback, | 218 const CompletionCallback& callback, |
| 103 int result) { | 219 int result) { |
| 104 DCHECK(!callback.is_null()); | 220 file_stream_.swap(*file_stream); |
| 105 | 221 content_length_ = *content_length; |
| 106 if (result < 0) { | 222 bytes_remaining_ = GetContentLength(); |
| 107 DLOG(WARNING) << "Failed to open \"" << path_.value() | 223 if (!callback.is_null()) |
| 108 << "\" for reading: " << result; | |
| 109 callback.Run(result); | 224 callback.Run(result); |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 if (range_offset_) { | |
| 114 int result = file_stream_->Seek( | |
| 115 FROM_BEGIN, range_offset_, | |
| 116 base::Bind(&UploadFileElementReader::OnSeekCompleted, | |
| 117 weak_ptr_factory_.GetWeakPtr(), | |
| 118 callback)); | |
| 119 DCHECK_GT(0, result); | |
| 120 if (result != ERR_IO_PENDING) | |
| 121 callback.Run(result); | |
| 122 } else { | |
| 123 OnSeekCompleted(callback, OK); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 void UploadFileElementReader::OnSeekCompleted( | |
| 128 const CompletionCallback& callback, | |
| 129 int64 result) { | |
| 130 DCHECK(!callback.is_null()); | |
| 131 | |
| 132 if (result < 0) { | |
| 133 DLOG(WARNING) << "Failed to seek \"" << path_.value() | |
| 134 << "\" to offset: " << range_offset_ << " (" << result << ")"; | |
| 135 callback.Run(result); | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 base::File::Info* file_info = new base::File::Info; | |
| 140 bool posted = base::PostTaskAndReplyWithResult( | |
| 141 task_runner_, | |
| 142 FROM_HERE, | |
| 143 base::Bind(&base::GetFileInfo, | |
| 144 path_, | |
| 145 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 } | 225 } |
| 185 | 226 |
| 186 void UploadFileElementReader::OnReadCompleted( | 227 void UploadFileElementReader::OnReadCompleted( |
| 228 ScopedFileStreamPtr file_stream, |
| 187 const CompletionCallback& callback, | 229 const CompletionCallback& callback, |
| 188 int result) { | 230 int result) { |
| 189 DCHECK(!callback.is_null()); | 231 file_stream_.swap(file_stream); |
| 190 | |
| 191 if (result == 0) // Reached end-of-file earlier than expected. | |
| 192 result = ERR_UPLOAD_FILE_CHANGED; | |
| 193 | |
| 194 if (result > 0) { | 232 if (result > 0) { |
| 195 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); | 233 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); |
| 196 bytes_remaining_ -= result; | 234 bytes_remaining_ -= result; |
| 197 } | 235 } |
| 198 callback.Run(result); | 236 if (!callback.is_null()) |
| 237 callback.Run(result); |
| 199 } | 238 } |
| 200 | 239 |
| 201 UploadFileElementReader::ScopedOverridingContentLengthForTests:: | 240 UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
| 202 ScopedOverridingContentLengthForTests(uint64 value) { | 241 ScopedOverridingContentLengthForTests(uint64 value) { |
| 203 overriding_content_length = value; | 242 overriding_content_length = value; |
| 204 } | 243 } |
| 205 | 244 |
| 206 UploadFileElementReader::ScopedOverridingContentLengthForTests:: | 245 UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
| 207 ~ScopedOverridingContentLengthForTests() { | 246 ~ScopedOverridingContentLengthForTests() { |
| 208 overriding_content_length = 0; | 247 overriding_content_length = 0; |
| 209 } | 248 } |
| 210 | 249 |
| 211 } // namespace net | 250 } // namespace net |
| OLD | NEW |