1 //
2 // Copyright (C) 2010 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "update_engine/common/multi_range_http_fetcher.h"
18 
19 #include <base/strings/stringprintf.h>
20 
21 #include <algorithm>
22 #include <string>
23 
24 namespace chromeos_update_engine {
25 
26 // Begins the transfer to the specified URL.
27 // State change: Stopped -> Downloading
28 // (corner case: Stopped -> Stopped for an empty request)
BeginTransfer(const std::string & url)29 void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
30   CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
31   CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
32   CHECK(!terminating_) << "BeginTransfer but terminating.";
33 
34   if (ranges_.empty()) {
35     // Note that after the callback returns this object may be destroyed.
36     if (delegate_)
37       delegate_->TransferComplete(this, true);
38     return;
39   }
40   url_ = url;
41   current_index_ = 0;
42   bytes_received_this_range_ = 0;
43   LOG(INFO) << "starting first transfer";
44   base_fetcher_->set_delegate(this);
45   StartTransfer();
46 }
47 
48 // State change: Downloading -> Pending transfer ended
TerminateTransfer()49 void MultiRangeHttpFetcher::TerminateTransfer() {
50   if (!base_fetcher_active_) {
51     LOG(INFO) << "Called TerminateTransfer but not active.";
52     // Note that after the callback returns this object may be destroyed.
53     if (delegate_)
54       delegate_->TransferTerminated(this);
55     return;
56   }
57   terminating_ = true;
58 
59   if (!pending_transfer_ended_) {
60     pending_transfer_ended_ = true;
61     base_fetcher_->TerminateTransfer();
62   }
63 }
64 
65 // State change: Stopped or Downloading -> Downloading
StartTransfer()66 void MultiRangeHttpFetcher::StartTransfer() {
67   if (current_index_ >= ranges_.size()) {
68     return;
69   }
70 
71   Range range = ranges_[current_index_];
72   LOG(INFO) << "starting transfer of range " << range.ToString();
73 
74   bytes_received_this_range_ = 0;
75   base_fetcher_->SetOffset(range.offset());
76   if (range.HasLength())
77     base_fetcher_->SetLength(range.length());
78   else
79     base_fetcher_->UnsetLength();
80   if (delegate_)
81     delegate_->SeekToOffset(range.offset());
82   base_fetcher_active_ = true;
83   base_fetcher_->BeginTransfer(url_);
84 }
85 
86 // State change: Downloading -> Downloading or Pending transfer ended
ReceivedBytes(HttpFetcher * fetcher,const void * bytes,size_t length)87 bool MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
88                                           const void* bytes,
89                                           size_t length) {
90   CHECK_LT(current_index_, ranges_.size());
91   CHECK_EQ(fetcher, base_fetcher_.get());
92   CHECK(!pending_transfer_ended_);
93   size_t next_size = length;
94   Range range = ranges_[current_index_];
95   if (range.HasLength()) {
96     next_size =
97         std::min(next_size, range.length() - bytes_received_this_range_);
98   }
99   LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
100   // bytes_received_this_range_ needs to be updated regardless of the delegate_
101   // result, because it will be used to determine a successful transfer in
102   // TransferEnded().
103   bytes_received_this_range_ += length;
104   if (delegate_ && !delegate_->ReceivedBytes(this, bytes, next_size))
105     return false;
106 
107   if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
108     // Terminates the current fetcher. Waits for its TransferTerminated
109     // callback before starting the next range so that we don't end up
110     // signalling the delegate that the whole multi-transfer is complete
111     // before all fetchers are really done and cleaned up.
112     pending_transfer_ended_ = true;
113     LOG(INFO) << "Terminating transfer.";
114     fetcher->TerminateTransfer();
115     return false;
116   }
117   return true;
118 }
119 
120 // State change: Downloading or Pending transfer ended -> Stopped
TransferEnded(HttpFetcher * fetcher,bool successful)121 void MultiRangeHttpFetcher::TransferEnded(HttpFetcher* fetcher,
122                                           bool successful) {
123   CHECK(base_fetcher_active_) << "Transfer ended unexpectedly.";
124   CHECK_EQ(fetcher, base_fetcher_.get());
125   pending_transfer_ended_ = false;
126   http_response_code_ = fetcher->http_response_code();
127   LOG(INFO) << "TransferEnded w/ code " << http_response_code_;
128   if (terminating_) {
129     LOG(INFO) << "Terminating.";
130     Reset();
131     // Note that after the callback returns this object may be destroyed.
132     if (delegate_)
133       delegate_->TransferTerminated(this);
134     return;
135   }
136 
137   // If we didn't get enough bytes, it's failure
138   Range range = ranges_[current_index_];
139   if (range.HasLength()) {
140     if (bytes_received_this_range_ < range.length()) {
141       // Failure
142       LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
143       Reset();
144       // Note that after the callback returns this object may be destroyed.
145       if (delegate_)
146         delegate_->TransferComplete(this, false);
147       return;
148     }
149     // We got enough bytes and there were bytes specified, so this is success.
150     successful = true;
151   }
152 
153   // If we have another transfer, do that.
154   if (current_index_ + 1 < ranges_.size()) {
155     current_index_++;
156     LOG(INFO) << "Starting next transfer (" << current_index_ << ").";
157     StartTransfer();
158     return;
159   }
160 
161   LOG(INFO) << "Done w/ all transfers";
162   Reset();
163   // Note that after the callback returns this object may be destroyed.
164   if (delegate_)
165     delegate_->TransferComplete(this, successful);
166 }
167 
TransferComplete(HttpFetcher * fetcher,bool successful)168 void MultiRangeHttpFetcher::TransferComplete(HttpFetcher* fetcher,
169                                              bool successful) {
170   LOG(INFO) << "Received transfer complete.";
171   TransferEnded(fetcher, successful);
172 }
173 
TransferTerminated(HttpFetcher * fetcher)174 void MultiRangeHttpFetcher::TransferTerminated(HttpFetcher* fetcher) {
175   LOG(INFO) << "Received transfer terminated.";
176   TransferEnded(fetcher, false);
177 }
178 
Reset()179 void MultiRangeHttpFetcher::Reset() {
180   base_fetcher_active_ = pending_transfer_ended_ = terminating_ = false;
181   current_index_ = 0;
182   bytes_received_this_range_ = 0;
183 }
184 
ToString() const185 std::string MultiRangeHttpFetcher::Range::ToString() const {
186   std::string range_str = base::StringPrintf("%jd+", offset());
187   if (HasLength())
188     range_str += std::to_string(length());
189   else
190     range_str += "?";
191   return range_str;
192 }
193 
SetOffset(off_t offset)194 void MultiRangeHttpFetcher::SetOffset(off_t offset) {
195   current_index_ = 0;
196   for (const Range& range : ranges_) {
197     if (!range.HasLength() || static_cast<size_t>(offset) < range.length()) {
198       bytes_received_this_range_ = offset;
199 
200       base_fetcher_->SetOffset(range.offset() + offset);
201       if (range.HasLength())
202         base_fetcher_->SetLength(range.length());
203       else
204         base_fetcher_->UnsetLength();
205       if (delegate_)
206         delegate_->SeekToOffset(range.offset() + offset);
207       return;
208     }
209     current_index_++;
210     offset -= range.length();
211   }
212   if (offset > 0) {
213     LOG(ERROR) << "Offset too large.";
214   }
215 }
216 
217 }  // namespace chromeos_update_engine
218