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 |