xref: /aosp_15_r20/external/cronet/net/base/upload_file_element_reader.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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 <memory>
8 
9 #include "base/files/file_util.h"
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/task/task_runner.h"
13 #include "net/base/file_stream.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 
17 namespace net {
18 
19 namespace {
20 
21 // In tests, this value is used to override the return value of
22 // UploadFileElementReader::GetContentLength() when set to non-zero.
23 uint64_t overriding_content_length = 0;
24 
25 }  // namespace
26 
UploadFileElementReader(base::TaskRunner * task_runner,base::File file,const base::FilePath & path,uint64_t range_offset,uint64_t range_length,const base::Time & expected_modification_time)27 UploadFileElementReader::UploadFileElementReader(
28     base::TaskRunner* task_runner,
29     base::File file,
30     const base::FilePath& path,
31     uint64_t range_offset,
32     uint64_t range_length,
33     const base::Time& expected_modification_time)
34     : task_runner_(task_runner),
35       path_(path),
36       range_offset_(range_offset),
37       range_length_(range_length),
38       expected_modification_time_(expected_modification_time) {
39   DCHECK(file.IsValid());
40   DCHECK(task_runner_.get());
41   file_stream_ = std::make_unique<FileStream>(std::move(file), task_runner);
42 }
43 
UploadFileElementReader(base::TaskRunner * task_runner,const base::FilePath & path,uint64_t range_offset,uint64_t range_length,const base::Time & expected_modification_time)44 UploadFileElementReader::UploadFileElementReader(
45     base::TaskRunner* task_runner,
46     const base::FilePath& path,
47     uint64_t range_offset,
48     uint64_t range_length,
49     const base::Time& expected_modification_time)
50     : task_runner_(task_runner),
51       path_(path),
52       range_offset_(range_offset),
53       range_length_(range_length),
54       expected_modification_time_(expected_modification_time) {
55   DCHECK(task_runner_.get());
56 }
57 
58 UploadFileElementReader::~UploadFileElementReader() = default;
59 
AsFileReader() const60 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const {
61   return this;
62 }
63 
Init(CompletionOnceCallback callback)64 int UploadFileElementReader::Init(CompletionOnceCallback callback) {
65   DCHECK(!callback.is_null());
66 
67   bytes_remaining_ = 0;
68   content_length_ = 0;
69   pending_callback_.Reset();
70 
71   // If the file is being opened, just update the callback, and continue
72   // waiting.
73   if (next_state_ == State::OPEN_COMPLETE) {
74     DCHECK(file_stream_);
75     pending_callback_ = std::move(callback);
76     return ERR_IO_PENDING;
77   }
78 
79   // If there's already a pending operation, wait for it to complete before
80   // restarting the request.
81   if (next_state_ != State::IDLE) {
82     init_called_while_operation_pending_ = true;
83     pending_callback_ = std::move(callback);
84     return ERR_IO_PENDING;
85   }
86 
87   DCHECK(!init_called_while_operation_pending_);
88 
89   if (file_stream_) {
90     // If the file is already open, just re-use it.
91     // TODO(mmenke): Consider reusing file info, too.
92     next_state_ = State::SEEK;
93   } else {
94     next_state_ = State::OPEN;
95   }
96   int result = DoLoop(OK);
97   if (result == ERR_IO_PENDING)
98     pending_callback_ = std::move(callback);
99   return result;
100 }
101 
GetContentLength() const102 uint64_t UploadFileElementReader::GetContentLength() const {
103   if (overriding_content_length)
104     return overriding_content_length;
105   return content_length_;
106 }
107 
BytesRemaining() const108 uint64_t UploadFileElementReader::BytesRemaining() const {
109   return bytes_remaining_;
110 }
111 
Read(IOBuffer * buf,int buf_length,CompletionOnceCallback callback)112 int UploadFileElementReader::Read(IOBuffer* buf,
113                                   int buf_length,
114                                   CompletionOnceCallback callback) {
115   DCHECK(!callback.is_null());
116   DCHECK_EQ(next_state_, State::IDLE);
117   DCHECK(file_stream_);
118 
119   int num_bytes_to_read = static_cast<int>(
120       std::min(BytesRemaining(), static_cast<uint64_t>(buf_length)));
121   if (num_bytes_to_read == 0)
122     return 0;
123 
124   next_state_ = State::READ_COMPLETE;
125   int result = file_stream_->Read(
126       buf, num_bytes_to_read,
127       base::BindOnce(base::IgnoreResult(&UploadFileElementReader::OnIOComplete),
128                      weak_ptr_factory_.GetWeakPtr()));
129 
130   if (result != ERR_IO_PENDING)
131     result = DoLoop(result);
132 
133   if (result == ERR_IO_PENDING)
134     pending_callback_ = std::move(callback);
135 
136   return result;
137 }
138 
DoLoop(int result)139 int UploadFileElementReader::DoLoop(int result) {
140   DCHECK_NE(result, ERR_IO_PENDING);
141 
142   if (init_called_while_operation_pending_) {
143     // File should already have been opened successfully.
144     DCHECK_NE(next_state_, State::OPEN_COMPLETE);
145 
146     next_state_ = State::SEEK;
147     init_called_while_operation_pending_ = false;
148     result = net::OK;
149   }
150 
151   while (next_state_ != State::IDLE && result != ERR_IO_PENDING) {
152     State state = next_state_;
153     next_state_ = State::IDLE;
154     switch (state) {
155       case State::IDLE:
156         NOTREACHED();
157         break;
158       case State::OPEN:
159         // Ignore previous result here. It's typically OK, but if Init()
160         // interrupted the previous operation, it may be an error.
161         result = DoOpen();
162         break;
163       case State::OPEN_COMPLETE:
164         result = DoOpenComplete(result);
165         break;
166       case State::SEEK:
167         DCHECK_EQ(OK, result);
168         result = DoSeek();
169         break;
170       case State::GET_FILE_INFO:
171         result = DoGetFileInfo(result);
172         break;
173       case State::GET_FILE_INFO_COMPLETE:
174         result = DoGetFileInfoComplete(result);
175         break;
176 
177       case State::READ_COMPLETE:
178         result = DoReadComplete(result);
179         break;
180     }
181   }
182 
183   return result;
184 }
185 
DoOpen()186 int UploadFileElementReader::DoOpen() {
187   DCHECK(!file_stream_);
188 
189   next_state_ = State::OPEN_COMPLETE;
190 
191   file_stream_ = std::make_unique<FileStream>(task_runner_.get());
192   int result = file_stream_->Open(
193       path_,
194       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC,
195       base::BindOnce(&UploadFileElementReader::OnIOComplete,
196                      weak_ptr_factory_.GetWeakPtr()));
197   DCHECK_GT(0, result);
198   return result;
199 }
200 
DoOpenComplete(int result)201 int UploadFileElementReader::DoOpenComplete(int result) {
202   if (result < 0) {
203     DLOG(WARNING) << "Failed to open \"" << path_.value()
204                   << "\" for reading: " << result;
205     file_stream_.reset();
206     return result;
207   }
208 
209   if (range_offset_) {
210     next_state_ = State::SEEK;
211   } else {
212     next_state_ = State::GET_FILE_INFO;
213   }
214   return net::OK;
215 }
216 
DoSeek()217 int UploadFileElementReader::DoSeek() {
218   next_state_ = State::GET_FILE_INFO;
219   return file_stream_->Seek(
220       range_offset_,
221       base::BindOnce(
222           [](base::WeakPtr<UploadFileElementReader> weak_this, int64_t result) {
223             if (!weak_this)
224               return;
225             weak_this->OnIOComplete(result >= 0 ? OK
226                                                 : static_cast<int>(result));
227           },
228           weak_ptr_factory_.GetWeakPtr()));
229 }
230 
DoGetFileInfo(int result)231 int UploadFileElementReader::DoGetFileInfo(int result) {
232   if (result < 0) {
233     DLOG(WARNING) << "Failed to seek \"" << path_.value()
234                   << "\" to offset: " << range_offset_ << " (" << result << ")";
235     return result;
236   }
237 
238   next_state_ = State::GET_FILE_INFO_COMPLETE;
239 
240   auto file_info = std::make_unique<base::File::Info>();
241   auto* file_info_ptr = file_info.get();
242   result = file_stream_->GetFileInfo(
243       file_info_ptr,
244       base::BindOnce(
245           [](base::WeakPtr<UploadFileElementReader> weak_this,
246              std::unique_ptr<base::File::Info> file_info, int result) {
247             if (!weak_this)
248               return;
249             weak_this->file_info_ = *file_info;
250             weak_this->OnIOComplete(result);
251           },
252           weak_ptr_factory_.GetWeakPtr(), std::move(file_info)));
253   // GetFileInfo() can't succeed synchronously.
254   DCHECK_NE(result, OK);
255   return result;
256 }
257 
DoGetFileInfoComplete(int result)258 int UploadFileElementReader::DoGetFileInfoComplete(int result) {
259   if (result != OK) {
260     DLOG(WARNING) << "Failed to get file info of \"" << path_.value() << "\"";
261     return result;
262   }
263 
264   int64_t length = file_info_.size;
265   if (range_offset_ < static_cast<uint64_t>(length)) {
266     // Compensate for the offset.
267     length = std::min(length - range_offset_, range_length_);
268   }
269 
270   // If the underlying file has been changed and the expected file modification
271   // time is set, treat it as error. Note that |expected_modification_time_| may
272   // have gone through multiple conversion steps involving loss of precision
273   // (including conversion to time_t). Therefore the check below only verifies
274   // that the timestamps are within one second of each other. This check is used
275   // for sliced files.
276   if (!expected_modification_time_.is_null() &&
277       (expected_modification_time_ - file_info_.last_modified)
278               .magnitude()
279               .InSeconds() != 0) {
280     return ERR_UPLOAD_FILE_CHANGED;
281   }
282 
283   content_length_ = length;
284   bytes_remaining_ = GetContentLength();
285   return result;
286 }
287 
DoReadComplete(int result)288 int UploadFileElementReader::DoReadComplete(int result) {
289   if (result == 0)  // Reached end-of-file earlier than expected.
290     return ERR_UPLOAD_FILE_CHANGED;
291 
292   if (result > 0) {
293     DCHECK_GE(bytes_remaining_, static_cast<uint64_t>(result));
294     bytes_remaining_ -= result;
295   }
296 
297   return result;
298 }
299 
OnIOComplete(int result)300 void UploadFileElementReader::OnIOComplete(int result) {
301   DCHECK(pending_callback_);
302 
303   result = DoLoop(result);
304 
305   if (result != ERR_IO_PENDING)
306     std::move(pending_callback_).Run(result);
307 }
308 
309 UploadFileElementReader::ScopedOverridingContentLengthForTests::
ScopedOverridingContentLengthForTests(uint64_t value)310     ScopedOverridingContentLengthForTests(uint64_t value) {
311   overriding_content_length = value;
312 }
313 
314 UploadFileElementReader::ScopedOverridingContentLengthForTests::
~ScopedOverridingContentLengthForTests()315 ~ScopedOverridingContentLengthForTests() {
316   overriding_content_length = 0;
317 }
318 
319 }  // namespace net
320