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 | |
101 } // namespace | 23 } // namespace |
102 | 24 |
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( | 25 UploadFileElementReader::UploadFileElementReader( |
120 base::TaskRunner* task_runner, | 26 base::TaskRunner* task_runner, |
121 const base::FilePath& path, | 27 const base::FilePath& path, |
122 uint64 range_offset, | 28 uint64 range_offset, |
123 uint64 range_length, | 29 uint64 range_length, |
124 const base::Time& expected_modification_time) | 30 const base::Time& expected_modification_time) |
125 : task_runner_(task_runner), | 31 : task_runner_(task_runner), |
126 path_(path), | 32 path_(path), |
127 range_offset_(range_offset), | 33 range_offset_(range_offset), |
128 range_length_(range_length), | 34 range_length_(range_length), |
129 expected_modification_time_(expected_modification_time), | 35 expected_modification_time_(expected_modification_time), |
130 file_stream_(NULL, FileStreamDeleter(task_runner_.get())), | |
131 content_length_(0), | 36 content_length_(0), |
132 bytes_remaining_(0), | 37 bytes_remaining_(0), |
133 weak_ptr_factory_(this) { | 38 weak_ptr_factory_(this) { |
134 DCHECK(task_runner_.get()); | 39 DCHECK(task_runner_.get()); |
135 } | 40 } |
136 | 41 |
137 UploadFileElementReader::~UploadFileElementReader() { | 42 UploadFileElementReader::~UploadFileElementReader() { |
138 } | 43 } |
139 | 44 |
140 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const { | 45 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const { |
141 return this; | 46 return this; |
142 } | 47 } |
143 | 48 |
144 int UploadFileElementReader::Init(const CompletionCallback& callback) { | 49 int UploadFileElementReader::Init(const CompletionCallback& callback) { |
145 DCHECK(!callback.is_null()); | 50 DCHECK(!callback.is_null()); |
146 Reset(); | 51 Reset(); |
147 | 52 |
148 ScopedFileStreamPtr* file_stream = | 53 file_stream_.reset(new FileStream(NULL, task_runner_.get())); |
149 new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get())); | 54 int result = file_stream_->Open( |
150 uint64* content_length = new uint64; | 55 path_, |
151 const bool posted = base::PostTaskAndReplyWithResult( | 56 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | |
152 task_runner_.get(), | 57 base::PLATFORM_FILE_ASYNC, |
153 FROM_HERE, | 58 base::Bind(&UploadFileElementReader::OnOpenCompleted, |
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(), | 59 weak_ptr_factory_.GetWeakPtr(), |
163 base::Owned(file_stream), | |
164 base::Owned(content_length), | |
165 callback)); | 60 callback)); |
166 DCHECK(posted); | 61 DCHECK_GT(0, result); |
167 return ERR_IO_PENDING; | 62 return result; |
168 } | 63 } |
169 | 64 |
170 uint64 UploadFileElementReader::GetContentLength() const { | 65 uint64 UploadFileElementReader::GetContentLength() const { |
171 if (overriding_content_length) | 66 if (overriding_content_length) |
172 return overriding_content_length; | 67 return overriding_content_length; |
173 return content_length_; | 68 return content_length_; |
174 } | 69 } |
175 | 70 |
176 uint64 UploadFileElementReader::BytesRemaining() const { | 71 uint64 UploadFileElementReader::BytesRemaining() const { |
177 return bytes_remaining_; | 72 return bytes_remaining_; |
178 } | 73 } |
179 | 74 |
180 int UploadFileElementReader::Read(IOBuffer* buf, | 75 int UploadFileElementReader::Read(IOBuffer* buf, |
181 int buf_length, | 76 int buf_length, |
182 const CompletionCallback& callback) { | 77 const CompletionCallback& callback) { |
183 DCHECK(!callback.is_null()); | 78 DCHECK(!callback.is_null()); |
184 | 79 |
185 if (BytesRemaining() == 0) | 80 uint64 num_bytes_to_read = |
| 81 std::min(BytesRemaining(), static_cast<uint64>(buf_length)); |
| 82 if (num_bytes_to_read == 0) |
186 return 0; | 83 return 0; |
187 | 84 |
188 // Save the value of file_stream_.get() before base::Passed() invalidates it. | 85 int result = file_stream_->Read( |
189 FileStream* file_stream_ptr = file_stream_.get(); | 86 buf, num_bytes_to_read, |
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, | 87 base::Bind(&UploadFileElementReader::OnReadCompleted, |
201 weak_ptr_factory_.GetWeakPtr(), | 88 weak_ptr_factory_.GetWeakPtr(), |
202 base::Passed(&file_stream_), | |
203 callback)); | 89 callback)); |
204 DCHECK(posted); | 90 DCHECK_GT(0, result); |
205 return ERR_IO_PENDING; | 91 return result; |
206 } | 92 } |
207 | 93 |
208 void UploadFileElementReader::Reset() { | 94 void UploadFileElementReader::Reset() { |
209 weak_ptr_factory_.InvalidateWeakPtrs(); | 95 weak_ptr_factory_.InvalidateWeakPtrs(); |
210 bytes_remaining_ = 0; | 96 bytes_remaining_ = 0; |
211 content_length_ = 0; | 97 content_length_ = 0; |
212 file_stream_.reset(); | 98 file_stream_.reset(); |
213 } | 99 } |
214 | 100 |
215 void UploadFileElementReader::OnInitCompleted( | 101 void UploadFileElementReader::OnOpenCompleted( |
216 ScopedFileStreamPtr* file_stream, | |
217 uint64* content_length, | |
218 const CompletionCallback& callback, | 102 const CompletionCallback& callback, |
219 int result) { | 103 int result) { |
220 file_stream_.swap(*file_stream); | 104 DCHECK(!callback.is_null()); |
221 content_length_ = *content_length; | 105 |
| 106 if (result < 0) { |
| 107 DLOG(WARNING) << "Failed to open \"" << path_.value() |
| 108 << "\" for reading: " << result; |
| 109 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; |
222 bytes_remaining_ = GetContentLength(); | 182 bytes_remaining_ = GetContentLength(); |
223 if (!callback.is_null()) | 183 callback.Run(OK); |
224 callback.Run(result); | |
225 } | 184 } |
226 | 185 |
227 void UploadFileElementReader::OnReadCompleted( | 186 void UploadFileElementReader::OnReadCompleted( |
228 ScopedFileStreamPtr file_stream, | |
229 const CompletionCallback& callback, | 187 const CompletionCallback& callback, |
230 int result) { | 188 int result) { |
231 file_stream_.swap(file_stream); | 189 DCHECK(!callback.is_null()); |
| 190 |
| 191 if (result == 0) // Reached end-of-file earlier than expected. |
| 192 result = ERR_UPLOAD_FILE_CHANGED; |
| 193 |
232 if (result > 0) { | 194 if (result > 0) { |
233 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); | 195 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); |
234 bytes_remaining_ -= result; | 196 bytes_remaining_ -= result; |
235 } | 197 } |
236 if (!callback.is_null()) | 198 callback.Run(result); |
237 callback.Run(result); | |
238 } | 199 } |
239 | 200 |
240 UploadFileElementReader::ScopedOverridingContentLengthForTests:: | 201 UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
241 ScopedOverridingContentLengthForTests(uint64 value) { | 202 ScopedOverridingContentLengthForTests(uint64 value) { |
242 overriding_content_length = value; | 203 overriding_content_length = value; |
243 } | 204 } |
244 | 205 |
245 UploadFileElementReader::ScopedOverridingContentLengthForTests:: | 206 UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
246 ~ScopedOverridingContentLengthForTests() { | 207 ~ScopedOverridingContentLengthForTests() { |
247 overriding_content_length = 0; | 208 overriding_content_length = 0; |
248 } | 209 } |
249 | 210 |
250 } // namespace net | 211 } // namespace net |
OLD | NEW |